GHSA-fxhp-mv3v-67qp: `oras-go` tar extraction: Hardlink entry with relative Linkname escapes extract dir via process CWD resolution
Root cause
The tar-extraction helper ensureLinkPath at content/file/utils.go:262-275 validates that a hardlink's target resolves inside the extract base, but then returns the original unresolved target string back to the caller:
func ensureLinkPath(baseAbs, baseRel, link, target string) (string, error) {
path := target
if !filepath.IsAbs(target) {
path = filepath.Join(filepath.Dir(link), target) // resolved FOR VALIDATION
}
if _, err := resolveRelToBase(baseAbs, baseRel, path); err != nil {
return "", err
}
return target, nil // <-- returns the ORIGINAL target, not the validated path
}
The caller for TypeLink hardlinks then does:
case tar.TypeLink:
var target string
if target, err = ensureLinkPath(dirPath, dirName, filePath, header.Linkname); err == nil {
err = os.Link(target, filePath)
}
os.Link(oldname, newname) wraps the link(2) system call. From the link(2) man page:
oldpath and newpath are interpreted relative to the current working directory of the calling process.
So when target (i.e., header.Linkname) is a relative path, os.Link resolves it against the process's current working directory, not against filepath.Dir(link) as the validation assumed.
Attack
An attacker who controls an OCI-compliant registry (or any artifact source the victim consumes via oras pull) crafts a tarball layer with:
- A regular file: payload.tar.gz/README.txt.
- A hardlink entry: Typeflag=TypeLink, Name=payload.tar.gz/evil_cwd_link, Linkname="victim.secret" (relative).
and marks the layer descriptor with io.deis.oras.content.unpack: "true" (a standard annotation that tells oras-go to auto-extract).
When a victim runs oras pull (or any Go code using content.File), the extraction:
1. Validates payload.tar.gz/evil_cwd_link — passes.
2. Calls ensureLinkPath(dirPath, "payload.tar.gz", filePath, "victim.secret"):
- path = filepath.Join(filepath.Dir(filePath), "victim.secret") = <extract_base>/payload.tar.gz/victim.secret → inside base → validation passes.
- Returns target = "vic
Details
Original advisory: https://github.com/advisories/GHSA-fxhp-mv3v-67qp
Exploitation outlook
EPSS (FIRST.org) estimates each CVE’s probability of exploitation in the next 30 days — here is the CSIRTS.com read on those numbers.
- Low exploitation riskCVE-2026-501630.34% 30-day exploitation probability — currently an unlikely target, but scores change as exploit code circulates. Riskier than 27% of all scored CVEs.
Referenced CVEs
| CVE | CSIRTS overview | External |
|---|---|---|
| CVE-2026-50163 | coverage & exploitation status | NVD · CVE.org |
Same CVEs, other sources
How other CERTs, PSIRTs and databases cover the vulnerabilities in this advisory.
More from GitHub Security Advisories
- mediumGHSA-xm43-3m56-w3wf: Ghost: Paid gift memberships obtainable at minimal cost via the donations feature2026-08-04
- mediumGHSA-chgm-3698-jm42: Ghost: Member existence leak via magic link sign-in response2026-08-04
- highGHSA-xpp7-93x6-v29m: XSS in Ghost's ActivityPub client2026-08-04
- mediumGHSA-7mpp-r37j-x5wh: Ghost: Session Fixation in Ghost Admin2026-08-04
- mediumGHSA-cjc9-q5gf-327p: Ghost: Theme Upload Path Traversal2026-08-04