
Reproductor para CVE-2026-43866 — bypass del filtro de deserialización de CVE-2026-40860 mediante DefaultExchangeHolder forjado en Apache Camel camel-jms (inyección de estado de Exchange)
Este proyecto demuestra la CVE-2026-43866, una omisión (bypass) de la corrección de la CVE-2026-40860 en los componentes camel-jms (y camel-sjms / familia JMS) de Apache Camel. La CVE-2026-40860 añadió una lista blanca de clases posterior a la deserialización (java.**;javax.**;org.apache.camel.**;!*) para las cargas útiles entrantes de JMS ObjectMessage. Pero org.apache.camel.support.DefaultExchangeHolder se encuentra en el espacio de nombres org.apache.camel.** incluido en la lista blanca, por lo que un ObjectMessage cuyo objeto de nivel superior es un DefaultExchangeHolder supera la comprobación. El lado receptor llama entonces a DefaultExchangeHolder.unmarshal() sobre él sin requerir transferExchange, escribiendo cada campo no nulo del holder en el Exchange enrutado (body, cabeceras IN/OUT, propiedades del exchange, variables, id del exchange, excepción). Un atacante que pueda publicar un ObjectMessage puede por tanto inyectar estado arbitrario del Exchange utilizando únicamente tipos java.* universalmente confiados, sin necesidad de una cadena de gadgets de deserialización — para manipular el enrutamiento, las cabeceras, las propiedades y el manejo de errores.
Aviso de seguridad: 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;
}
}
La asimetría: el lado emisor restringe la creación de ObjectMessage/transferExchange, pero el lado receptor ejecuta unmarshal sobre cualquier DefaultExchangeHolder que deserialice. La corrección (4.14.8 / 4.18.3 / 4.21.0) añade una nueva opción objectMessageEnabled (security = "insecure:serialization", por defecto false): un ObjectMessage entrante ya no se deserializa en absoluto a menos que se habilite explícitamente, por lo que un holder forjado nunca puede llegar a unmarshal(). (Este es un cambio disruptivo para las rutas que dependen de ObjectMessage / transferExchange).
Una lista blanca del proveedor JMS no sirve de ayuda. Este PoC configura el cliente ActiveMQ con un
trustedPackages = [java, javax, org.apache.camel]realista y restringido: la entradaorg.apache.cameles exactamente lo que un despliegue que usatransferExchangelegítimo debe confiar. El holder forjado sigue superando la comprobación, porque es en sí mismo unDefaultExchangeHoldercuyos campos son todos tiposjava.*confiados, indistinguible de uno legítimo.
from("jms:queue:cve") // mapJmsMessage defaults to true; transferExchange NOT set
.process(exchange -> { /* observes the injected body / headers / properties */ });
La víctima es el consumidor JMS de Camel. El atacante es cualquier productor que pueda publicar en la cola. Ambos se comunican con un broker real de Apache ActiveMQ Artemis en 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
# -> 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
La ruta se ejecutó con un body, una cabecera y una propiedad que nunca estableció: todos proporcionados por el holder forjado del atacante.
docker compose down
Cualquier consumidor JMS de Camel (camel-jms, camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) con mapJmsMessage=true (el valor por defecto), que lea de un destino al que un atacante pueda publicar. transferExchange no necesita estar habilitado en el consumidor.
mapJmsMessage=true (por defecto), en una versión afectada.ObjectMessage cuya carga útil sea un DefaultExchangeHolder.transferExchange, significa que el proveedor ya confía en org.apache.camel. No se requiere ninguna librería de gadgets.Actualice a 4.14.8 / 4.18.3 / 4.21.0 (CAMEL-23373 / CAMEL-23409). El manejo de JMS ObjectMessage está deshabilitado por defecto mediante la nueva opción objectMessageEnabled; actívela únicamente para destinos alimentados exclusivamente por productores de confianza.
Mientras se actualiza:
ObjectMessage a redes no confiadas.DefaultExchangeHolder).Este reproductor se proporciona únicamente para investigación de seguridad y pruebas autorizadas, para una vulnerabilidad divulgada públicamente y corregida. No lo utilice contra sistemas sin permiso explícito.
| Propiedad | Valor |
|---|
| Componentes | camel-jms (+ camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) |
| Clase afectada | org.apache.camel.component.jms.JmsBinding#extractBodyFromJms → DefaultExchangeHolder.unmarshal |
| CWE | CWE-502 (Deserialización de datos no confiados) + CWE-20 (Validación de entrada incorrecta) |
| Impacto | Inyección de estado del Exchange: body, cabeceras, propiedades, variables y excepción controlados por el atacante |
| Naturaleza | Omisión (bypass) de la corrección del filtro de clases de la CVE-2026-40860 (no es un fallo de la propia corrección) |
| Versiones afectadas | Desde 3.0.0 hasta antes de 4.14.8, desde 4.15.0 hasta antes de 4.18.3, desde 4.19.0 hasta antes de 4.21.0 |
| Versiones corregidas | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23373 (jms), CAMEL-23409 (sjms) |
| Reportero | gaorenyusi |