Few tools are as trusted, and as automatically invoked, as a language's own package fetcher. That trust is exactly what made CVE-2018-16874 worth taking seriously: a directory traversal vulnerability in Go's go get command that let a malicious package escape its intended workspace directory and write files anywhere the running user could reach. Disclosed in December 2018 alongside two sibling Go vulnerabilities, CVE-2018-16874 is a reminder that dependency resolution logic — the code that decides where on disk a fetched package lands — is itself part of your attack surface. If an attacker can influence that decision, they can potentially plant files, overwrite scripts, or set up follow-on code execution before you've even opened the code you meant to review.
What CVE-2018-16874 Actually Is
CVE-2018-16874 affects the Go toolchain's handling of import paths when running go get in GOPATH mode (the pre-Go-modules dependency layout, where source lived under $GOPATH/src). The go command builds a local filesystem path from a package's import path — for example, github.com/user/repo maps to $GOPATH/src/github.com/user/repo. The vulnerability arose because the toolchain did not properly validate import paths that contained curly brace characters ({ and }). By crafting an import path with carefully placed curly braces, a malicious package could cause go get to resolve a destination path that traversed outside the intended $GOPATH/src tree entirely.
The practical consequence is arbitrary filesystem write: an attacker-controlled package, once fetched via go get, could write files to locations the victim never intended — including places that could later be executed, sourced, or loaded as trusted code, turning a simple directory traversal into a path toward remote code execution. Because go get is routinely run by developers and CI pipelines pulling in transitive dependencies, exploitation didn't require tricking anyone into running odd commands — just getting a poisoned import path into a dependency chain that got fetched.
Importantly, the flaw is scoped to GOPATH mode. Projects that had already adopted Go modules — the dependency management system Go introduced in 1.11 and made default in later releases — were not affected by this specific traversal bug, since Go modules use a different, content-addressed module cache layout rather than deriving a raw filesystem path straight from an untrusted import string. This is one of several reasons the Go team pushed hard for the ecosystem to migrate off GOPATH and onto Go modules in the years that followed.
Affected Versions and Components
- Component:
cmd/go, specifically thego getcommand's import-path-to-filesystem-path resolution logic. - Affected versions: Go before 1.10.6, and Go 1.11.x before 1.11.3.
- Mode: GOPATH mode only — Go modules mode was not vulnerable to this traversal.
- Trigger: Fetching (via
go get) a malicious or compromised package whose import path contains curly brace characters.
CVE-2018-16874 was disclosed together with two related cmd/go and crypto/x509 issues fixed in the same release cycle: CVE-2018-16873 (a go get -u issue in GOPATH mode that could lead to remote command execution via a crafted vanity import path ending in /.git) and CVE-2018-16875 (a CPU denial-of-service issue in crypto/x509 certificate chain verification). The three were patched together, but they are distinct vulnerabilities with distinct root causes — CVE-2018-16874 is specifically the curly-brace path traversal in go get.
Severity: CVSS, EPSS, and KEV Context
NVD scores CVE-2018-16874 at 8.1 (High) under CVSS v3.1 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H) — reflecting a network-exploitable issue with high confidentiality, integrity, and availability impact, tempered by high attack complexity since an attacker needs a victim to fetch the malicious import path. The legacy CVSS v2 score is lower, 6.8 (Medium), which is typical for older NVD entries where scoring methodology has since evolved.
CVE-2018-16874 does not appear on CISA's Known Exploited Vulnerabilities (KEV) catalog, and there is no widely publicized evidence of in-the-wild exploitation. Given its age, narrow trigger condition (GOPATH-mode go get plus a specially crafted import path), and the ecosystem's broad migration to Go modules, its current EPSS-style exploitation likelihood is low. That said, "low likelihood today" is not the same as "irrelevant" — organizations still running legacy Go toolchains, vendored build pipelines, or older CI images that predate the modules era can still be exposed if they fetch untrusted or third-party packages.
Timeline
- Discovery: The curly-brace directory traversal in
go getwas reported by security researcher ztz of Tencent Security Platform, credited in the Go project's tracking issue (golang/go#29231). - December 13, 2018: The Go team publicly disclosed CVE-2018-16874 alongside CVE-2018-16873 and CVE-2018-16875, and shipped fixed releases.
- Fix released in Go 1.10.6 and Go 1.11.3: Both point releases patched the import-path validation logic in
cmd/goto reject import paths containing unsafe characters like curly braces before they could be turned into filesystem paths. - Ongoing: Downstream Linux distributions (Debian, SUSE, Arch, Alpine, and others) and Oracle's GCC Go implementation subsequently shipped their own backported fixes and advisories through their standard package update channels.
Remediation Steps
- Upgrade your Go toolchain. Move to Go 1.10.6, Go 1.11.3, or any later release. Every subsequent Go version includes this fix, so any reasonably current toolchain is unaffected.
- Migrate off GOPATH mode to Go modules. Beyond fixing this specific CVE, adopting
go.mod-based dependency management removes an entire class of import-path-to-filesystem-path traversal risk, since modules are fetched into a versioned, checksum-verified module cache rather than a raw path derived from an import string. - Verify build infrastructure, not just developer machines. CI runners, Docker build images, and internal package mirrors that still invoke
go getin GOPATH mode are just as exposed as a developer laptop — audit and patch these environments explicitly, since they're often the last places to get a Go version bump. - Enable Go's checksum database and module verification (
GOSUMDB/GONOSUMCHECK) where applicable, and pin dependencies withgo.sumto reduce the chance of an unexpected or tampered package entering the build in the first place. - Audit third-party and vendored Go code for stale GOPATH assumptions. Older internal tools, build scripts, or forked packages that hardcode GOPATH-style layouts are worth flagging during modernization efforts, even years after the original CVE-2018-16874 fix shipped.
How Safeguard Helps
CVE-2018-16874 is a textbook example of why software supply chain security can't stop at "is this package's code safe to run" — it has to ask "is the tooling that fetches and builds this package itself trustworthy." Safeguard is built around that broader view of supply chain risk.
Safeguard continuously inventories the language toolchains, build tools, and dependency managers across your engineering fleet — not just your application dependencies — so a lingering pre-1.10.6 or pre-1.11.3 Go installation on a build server or legacy CI image doesn't go unnoticed. When a vulnerability like CVE-2018-16874 is disclosed, Safeguard correlates it against real, observed usage across your repositories and pipelines, distinguishing between projects still running vulnerable GOPATH-mode workflows and those that have already migrated to Go modules, so security teams can prioritize the environments that actually carry risk instead of chasing every CVE with equal urgency.
Beyond detection, Safeguard's software composition analysis flags risky dependency-resolution patterns — including reliance on outdated GOPATH layouts, unpinned import paths, and toolchain versions that predate known cmd/go security fixes — as part of ongoing supply chain posture monitoring. Paired with build provenance and SBOM tracking, this gives teams the visibility to answer a question that matters well beyond any single CVE: when a malicious or compromised package tries to abuse the mechanics of how your language's tooling fetches and places code, will you find out before it writes a single unwanted file, or after?