CVE-2018-16873 is a remote code execution (RCE) vulnerability in the Go toolchain's go get command, disclosed in 2018 alongside two related flaws in the same subsystem. The bug let an attacker who controlled a crafted import path trick go get into invoking a version-control command with attacker-supplied arguments, turning an ordinary dependency fetch into arbitrary code execution on the machine that ran it. For any team that treats go get as a routine part of building Golang software — which in 2018 meant nearly every Go shop on earth — this was a serious supply-chain wake-up call: fetching a single malicious package could silently compromise a developer laptop or a CI runner.
CVE-2018-16873 was disclosed together with two sibling issues, CVE-2018-16874 (a directory-traversal flaw in go get that could write files outside GOPATH) and CVE-2018-16875 (a crypto/x509 CPU exhaustion denial-of-service triggered by crafted certificate chains). All three were fixed in the same toolchain release, but CVE-2018-16873 is the one that matters most from a supply-chain-security perspective, because it demonstrates how a routine dependency-management action — one developer typing go get example.com/somepackage — can be weaponized into full command execution.
What Is CVE-2018-16873?
To understand the bug, it helps to understand how go get resolves "vanity" or custom import paths. When a package's import path doesn't point at a hard-coded, well-known host like github.com or bitbucket.org, the Go tool fetches https://<host>/<path>?go-get=1 and looks for an HTML <meta name="go-import" content="root-path vcs repo-url"> tag. That tag tells go get which version control system (Git, Mercurial, Bazaar, or Subversion) to use and which repository URL to fetch. This mechanism is what lets organizations publish clean import paths such as golang.org/x/... or k8s.io/... while the underlying code actually lives somewhere else.
The flaw in pre-fix versions of the Go toolchain was in how go get inferred trust for paths that looked like they belonged to GitHub, Bitbucket, or Launchpad — even when the request had been redirected through, or served metadata from, a different, attacker-controlled host. By crafting an import path and hosting response that exploited this inference logic, an attacker could get go get to shell out to a DVCS binary with a repository "URL" field that was actually built to be parsed as command-line arguments rather than a plain address. Because tools like Git and Mercurial support flags and transport helpers (Git's ext:: transport being the canonical example) that can execute arbitrary shell commands, smuggling one of those through the go-import metadata amounted to classic command injection: the victim's own go get process became the vehicle for running attacker-chosen code.
In short, the exploit chain looked like: crafted import path → spoofed or redirected go-import metadata → go get invokes a version-control command with attacker-controlled arguments → command injection → remote code execution on the developer machine or build server that ran the fetch. No credential theft or prior foothold was required — just convincing a victim (or an automated pipeline) to run go get against a hostile or compromised import path, something that happens constantly whenever teams add a new dependency or a CI job resolves go.mod/GOPATH packages fresh.
Affected Versions and Components
CVE-2018-16873 affected the go command itself — specifically the dependency-fetching logic in cmd/go's internal get and version-control-system handling — in all Go toolchain releases prior to the patched versions. That means any project, container image, or CI pipeline invoking go get (whether through GOPATH-style workflows or the early days of Go modules) with an unpatched toolchain was exposed, regardless of the application code being built. This was a toolchain-level vulnerability rather than a flaw in any particular Go program, which is exactly why it deserves attention years later: outdated Go toolchains still show up embedded in old base images, cached build tools, and legacy CI runners.
CVE-2018-16873 Severity, CVSS, and Exploitation Context
Public vulnerability trackers classify CVE-2018-16873 as a high-severity issue given the unauthenticated, network-reachable path to full remote code execution once a victim resolves a hostile import path. As with most supply-chain-style bugs, the practical blast radius for any given organization depends heavily on context: a pinned, vendored, or module-proxy-cached dependency set is far less exposed than a CI pipeline that freely runs go get against arbitrary, unreviewed import paths at build time.
CVE-2018-16873 does not appear on CISA's Known Exploited Vulnerabilities (KEV) catalog, and EPSS-style exploitation-probability scoring for the CVE has stayed low — consistent with a well-patched, multi-year-old toolchain issue rather than something under active mass exploitation. Low EPSS should not be read as "safe to ignore," though. The exposure doesn't decay with time the way exploitation trends do; it resurfaces the moment an old Go toolchain gets reused in a new build image, a vendored CI cache, or an unmaintained internal tool that nobody has upgraded since 2018.
Timeline
- Pre-2018: The
go getcustom-domain resolution logic incmd/goallows meta-tag-driven host inference to be spoofed for GitHub-, Bitbucket-, and Launchpad-style import paths, creating the conditions for command injection via crafted VCS arguments. - Early July 2018: The Go security team prepares a coordinated fix for three related toolchain issues: the
go getremote code execution bug (CVE-2018-16873), a companiongo getdirectory-traversal bug (CVE-2018-16874), and acrypto/x509certificate-chain CPU-exhaustion denial-of-service (CVE-2018-16875). - Mid-July 2018: Go 1.10.3 and Go 1.9.7 are released, closing all three issues at the toolchain level. Teams on either the current (1.10.x) or previous (1.9.x) supported release line receive a fix.
- Following weeks: The fixes propagate downstream into Linux distribution packages, container base images, and Go module proxies; CVE-2018-16873 is formally cataloged by NVD/MITRE.
- Later (Go 1.11 onward): Go's module system matures with
go.sumchecksums and thesum.golang.orgchecksum database (GOSUMDB) plus module proxy support, meaningfully reducing reliance on live, unauthenticated DVCS fetches during ordinarygo getoperations and giving teams a cryptographic way to detect tampering that this class of bug depended on going unnoticed.
Remediation and Mitigation
Because CVE-2018-16873 lives in the Go toolchain rather than in application code, remediation is primarily about toolchain hygiene and build-pipeline discipline rather than patching a library dependency:
- Upgrade the Go toolchain. Run Go 1.10.3, Go 1.9.7, or any later release — and in practice, run a currently supported Go version, since the project only maintains security fixes for the two most recent major releases. If you find a build image, CI runner, or embedded tool still shelling out with a pre-1.10.3
gobinary, that's a direct exposure to this issue. - Move fully onto Go modules. Adopting
go.mod/go.sumand Go's module proxy and checksum database (GOSUMDB) reduces how oftengo getneeds to perform a live, unauthenticated fetch against an arbitrary host, and adds a verifiable integrity layer that this vulnerability class undermines. - Pin and verify dependencies. Commit
go.sum, use-mod=readonly(or vendor dependencies) in CI, and treat any unexpected change to a checksum or an unfamiliar new import host as a signal worth investigating before merging. - Restrict build-time network egress. Running
go get/go buildin CI sandboxes with tightly scoped, allow-listed egress limits how much damage a command injection can do even if a malicious import path slips through. - Audit for stale toolchains. Inventory container base images, CI agents, and developer tooling for Go versions predating the 2018 fixes — this is a "known, patched" CVE, but only for the environments that actually got the update.
- Review new dependencies before adding them. Custom/vanity import paths are useful, but they also mean trusting whatever a remote host decides to serve back at fetch time; treat adding a new one with the same scrutiny as adding a new build dependency from any other untrusted source.
How Safeguard Helps
Vulnerabilities like CVE-2018-16873 are exactly the kind of risk that hides in plain sight: the fix has existed for years, yet outdated Go toolchains keep resurfacing in forked build images, legacy CI configurations, and vendored tool caches long after everyone assumes the problem is solved. Safeguard's software supply chain security platform is built to catch precisely this gap.
Safeguard continuously builds SBOMs and toolchain inventories across your build pipelines and container images, so a Go compiler version predating 1.10.3 or 1.9.7 gets flagged automatically instead of quietly shipping in a new base image. Policy checks can block builds that rely on known-vulnerable toolchains, and dependency monitoring watches go.mod/go.sum changes for suspicious custom import paths, unexpected checksum drift, or dependencies pulled from hosts that don't match their claimed origin — the same pattern that made the original go get command-injection exploit possible. Combined with build provenance and CI egress visibility, Safeguard helps Golang teams verify that every dependency fetch resolves to what it claims to be, closing the exact class of trust-on-first-fetch gap that CVE-2018-16873 exploited, and giving security and platform teams a durable way to prove their Go build pipelines aren't quietly running on toolchains from 2018.