
Reproducteur pour CVE-2026-43866 — camel-jms d'Apache Camel forge DefaultExchangeHolder contournant le filtre de désérialisation CVE-2026-40860 (injection d'état d'échange)
Ce projet démontre CVE-2026-43866, un contournement du correctif de CVE-2026-40860 dans les composants Apache Camel camel-jms (et camel-sjms / famille JMS). CVE-2026-40860 a ajouté une liste blanche de classes post-désérialisation (java.**;javax.**;org.apache.camel.**;!*) aux charges utiles JMS ObjectMessage entrantes. Mais org.apache.camel.support.DefaultExchangeHolder réside dans l'espace de noms org.apache.camel.** autorisé, donc un ObjectMessage dont l'objet de premier niveau est un DefaultExchangeHolder passe la vérification. Le côté récepteur appelle ensuite dessus exiger — écrivant chaque champ non nul du holder dans l'Exchange routé (body, en-têtes IN/OUT, propriétés de l'échange, variables, id d'échange, exception). Un attaquant capable de publier un peut donc — pour manipuler le routage, les en-têtes, les propriétés et la gestion des erreurs.
DefaultExchangeHolder.unmarshal()transferExchangeObjectMessagejava.* universellement fiables — sans nécessiter de chaîne de gadgets de désérialisation| Propriété | Valeur |
|---|---|
| Composants | camel-jms (+ camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) |
| Classe affectée | org.apache.camel.component.jms.JmsBinding#extractBodyFromJms → DefaultExchangeHolder.unmarshal |
| CWE | CWE-502 (Désérialisation de données non fiables) + CWE-20 (Validation d'entrée incorrecte) |
| Impact | Injection d'état Exchange : body, en-têtes, propriétés, variables, exception contrôlés par l'attaquant |
| Nature | Contournement du correctif de filtre de classe CVE-2026-40860 (pas une faille en soi) |
| Versions affectées | De 3.0.0 avant 4.14.8, de 4.15.0 avant 4.18.3, de 4.19.0 avant 4.21.0 |
| Versions corrigées | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23373 (jms), CAMEL-23409 (sjms) |
| Signaleur | gaorenyusi |
// 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;
}
}
L'asymétrie : le côté envoi contrôle la création d'ObjectMessage/transferExchange, mais le côté réception désérialise tout DefaultExchangeHolder qu'il désérialise. Le correctif (4.14.8 / 4.18.3 / 4.21.0) ajoute une nouvelle option objectMessageEnabled (security = "insecure:serialization", par défaut false) — un ObjectMessage entrant n'est plus du tout désérialisé sauf s'il est explicitement activé, donc un holder forgé ne peut jamais atteindre unmarshal(). (C'est un changement cassant pour les routes qui dépendent d'ObjectMessage / transferExchange.)
Une liste blanche du fournisseur JMS n'aide pas. Cette POC configure le client ActiveMQ avec un
trustedPackages = [java, javax, org.apache.camel]réaliste et verrouillé — l'entréeorg.apache.camelest exactement ce qu'un déploiement utilisant untransferExchangelégitime doit approuver. Le holder forgé passe encore, car il est lui-même unDefaultExchangeHolderdont les champs sont tous des typesjava.*fiables, indiscernables d'un légitime.
from("jms:queue:cve") // mapJmsMessage defaults to true; transferExchange NOT set
.process(exchange -> { /* observes the injected body / headers / properties */ });
La victime est le consommateur Camel JMS. L'attaquant est tout producteur pouvant publier dans la file d'attente. Les deux communiquent avec un courtier Apache ActiveMQ Artemis réel dans Docker.
CVE-2026-43866/
├── pom.xml # camel-jms 4.18.2 + activemq-client 6.2.4 (NO gadget library)
├── Dockerfile # runs the app (no --add-opens; no gadget)
├── docker-compose.yml # Artemis broker (quay.io) + the reproducer app
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── JmsConfig.java # OpenWire ConnectionFactory (trustedPackages incl. org.apache.camel) + jms component
│ ├── VictimRoute.java # victim: from("jms:queue:cve"); records what the route observed
│ ├── CapturedState.java
│ ├── ForgedHolderFactory.java # builds a DefaultExchangeHolder via the public marshal() API
│ └── ExploitController.java # attacker: publishes the forged holder as an ObjectMessage
└── resources/
└── application.properties
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
# -> Publication d'un DefaultExchangeHolder forgé en tant qu'ObjectMessage JMS dans la file 'cve'.
# ...
# corps = INJECTED-BODY-... (injecté: true)
# en-tête = pwned-header-... (injecté: true)
# propriété = pwned-property-... (injecté: true)
#
# >>> Preuve d'injection d'état Exchange — corps+en-tête+propriété contrôlés par l'attaquant : true
La route s'est exécutée avec un corps, un en-tête et une propriété qu'elle n'a jamais définis — tous fournis par le holder forgé de l'attaquant.
docker compose down
Tout consommateur Camel JMS (camel-jms, camel-sjms, camel-sjms2, camel-amqp, camel-activemq,
camel-activemq6) avec mapJmsMessage=true (par défaut), lisant depuis une destination où un attaquant peut publier.
transferExchange n'a pas besoin d'être activé sur le consommateur.
mapJmsMessage=true (par défaut), sur une version affectée.ObjectMessage dont la charge utile est un DefaultExchangeHolder.transferExchange, signifie que le fournisseur approuve déjà org.apache.camel. Aucune bibliothèque de gadgets n'est requise.Mettez à niveau vers 4.14.8 / 4.18.3 / 4.21.0 (CAMEL-23373 / CAMEL-23409). La gestion des ObjectMessage JMS est désactivée par défaut via la nouvelle option objectMessageEnabled ; activez-la uniquement pour des destinations alimentées exclusivement par des producteurs de confiance.
En attendant la mise à niveau :
ObjectMessage à des réseaux non fiables.DefaultExchangeHolder).Ce reproducteur est fourni à des fins de recherche en sécurité et de tests autorisés uniquement, pour une vulnérabilité divulguée publiquement et corrigée. Ne l'utilisez pas contre des systèmes sans autorisation explicite.