本项目演示了 Apache Camel 的 camel-docling 组件中的一个 CLI 参数注入和路径遍历 漏洞,编号为 CVE-2026-40047。DoclingProducer 根据消息头构建外部 docling 命令行工具的调用;通过 CamelDoclingCustomArguments 头传入的自定义参数仅经过不充分的校验(一个拒绝列表加上字符串字面量 ../ 检查)即被追加,因此能够影响这些消息头的攻击者可以向子进程注入任意的 docling CLI 标志和包含路径穿越的值。
公告:https://camel.apache.org/security/CVE-2026-40047.html
| 属性 | 值 |
|---|---|
| 组件 | camel-docling |
| 受影响类 | org.apache.camel.component.docling.DoclingProducer(addCustomArguments / validateCustomArguments) |
| 根本原因 | CamelDoclingCustomArguments(List<String>)被追加到 docling CLI 参数,仅使用弱的拒绝列表 + 字面量 ../ 检查 |
| CWE | CWE-88(参数注入)/ CWE-22(路径遍历) |
| 影响 | 可向外部工具注入任意/非预期的 docling CLI 标志和目录外路径值。不是操作系统命令注入(基于列表的 ProcessBuilder,无 shell)。 |
| 受影响版本 | 自 4.15.0 起,4.18.3 之前 |
| 已修复版本 | 4.18.3, 4.19.0 |
| JIRA | CAMEL-23212 |
| 报告者 | Andrea Cosentino(Apache 软件基金会) |
DoclingProducer 组装 docling 调用并通过 java.lang.ProcessBuilder(列表形式——无 shell)运行。来自 CamelDoclingCustomArguments 消息头的自定义 CLI 参数被追加到命令中:
// DoclingProducer.addCustomArguments(...) - 受影响版本
List<String> customArgs = exchange.getIn().getHeader(DoclingHeaders.CUSTOM_ARGUMENTS, List.class);
if (customArgs != null && !customArgs.isEmpty()) {
validateCustomArguments(customArgs); // 拒绝列表 + 字面量 "../" 检查(较弱)
command.addAll(customArgs);
}
在受影响版本中,validateCustomArguments 依赖一个被禁止标志的 拒绝列表,且仅拒绝包含字面量 ../ 的路径值。其结果是:
docling。../ 的路径穿越值(绝对路径或规范化后的序列)不会被捕获。由于 Camel 负责构建 docling 调用,因此该组件有责任约束这些值。修复(CAMEL-23212)将拒绝列表替换为已识别标志的严格 允许列表,拒绝由生产者管理的标志(--output/-o)和 shell 元字符(纵深防御),并在验证前使用 Path.normalize() 规范化路径类值。
该调用使用
ProcessBuilder的列表形式,因此不会有 shell 来解释这些值——通过 shell 元字符进行 OS 命令注入是不可能的;修复中的元字符拒绝属于纵深防御。
from("direct:convert")
.to("docling:convert?operation=CONVERT_TO_MARKDOWN&contentInBody=true");
docling 是一个外部工具。本复现工具附带一个 docling 桩程序(位于容器内的 PATH 中),它会记录收到的 argv 并回写一个 markdown 文件,这样注入的参数可以在 HTTP 响应和 /tmp/docling-invocations.log 中看到。一切都在 Docker 镜像内运行(应用 + 桩程序)——因此无需安装真正的 docling。
docling 桩程序)mvn clean package -DskipTests
docker compose up -d --build
curl http://localhost:8080/exploit/normal
# 桩 docling 收到:docling --to md --ocr-lang en --output <tmp> /tmp/input.txt
curl "http://localhost:8080/exploit/attack"
# 注入 CamelDoclingCustomArguments = [--injected-by-attacker, arbitrary-value]
# -> 桩 docling 收到:
# docling --injected-by-attacker arbitrary-value --to md --ocr-lang en --output <tmp> /tmp/input.txt
# 不包含字面量 "../" 的路径值(绝对路径)同样可以通过:
curl "http://localhost:8080/exploit/attack?flag=--artifacts-path&value=/etc/attacker-controlled"
在受影响版本(本复现工具固定为 4.18.2)上,该路由 成功 且注入的参数会到达子进程。在已修复版本(4.18.3 / 4.19.0)上,允许列表会以 IllegalArgumentException 拒绝 --injected-by-attacker,路由失败。
docker compose down
docling: 生产者的 CamelDoclingCustomArguments(或携带路径的消息头)中。升级到 4.18.3 / 4.19.0。该修复使用已识别 docling 标志的严格允许列表,拒绝由生产者管理的标志和 shell 元字符,并在验证前使用 Path.normalize() 规范化路径值。
在升级之前:
CamelDoclingCustomArguments 或携带路径的消息头中。docling: 生产者之前,剥离 Camel 内部消息头(removeHeaders("Camel*")),适用于来自不可信生产者的消息。CVE-2026-40047/
├── pom.xml
├── Dockerfile # 应用 + PATH 上的 'docling' 桩程序
├── docker-compose.yml
├── docling-stub.sh # 'docling' 桩程序(记录 argv、写入 markdown)
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── DoclingRoute.java # from(direct:convert).to(docling:convert)
│ └── ExploitController.java # 注入 CamelDoclingCustomArguments
└── resources/
└── application.properties
本复现工具仅用于 安全研究和授权测试,针对的是一个 已公开披露并已修复 的漏洞。未经明确许可,请勿将其用于任何系统。