
Reproducer per CVE-2026-40859 — Apache Camel camel-netty-http / camel-vertx-http: deserializzazione non sicura lato producer dei corpi delle risposte HTTP (RCE)
Questo progetto dimostra una vulnerabilità di deserializzazione Java nei componenti camel-netty-http e camel-vertx-http di Apache Camel, tracciata come CVE-2026-40859. Quando un endpoint producer è configurato con transferException=true (o con l'opzione a livello di componente allowJavaSerializedObject=true), una risposta HTTP del backend con stato di errore e Content-Type: application/x-java-serialized-object viene deserializzata nel corpo usando un java.io.ObjectInputStream grezzo e senza ObjectInputFilter. Un attaccante che controlla il backend con cui comunica il producer Camel — un servizio compromesso o un uomo-in-the-middle su una connessione HTTP in chiaro — può restituire un oggetto serializzato appositamente costruito e, se una gadget chain è presente nel classpath, ottenere sull'host Camel.
| Proprietà | Valore |
|---|---|
| Componenti | camel-netty-http, camel-vertx-http (lato producer) |
| Classe interessata | org.apache.camel.component.netty.http.NettyHttpHelper#deserializeJavaObjectFromStream (e VertxHttpHelper#deserializeJavaObjectFromStream) |
| CWE | CWE-502: Deserializzazione di dati non attendibili |
| Impatto | Esecuzione remota di codice (RCE) |
| Precondizione | transferException=true (o allowJavaSerializedObject=true) + throwExceptionOnFailure=true (predefinito) + backend controllato dall'attaccante |
| Versioni interessate | Dalla 4.0.0 alla 4.14.8 (esclusa), dalla 4.15.0 alla 4.18.3 (esclusa), dalla 4.19.0 alla 4.20.0 (esclusa) |
| Versioni corrette | 4.14.8, 4.18.3, 4.20.0 |
| JIRA | CAMEL-23324 |
| Reporter | Venkatraman Kumar (Securin) |
Non sfruttabile nella configurazione predefinita —
transferExceptionè impostato sufalseper impostazione predefinita. La PoC lo abilita, come farebbe un'applicazione che desidera la propagazione delle eccezioni remote.
In caso di risposta non-2xx, il producer netty-http (con throwExceptionOnFailure=true, l'impostazione predefinita) costruisce un'eccezione dalla risposta tramite populateNettyHttpOperationFailedException. Se transferException è attivo e la risposta ha il content type dell'oggetto serializzato, deserializza il corpo:
// NettyHttpHelper.populateNettyHttpOperationFailedException(...) - affected version
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) - affected version
ObjectInputStream ois = new ObjectInputStream(is); // NO ObjectInputFilter
answer = ois.readObject(); // gadget fires here
La gadget viene eseguita all'interno di readObject(), prima del controllo instanceof Exception — quindi il payload non deve nemmeno essere un'eccezione. camel-vertx-http ha lo stesso sink in VertxHttpHelper.deserializeJavaObjectFromStream.
from("direct:call")
.to("netty-http://backend-host:PORT/path?transferException=true");
// throwExceptionOnFailure defaults to true
Qualsiasi chiamata del producer il cui backend risponde con 5xx + application/x-java-serialized-object attiva il sink.
La vittima è il producer Camel (esegue la deserializzazione). L'attaccante controlla il backend che esso chiama. In questa PoC autonoma entrambi i ruoli vengono eseguiti nella stessa JVM/container: un server HTTP embedded su raw socket (MaliciousBackend) svolge il ruolo di backend controllato dall'attaccante, e la route Camel (VictimRoute) è la vittima.
CVE-2026-40859/
├── pom.xml # camel-netty-http 4.18.2 + commons-collections 3.2.1 (gadget)
├── Dockerfile # runs the app (with --add-opens, needed only to build the gadget)
├── docker-compose.yml
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── VictimRoute.java # victim: netty-http producer, transferException=true
│ ├── MaliciousBackend.java # attacker backend: 500 + serialized-object body on :9999
│ ├── Gadget.java # CommonsCollections6 gadget, fires during readObject()
│ └── ExploitController.java # /exploit/attack drives the producer call
└── resources/
└── application.properties
In un attacco reale i byte serializzati vengono prodotti offline dall'attaccante (ad es. con ysoserial); solo la vittima deve avere la gadget chain nel proprio classpath. Questa PoC costruisce la gadget in-process per comodità, motivo per cui la JVM viene avviata con
--add-opens java.base/java.util=ALL-UNNAMED— quel flag è un dettaglio di costruzione della gadget, non correlato alla vulnerabilità.
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
# -> producer threw ...NettyHttpOperationFailedException (expected)
#
# >>> RCE proof — /tmp/pwned exists: true
docker exec cve-2026-40859 ls -la /tmp/pwned
docker compose down
Qualsiasi producer camel-netty-http / camel-vertx-http configurato con transferException=true (o allowJavaSerializedObject=true) che comunica con un backend che un attaccante può controllare o intercettare:
http://) sostituisce la risposta.transferException=true (o con l'opzione di componente allowJavaSerializedObject=true).throwExceptionOnFailure=true (l'impostazione predefinita).5xx + application/x-java-serialized-object.commons-collections:3.2.1).Aggiornare a 4.14.8 / 4.18.3 / 4.20.0. La correzione applica a entrambi gli helper un elenco consentito (allow-list) ObjectInputFilter predefinito (java.**;javax.**;org.apache.camel.**;!*), personalizzabile tramite la nuova opzione di endpoint deserializationFilter o la proprietà di sistema JVM -Djdk.serialFilter.
Fino all'aggiornamento:
transferException=true / allowJavaSerializedObject=true sui producer che comunicano con backend non attendibili o raggiungibili tramite rete.https) per le connessioni del producer in modo che le risposte non possano essere sostituite in transito.-Djdk.serialFilter=java.**;org.apache.camel.**;!*.Questo riproduttore è fornito esclusivamente per ricerca sulla sicurezza e test autorizzati, per una vulnerabilità divulgata pubblicamente e corretta. Non utilizzarlo contro sistemi senza esplicita autorizzazione.