
Reproducer for CVE-2026-43866 — Apache Camel camel-jms forged DefaultExchangeHolder bypass of the CVE-2026-40860 deserialization filter (Exchange-state injection)
Dieses Projekt demonstriert CVE-2026-43866, einen Bypass des CVE-2026-40860-Fixes in den camel-jms-Komponenten von Apache Camel (sowie camel-sjms / JMS-Familie). CVE-2026-40860 fügte eine Whitelist für Klassen nach der Deserialisierung (java.**;javax.**;org.apache.camel.**;!*) für eingehende JMS ObjectMessage-Payloads hinzu. Da org.apache.camel.support.DefaultExchangeHolder jedoch im erlaubten Namespace org.apache.camel.** lebt, passiert ein ObjectMessage, dessen Top-Level-Objekt ein DefaultExchangeHolder ist, die Prüfung. Die empfangende Seite ruft dann DefaultExchangeHolder.unmarshal() darauf auf ohne dass transferExchange erforderlich ist – und schreibt jedes nicht-null-Feld des Holders in den gerouteten Exchange (Body, IN/OUT-Header, Exchange-Eigenschaften, Variablen, Exchange-ID, Exception). Ein Angreifer, der ein ObjectMessage veröffentlichen kann, kann somit beliebigen Exchange-Zustand nur mit universell vertrauenswürdigen java.*-Typen injizieren – keine Deserialisierungs-Gadget-Kette erforderlich – um Routing, Header, Eigenschaften und Fehlerbehandlung zu manipulieren.
Advisory: https://camel.apache.org/security/CVE-2026-43866.html
// JmsBinding.extractBodyFromJms(...) - affected 4.18.2
if (message instanceof ObjectMessage objectMessage) {
Object payload = objectMessage.getObject();
checkDeserializedClass(payload); // CVE-2026-40860 allow-list: java.**;javax.**;org.apache.camel.**;!*
if (payload instanceof DefaultExchangeHolder holder) { // <-- DefaultExchangeHolder is org.apache.camel.** -> passes
DefaultExchangeHolder.unmarshal(exchange, holder); // <-- writes forged state into the Exchange; NO transferExchange gate
Map<String, Object> jmsHeaders = extractHeadersFromJms(message, exchange);
exchange.getIn().getHeaders().putAll(jmsHeaders);
return exchange.getIn().getBody();
} else {
return payload;
}
}
Die Asymmetrie: Die sendende Seite steuert die Erstellung von ObjectMessage/transferExchange, aber die empfangende Seite entpackt jeden DefaultExchangeHolder, den sie deserialisiert. Der Fix (4.14.8 / 4.18.3 / 4.21.0) fügt eine neue Option objectMessageEnabled hinzu (security = "insecure:serialization", Standardwert false) – eine eingehende ObjectMessage wird überhaupt nicht mehr deserialisiert, es sei denn, sie ist explizit aktiviert, sodass ein gefälschter Holder nie unmarshal() erreicht. (Dies ist eine bahnbrechende Änderung für Routen, die auf ObjectMessage / transferExchange angewiesen sind.)
Eine JMS-Provider-Whitelist hilft nicht. Dieser PoC konfiguriert den ActiveMQ-Client mit einer realistischen, restriktiven
trustedPackages = [java, javax, org.apache.camel]– der Eintragorg.apache.camelist genau das, was eine Bereitstellung, die legitimestransferExchangeverwendet, vertrauen muss. Der gefälschte Holder passiert trotzdem, weil er selbst einDefaultExchangeHolderist, dessen Felder alle vertrauenswürdigejava.*-Typen sind – nicht von einem legitimen zu unterscheiden.
from("jms:queue:cve") // mapJmsMessage defaults to true; transferExchange NOT set
.process(exchange -> { /* observes the injected body / headers / properties */ });
Das Opfer ist der Camel JMS-Consumer. Der Angreifer ist jeder Producer, der in die Queue veröffentlichen kann. Beide kommunizieren mit einem echten Apache ActiveMQ Artemis-Broker in Docker.
CVE-2026-43866/
├── pom.xml # camel-jms 4.18.2 + activemq-client 6.2.4 (KEINE Gadget-Bibliothek)
├── Dockerfile # führt die App aus (kein --add-opens; kein Gadget)
├── docker-compose.yml # Artemis-Broker (quay.io) + die Reproducer-App
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── JmsConfig.java # OpenWire ConnectionFactory (trustedPackages inkl. org.apache.camel) + jms-Komponente
│ ├── VictimRoute.java # Opfer: from("jms:queue:cve"); zeichnet auf, was die Route beobachtet hat
│ ├── CapturedState.java
│ ├── ForgedHolderFactory.java # erstellt einen DefaultExchangeHolder über die öffentliche marshal()-API
│ └── ExploitController.java # Angreifer: veröffentlicht den gefälschten Holder als ObjectMessage
└── resources/
└── application.properties
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
# -> Published a forged DefaultExchangeHolder as a JMS ObjectMessage to queue 'cve'.
# ...
# body = INJECTED-BODY-... (injected: true)
# header = pwned-header-... (injected: true)
# property = pwned-property-... (injected: true)
#
# >>> Exchange-state injection proof — attacker controlled body+header+property: true
Die Route wurde mit einem Body, Header und einer Property ausgeführt, die sie nie gesetzt hatte – alle vom gefälschten Holder des Angreifers geliefert.
docker compose down
Jeder Camel JMS-Consumer (camel-jms, camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) mit mapJmsMessage=true (Standard), der von einem Ziel liest, auf das ein Angreifer veröffentlichen kann. transferExchange muss nicht auf dem Consumer aktiviert sein.
mapJmsMessage=true (Standard) in einer betroffenen Version.ObjectMessage in die Warteschlange einreihen, dessen Payload ein DefaultExchangeHolder ist.transferExchange verwendet, bedeutet, dass der Provider org.apache.camel bereits vertraut. Es wird keine Gadget-Bibliothek benötigt.Aktualisieren Sie auf 4.14.8 / 4.18.3 / 4.21.0 (CAMEL-23373 / CAMEL-23409). Die Behandlung von JMS ObjectMessage ist standardmäßig über die neue Option objectMessageEnabled deaktiviert; aktivieren Sie sie nur für Ziele, die ausschließlich von vertrauenswürdigen Produzenten gespeist werden.
Bis zur Aktualisierung:
ObjectMessage-Bodies mappen, ungeschützten Netzwerken aus.DefaultExchangeHolder).Dieser Reproducer wird nur für Sicherheitsforschung und autorisierte Tests bereitgestellt, für eine öffentlich offengelegte und behobene Schwachstelle. Verwenden Sie ihn nicht gegen Systeme ohne ausdrückliche Genehmigung.
| Eigenschaft | Wert |
|---|
| Komponenten | camel-jms (+ camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) |
| Betroffene Klasse | org.apache.camel.component.jms.JmsBinding#extractBodyFromJms → DefaultExchangeHolder.unmarshal |
| CWE | CWE-502 (Deserialisierung nicht vertrauenswürdiger Daten) + CWE-20 (Unzureichende Eingabevalidierung) |
| Auswirkung | Injection von Exchange-Zustand: vom Angreifer kontrollierte Body, Header, Eigenschaften, Variablen, Exception |
| Art | Bypass des CVE-2026-40860-Klassenfilter-Fixes (kein Fehler darin) |
| Betroffene Versionen | Von 3.0.0 vor 4.14.8, von 4.15.0 vor 4.18.3, von 4.19.0 vor 4.21.0 |
| Behobene Versionen | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23373 (jms), CAMEL-23409 (sjms) |
| Melder | gaorenyusi |