downloadFileName 路径遍历同一 Apache Camel 漏洞的可运行概念验证复现程序,每个运行时一个:
| 运行时 | 目录 | 技术栈 |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-google-storage 4.18.2 |
| Camel Quarkus | camel-quarkus/ | Quarkus 3.36.0 + Camel Quarkus 3.36.0(内置 Camel 4.20.0) |
两者均为受影响版本(该问题已在 4.14.9 / 4.18.4 / 4.22.0 中修复),并且两者均展示了相同的缺陷:当 downloadFileName 为目录时,camel-google-storage 消费者会将存储桶对象下载到本地文件系统,通过将远程对象名称附加到其后构建本地目标路径(downloadFileName + "/${file:name}")。${file:name} 令牌会原样返回对象名称(与 ${file:onlyname} 不同,后者会去除路径),结果被传递给 new File(result) / blob.downloadTo(file.toPath()),且没有进行规范化,也没有检查目标是否仍位于配置的目录内。对象名称不受路由控制——消费者会列出存储桶中的每个对象并逐一下载——因此名称包含 ../ 段的对象会被写入 downloadFileName 之外(CWE-22,路径遍历 → 任意文件写入)。
每个子目录都是独立的(包含自己的 Dockerfile、用于启动 fake-gcs-server 模拟器的 docker-compose.yml,以及 README)。简而言之,对于任意一个:
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
受影响构建上的预期输出(两种变体):
Files inside the intended download directory /app/downloads:
- report.txt
File written OUTSIDE it, at /tmp/pwned-66907.txt: true
content: PWNED via path traversal — CVE-2026-66907
>>> PROVEN: the object name's ../ segments escaped the configured downloadFileName directory ... : true
公告:https://camel.apache.org/security/CVE-2026-66907.html
消费者现在会对解析后的路径进行规范化,并断言其位于配置的 downloadFileName 目录内(通过 GoogleCloudStorageFileNameHelper.assertWithinDirectory),拒绝会逃逸该目录的对象名称。
docker-compose.yml 使用 -backend memory 运行 fake-gcs-server。对象名称随后作为不透明的映射键保存,因此包含 ../ 的名称得以保留;默认的文件系统后端会解析 ../,对象将无法被列出。
本仓库仅用于教育和防御目的:帮助 Apache Camel 用户了解该漏洞、验证自己是否受影响,并确认升级能够解决该问题。写入的文件是位于 /tmp 下的一个良性标记。请勿将本材料用于您不拥有或不运营的系统。
| 属性 | 值 |
|---|
| 组件 | camel-google-storage(Spring Boot:camel-google-storage-starter;Quarkus:camel-quarkus-google-storage) |
| CWE | CWE-22(对受限目录的路径名限制不当 — 路径遍历) |
| 攻击向量 | 名称包含 ../ 段的存储桶对象,由消费者在 downloadFileName 设置为目录时下载 |
| 影响 | 在配置的下载目录之外进行任意文件写入 |
| 受影响版本 | 4.0.0 至 4.14.9 之前,4.15.0 至 4.18.4 之前,4.19.0 至 4.22.0 之前 |
| 修复版本 | 4.14.9, 4.18.4, 4.22.0 |
| JIRA | CAMEL-24279 |
| 致谢 | n0mi1k |