
Runnable Spring Boot proof-of-concept reproducing CVE-2026-85245, a camel-thrift cross-exchange data disclosure, with affected vs fixed comparison and mitigation notes.
Runnable proof-of-concept reproducer for the Apache Camel vulnerability where the Thrift data format unmarshalled
into the shared defaultInstance of the target type rather than into a fresh copy, and returned that shared object
as the exchange body. Because the instance is shared process-wide, state written while unmarshalling one message
stayed on the object and was visible to every later message that used the same type. Fields absent from a later
payload retained the previous message's values, so content belonging to one sender could be read while processing
another sender's message, and concurrent exchanges could observe each other's partially written state. In a
deployment that serves more than one tenant or user through the same route, that is disclosure of one party's data to
another.
Spring Boot only. There is no
camel-quarkus-thriftextension, so this reproducer ships a single Camel Spring Boot module. (The flaw is in the component'sThriftDataFormat, which is runtime-independent.)
| Runtime | Directory | Stack |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-thrift 4.22.0 (affected; fixed 4.22.1) |
A route defines one ThriftDataFormat (backed by the generated Thrift type Work) and reuses it for every message,
as routes normally do. Two tenants send a message through it:
Work{num1=1, op=ADD, comment="TENANT-A-SECRET-…"} — the optional comment field carries sensitive
data;Work{num1=3, op=SUBTRACT} — no comment field.The route unmarshals tenant A's message, then tenant B's.
comment — Thrift only assigns the
fields present in the incoming bytes, and both messages were deserialized into (and returned as) the same shared
instance, so B's missing field kept A's value. The two bodies are literally the same object.comment is null — each exchange gets its own cleared copy.cd camel-spring-boot
mvn clean package
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack # affected: B sees A's secret; >>> PROVEN
docker compose down
# Patched control
mvn clean package -Dcamel.version=4.22.1
java -jar target/cve-2026-85245-thrift-cross-exchange-disclosure-0.0.1-SNAPSHOT.jar &
curl -s http://localhost:8080/exploit/attack # fixed: B's comment is null; >>> Not exploited
| Property | Value |
|---|---|
| Component | camel-thrift (ThriftDataFormat.unmarshal) |
| CWE | CWE-488 (Exposure of Data Element to Wrong Session) |
| Severity | MEDIUM |
| Attack vector | Two messages of the same Thrift type through one route; a later message omitting a field inherits the earlier one's value |
| Impact | Cross-exchange / cross-tenant data disclosure; concurrent exchanges can observe each other's partially written state |
| Affected Versions | From 4.0.0 before 4.18.5, from 4.19.0 before 4.22.1 |
| Fixed Versions | 4.18.5, 4.22.1, 4.23.0 |
| JIRA | CAMEL-24442 |
| Credit | Claude Security Scan Tool |
Advisory: https://camel.apache.org/security/CVE-2026-85245.html
The generated Thrift types (Work, Operation) are the public test fixtures from Apache Camel's own camel-thrift
tests.
ThriftDataFormat.unmarshal now deserializes into a per-exchange copy:
TBase instance = defaultInstance.deepCopy(); instance.clear(); — then deserializes the bytes into instance and
returns it. Each exchange gets its own cleared object, so no field carries between messages and no two exchanges share
a body.
Copy the unmarshalled body into a new instance immediately after the data format runs, and do not retain references to the returned object.
This repository is published for educational and defensive purposes: to help Apache Camel users understand the vulnerability, verify whether they are affected, and confirm that upgrading resolves it. The "secret" is an inert, made-up marker string. Do not use this material against systems you do not own or operate.