Gotenberg PDF 元数据 RCE,已在野外被利用(CVE-2026-42589)
PDF 元数据键中的换行符可在未认证的 8.31.0 之前 Gotenberg 服务器上执行 Perl 代码;七个攻击者集群正在利用。
摘要
- 8.31.0 之前的 Gotenberg 存在缺陷,未认证攻击者可通过 PDF 元数据端点执行操作系统命令。JSON 元数据键内的换行符会把 ExifTool 的输入切分为新的参数行,注入的
-if标志会执行 Perl 表达式。 - Gotenberg 是基于 Docker 的 PDF 转换 API,Docker Hub 拉取量超过 1.01 亿次。任何 8.31.0 之前且 3000 端口可达的实例都暴露在外,因为该端点不需要任何认证。
- VulnCheck 在 30 天内记录到来自 128 个源 IP 的 1,358 次利用事件,涉及至少七个攻击者集群。GitHub 公告将其评为 Critical CVSS v3.1 9.8,截至 2026-10-30 它不在 CISA 的 KEV 目录中。
- 升级到 Gotenberg 8.31.0 或更高版本并重建容器。该修复自 2026 年 4 月起就已发布。8.31.0 还改变了什么见"如何修复",VulnCheck 的指标见"如何检查是否已被入侵"。
我受影响吗?
以下条件全部成立时,您处于暴露状态:
- 实例运行 2026-04-17 发布的 Gotenberg 8.31.0 之前的版本。
- API 端口(默认 3000)可被攻击者访问。GitHub 公告指出这在内部和云部署中很常见。
/forms/pdfengines/metadata/write端点已启用。它是 pdfengines 模块的一部分,没有认证保护。
| 分支 | 受影响版本 | 修复版本 |
|---|---|---|
| CVE-2026-42589 记录(cve.org) | 8.31.0 之前 | 8.31.0(2026-04-17) |
该漏洞于 2026 年 4 月初被报告时,Docker Hub 的 gotenberg/gotenberg:8 标签指向 8.29.1,因此当时拉取主标签的实例都是暴露的。
检查实际运行的版本,而不是您最近拉取的镜像:
# Running instance: the API reports its own version
curl -s http://your-gotenberg-host:3000/version
# Container: which image the running container uses
docker inspect <container> --format '{{.Config.Image}}'
# Image tag pinned in the deployment file
grep -R "gotenberg/gotenberg:" docker-compose.yml k8s/ 2>/dev/null
如何修复?
- 把镜像引用改为 8.31.0 或更高,例如
gotenberg/gotenberg:8.31.0或当前的8标签。 - 重建容器,而不是只拉取镜像:
docker compose up -d --force-recreate gotenberg,或在 Kubernetes 中执行等效的滚动更新。 - 用
curl -s http://your-gotenberg-host:3000/version验证结果。 - 将 3000 端口限制给需要的调用方。该端点从未要求认证,网络位置才是持久的控制手段。
跨次版本升级前,先了解 8.31.0 还改变了什么。该版本还通过拒绝环回、RFC1918 和其他非公开地址来加固出站 URL,给 --webhook-deny-list 设置了阻止内部网段的默认值,并停止发布 thecodingmachine/gotenberg 镜像。获取内部资产或调用内部 webhook 的流程在升级后可能需要调整标志。
元数据缺陷本身没有配置层面的绕过方案。易受攻击的代码路径在任何选项生效之前就已运行。
根本原因是什么?
Gotenberg 把调用方提供的 PDF 元数据键传给 ExifTool 时不会拒绝控制字符,因此键中的换行符会把 ExifTool 基于行的 stdin 协议拆成攻击者指定的参数行。
Gotenberg 用无界面 Chromium 和 LibreOffice 把文档转换为 PDF。其 pdfengines 模块暴露 /forms/pdfengines/metadata/write,接受一个 PDF 文件加一个 JSON 元数据对象。处理程序把每个键和值经 go-exiftool 库交给 ExifTool。
go-exiftool 库把每一对写成 ExifTool stdin 的一行:
fmt.Fprintln(e.stdin, "-"+k+"="+str)
fmt.Fprintln 在每次写入后追加一个换行符。当调用方的 JSON 键本身包含 \n 时,Go 的 json.Unmarshal 将其保留为 map 键中的字面换行,写入便拆成多行。ExifTool 把每一行当作独立参数。键 Title\n-if\nsystem('cmd')||1\n-Comment 因此变成参数序列 -Title、-if、system('cmd')||1、-Comment=x。
ExifTool 的 -if 标志把参数作为 Perl 表达式求值。system('cmd') 以 Gotenberg 进程身份运行 cmd。公告将报告归功于 S-Senhaji。
该模块早已知道某些键是危险的。dangerousTags 黑名单会删除 FileName 和 Directory 键,但它使用精确字符串匹配,从不剥离或拒绝控制字符,换行注入因此原样通过。
如何被利用?
公告中的概念验证是一条未认证的 HTTP 请求。它把 id 的输出写到服务器上的一个文件:
curl -s -o /dev/null -w "HTTP:%{http_code}" \
-X POST http://TARGET:3000/forms/pdfengines/metadata/write \
-F 'files=@sample.pdf;type=application/pdf' \
--form-string $'metadata={"Title\\n-if\\nsystem(\'id>/tmp/pwned\')||1\\n-Comment": "x"}'
# → HTTP:200
# On server: uid=1001(gotenberg) gid=1001(gotenberg) groups=1001(gotenberg),0(root)
- 攻击者 POST 一个有效 PDF,加上一个键中内嵌换行符的元数据 JSON 对象。
- Gotenberg 反序列化 JSON;键保留字面换行符。
- go-exiftool 把键写入 ExifTool 的 stdin,拆分为参数行。
- ExifTool 把
-if system('cmd')||1当作 Perl 表达式处理并执行cmd。 - 服务器返回 HTTP 200 和一个有效的 PDF,因此该请求在监控中看起来像一次正常转换。
公告的数据外带变体把命令输出编码为 base64 并发送到带外 HTTP 监听器,使交换过程避开服务器自身日志。
VulnCheck 的 10 月遥测展示了真实攻击者的用法。一个集群用 id 测试命令执行,通过 ExifTool 的 -Subject<= 操作符把输出读回应答 PDF 的 Subject 元数据,然后从三个独立镜像下载名为 cache_base 的自定义 Go 反向 shell。Node.js 下载后备手段值得注意:Gotenberg 容器为无界面 Chromium 携带 Node.js,因此在缺少 wget 和 curl 的地方攻击者仍有下载器。
攻击如何流动?
一句话:攻击者发送包含换行符的元数据键,该键在 ExifTool 的 stdin 上拆分为参数行,注入的 -if 标志求值 Perl system 调用,服务器在返回看似正常的 PDF 的同时执行了命令。
JSON 键 POST"] --> B["json.Unmarshal 保留
换行符"] B --> C["go-exiftool 把键写入
ExifTool stdin"] C --> D["换行符把该行拆成
新的参数行"] D --> E["-if 把
system('cmd')||1
当作 Perl 求值"] E --> F["命令以 Gotenberg
进程身份运行"] F --> G["HTTP 200 与有效 PDF:
没有任何错误信号"]
攻击者能做什么?
攻击者以 Gotenberg 进程身份运行操作系统命令。公告的输出显示进程为 uid=1001(gotenberg) gid=1001(gotenberg) groups=1001(gotenberg),0(root):一个属于 root 组的非特权用户。CVSS 向量为 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H,评分 9.8。
条件极少:到 3000 端口的网络访问,无需凭据,无需用户交互。应答是一次成功转换,HTTP 交换中没有任何东西暴露攻击。
后续取决于操作者。VulnCheck 把观测到的活动归因于至少七个目标各异的集群:批量扫描、挖矿、Python 反向 shell,以及自定义 Go 反向 shell cache_base。被 VulnCheck 引用的 The Hunter's Ledger 报告了一次挖矿活动,确认约 200 个 Gotenberg 实例可被利用,并在其中约 150 个上以 polkitd 之名安装了 XMRig。
为什么修复有效?
修复是 8.31.0 版本中的三个提交,全部位于同一个 exiftool 模块。第一个拒绝一切不是普通标签名的键:
// safeKeyPattern matches legitimate ExifTool tag names: alphanumeric,
// hyphens, underscores, colons, and periods. Rejects control characters
// (especially \n) that would inject stdin arguments via go-exiftool's
// line-based protocol.
var safeKeyPattern = regexp.MustCompile(`^[a-zA-Z0-9\-_.:]+$`)
元数据对象中的每个键现在必须先匹配 safeKeyPattern 才能到达 go-exiftool。换行符、回车符或任何其他控制字符都无法匹配,请求被以 HTTP 400 拒绝。
第二个提交把同样的控制扩展到值:
func validateMetadataValue(key, value string) error {
if strings.ContainsAny(value, "\n\r\x00") {
return fmt.Errorf("write PDF metadata with ExifTool: invalid metadata value for key %q (contains control character): %w", key, gotenberg.ErrPdfEngineMetadataValueNotSupported)
}
return nil
}
第三个提交在危险标签检查前去除 System: 等 ExifTool 组前缀,使 System:FileName 无法再绕过检查,并把 FilePermissions 加入该黑名单。三者一起关闭了键和值中的参数走私路径,发布说明将其描述为阻止参数走私和文件系统伪标签滥用。
如何检查是否已被入侵?
VulnCheck 发布了 cache_base 集群的指标。请全部检查:
- 网络指标:与
210.170.22.208(日本住宅,观测到的攻击源)、39.105.65.32(Gitea 载荷服务器,阿里云北京)、132.145.123.143(载荷主机,Oracle Cloud 东京)、5.161.100.63(第二个植入体的 C2,Hetzner)的通信,以及域名ubuntu-repo.strangled[.]net、linux-x86-tcpudp.strangled[.]net、cheeva.strangled[.]net。 - 请求形态:发往
/forms/pdfengines/metadata/write且元数据 JSON 中含内嵌换行符的 POST 请求。观测到的载荷使用以Title\n-if\n开头的键以及临时文件/tmp/nx_cmd.txt。 - 主机痕迹:名为
.cached_db.lock的隐藏锁文件、植入体cache_base,以及 Go 植入体设置的环境变量PYTHONUNBUFFERED=1。 - 植入体哈希:
cache_baseMD5f5a756d13697cdfa69ed795d4468339d,cache_arm64MD5c31cd755293844f8ac0802d2c438e701,cache_mipsMD52b85605660343399a650b8c3745bccb0。 - 其他集群的指标:以进程名
polkitd运行的 XMRig,以及对4thepool_miner.sh、来自 S3 存储桶的banner_homepage_800x800.jpg、来自185.132.53.158:81/anonymus/client的载荷的下载。
仅命中元数据端点只是线索而非证据:合法客户端也会写 PDF 元数据。键中的换行符、临时文件和锁文件才是区分性的信号。
时间线
- 2026-04-06 至 2026-04-13:维护者在 8.31.0 分支中合入三个 ExifTool 加固提交。
- 2026-04-17:Gotenberg 8.31.0 发布,包含元数据净化和 SSRF 加固。
- 2026-04-30:GitHub 公告 GHSA-rqgh-gxv4-6657 发布,附带可用的概念验证。
- 2026-05-14:CVE-2026-42589 记录连同受影响范围发布。
- 2026-06-26:VulnCheck 把该缺陷加入其自有 KEV 式目录。截至 2026-10-30 CISA 尚未加入。
- 2026-08-31:一个 XMRig 集群攻击该缺陷。VulnCheck 观测到的最早探测来自轮换的 Telecom Italia 地址,从 6 月下旬持续到 8 月上旬。
- 2026-10-19 至 2026-10-24:Contabo 扫描器
5.189.138.115产生 364 次事件,超过 30 天总量的四分之一。 - 2026-10-26:VulnCheck 发布关于利用和
cache_base植入体的分析。
常见问题
为什么在利用之前没有扫描器发现它?
该缺陷有 CVE 和 GitHub 公告,但扫描器只能标记已盘点的东西。Docker 镜像不是包依赖,依赖扫描器看不见它,而容器仍在正常转换 PDF。版本端点才是可靠的检查。
缺陷在 ExifTool 还是 Gotenberg?
公告把它放在 Gotenberg。ExifTool 的 -if 行为是有文档的,go-exiftool 的行协议是库的设计。Gotenberg 控制进入该协议的内容,而它已有的黑名单表明维护者接受这份责任;修复完全位于 Gotenberg 的代码中。
cache_base 植入体做什么?
它是一个 5.2 MB 的去符号 Go 二进制,获取一个锁,向 ipinfo.io 查询受害者的位置,连接两个 C2 之一,并在连接与 /bin/sh 之间传送 shell。它自身没有持久化机制,但其工具包里有一个会重启它的看门狗。
我的实例在内网。我仍是目标吗?
正如公告所指出,内部和云部署常常让 3000 端口可达,仅一个 30 天窗口就统计到来自 128 个地址的 1,358 次事件。内网位置缩小了谁能到达该端口;它并没有消除缺陷。仍然要修复。
修复之后我需要轮换什么吗?
如果侵害检查匹配,请将该主机视为已被入侵:轮换 Gotenberg 进程能读到的一切,包括其环境可访问的服务凭据和 API 密钥,并从修复后的镜像重新部署,而不是就地清理。
用 Ostorlab 找出暴露的 Gotenberg 服务器
Ostorlab 的攻击面发现会盘点您暴露在互联网上的主机与服务,找出暴露文档转换 API 的主机,抢在攻击者之前检查其版本。
参考文献
- GitHub 公告 GHSA-rqgh-gxv4-6657,CVE-2026-42589,CVSS v3.1 9.8,2026-04-30 发布,含根本原因与概念验证: https://github.com/gotenberg/gotenberg/security/advisories/GHSA-rqgh-gxv4-6657 (2026 年 10 月 30 日核对)
- CVE-2026-42589 记录,受影响版本为 8.31.0 之前,2026-05-14 发布: https://www.cve.org/CVERecord?id=CVE-2026-42589 (2026 年 10 月 30 日核对)
- VulnCheck,"Cache Me If You Can: Unpacking a Go Implant and Its Toolkit",Jacob Baines,2026 年 10 月 26 日,利用计数、七个集群及指标: https://www.vulncheck.com/blog/cache-me-if-you-can (2026 年 10 月 30 日核对)
- Gotenberg 8.31.0 发布说明,2026-04-17 发布,ExifTool 净化与破坏性变更: https://github.com/gotenberg/gotenberg/releases/tag/v8.31.0 (2026 年 10 月 30 日核对)
- 修复提交 405f1069c026,"fix(exitool) [sic]: prevent control characters",2026-04-06: https://github.com/gotenberg/gotenberg/commit/405f1069c026 (2026 年 10 月 30 日核对)
- 修复提交 55d19522a816,"fix(exiftool): prevent line breaks",2026-04-11: https://github.com/gotenberg/gotenberg/commit/55d19522a816 (2026 年 10 月 30 日核对)
- 修复提交 46e190970fed,"fix(exiftool): remove System: prefixes",2026-04-13: https://github.com/gotenberg/gotenberg/commit/46e190970fed (2026 年 10 月 30 日核对)
- Gotenberg 仓库,2026 年 10 月 30 日 13,478 星: https://github.com/gotenberg/gotenberg (2026 年 10 月 30 日核对)
- Docker Hub 上的 Gotenberg,2026 年 10 月 30 日 101,471,977 次拉取: https://hub.docker.com/r/gotenberg/gotenberg (2026 年 10 月 30 日核对)
- The Hunter's Ledger,针对约 200 个可被利用的 Gotenberg 实例的 XMRig 活动,VulnCheck 引用: https://the-hunters-ledger.com/reports/gotenberg-rce-cryptomining-107-175-69-137/ (2026 年 10 月 30 日核对)
- CISA KEV 目录 JSON 数据源,2026 年 10 月 30 日核对 CVE-2026-42589(未列入): https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (2026 年 10 月 30 日核对)