CVE-2026-23745

Tar-pit of Doom: Escaping the Root in node-tar

Alon Barad
Alon Barad
Software Engineer

Jan 16, 2026·6 min read

Executive Summary (TL;DR)

node-tar <= 7.5.2 failed to sanitize the targets of hardlinks and symlinks. If an archive contains a link pointing to an absolute path (e.g., `/etc/passwd`), node-tar would happily create it, ignoring the intended extraction directory. This leads to Arbitrary File Overwrite and potential RCE via config file manipulation.

A critical path traversal vulnerability in the ubiquitous node-tar library allows malicious archives to bypass extraction root restrictions. By manipulating hardlink and symlink targets with absolute paths, attackers can overwrite arbitrary system files or poison symbolic links, effectively turning a standard unzip operation into a weaponized file system assault.

The Hook: When 'Unzip' becomes 'Pwn'

We take extraction libraries for granted. You run npm install, you unzip a backup, you pull an artifact in your CI/CD pipeline. Under the hood, specifically in the Node.js ecosystem, node-tar is the workhorse doing the heavy lifting. It’s supposed to be a boring, reliable utility. It takes a stream of bytes and turns them into files on your disk.

However, the golden rule of archive extraction is containment. When I tell the library to extract evil.tar into ./output, I expect every single file to land inside ./output. If a file lands in /etc/, the library has failed its one job. This is the contract of the preservePaths: false setting (which is the default, secure mode).

CVE-2026-23745 is a breach of that contract. It turns out that while node-tar was very careful about where it placed files, it was completely negligent about what those files pointed to. It’s like a bouncer checking your ID at the door but ignoring the loaded bazooka slung over your shoulder.

The Flaw: Absolute Power Corrupts Absolute Paths

The vulnerability stems from a fundamental misunderstanding of how Node.js handles path resolution, mixed with a lapse in input validation coverage. The issue resides specifically in how Link (hardlink) and SymbolicLink entries are handled.

In Node.js, path.resolve(from, to) is the standard way to resolve paths. However, it has a quirk that trips up developers constantly: if the second argument is an absolute path, the first argument is ignored completely.

[!NOTE] path.resolve('/safe/dir', '/etc/passwd') returns /etc/passwd, NOT /safe/dir/etc/passwd.

node-tar used this logic to determine where to create a hardlink. It took the current working directory (this.cwd) and tried to resolve the entry.linkpath (the target of the link) against it. Because the developer didn't check if entry.linkpath was absolute before passing it to resolve, an attacker could craft a TAR header saying: "I am a hardlink. Please link me to /root/.ssh/authorized_keys."

For symlinks, it was even lazier. The code simply passed the raw linkpath from the TAR header directly to fs.symlink. No questions asked. If the header said symlink -> /etc/shadow, node-tar obliged.

The Code: Anatomy of a Screw-Up

Let's look at the crime scene in src/unpack.ts. The validation routine [CHECKPATH] was guarding the front door (entry.path), but the back door (entry.linkpath) was left wide open.

The Vulnerable Logic (Conceptual):

// Hardlink handling
if (entry.type === 'Link') {
  // THE BUG: If entry.linkpath is absolute, target becomes absolute
  // ignoring this.cwd entirely.
  const target = path.resolve(this.cwd, entry.linkpath);
  fs.link(target, entry.absolute, cb);
}
 
// Symlink handling
if (entry.type === 'SymbolicLink') {
  // THE BUG: entry.linkpath is passed raw to the filesystem
  fs.symlink(entry.linkpath, entry.absolute, cb);
}

The Fix (Commit 340eb28):

The patch introduces a unified sanitizer, [STRIPABSOLUTEPATH]. This method explicitly strips out root indicators (like leading slashes or drive letters) and strictly validates that the path doesn't try to traverse upwards using ...

Crucially, the patch applies this check to both properties:

// The Fix: Validate BOTH destination and link source
if (
  !this[STRIPABSOLUTEPATH](entry, 'path') ||
  !this[STRIPABSOLUTEPATH](entry, 'linkpath') // <--- The critical addition
) {
  return false // Abort extraction if dirty
}

This forces the linkpath to be relative to the extraction root, effectively jailing the operation back to the intended directory.

The Exploit: Smashing the Filesystem

To exploit this, we don't need buffer overflows or ROP chains. We just need to understand the TAR format. We can manually craft a header that lies about its link target.

Attack Scenario: The Config Overwrite

Imagine a CI/CD pipeline that extracts a user-provided tarball to lint the code. The runner creates a temporary directory, extracts the files, and then cleans up.

  1. Preparation: Attacker creates a malicious TAR.
    • Entry 1: Type Link (Hardlink).
    • Path: temp_extraction/harmless.txt
    • LinkPath: /home/runner/.bashrc (Targeting the runner's shell config)
  2. Execution: The pipeline runs tar.x({ file: 'malicious.tar', cwd: './temp' }).
  3. Trigger: node-tar sees the hardlink request. It executes link('/home/runner/.bashrc', './temp/harmless.txt').
    • Now, harmless.txt and .bashrc are the same inode.
  4. The Kill: If the pipeline (or the attacker via a second file entry) writes anything to harmless.txt, it instantly overwrites .bashrc.

Here is the visual flow of the attack:

This allows an attacker to achieve persistence or RCE the next time a shell is spawned on that machine.

The Impact: Why You Should Panic (Slightly)

This isn't just about overwriting a text file. The implications depend heavily on who is extracting the archive and where.

  1. CI/CD Poisoning: As described above, overwriting .bashrc, .ssh/authorized_keys, or /etc/hosts on a build agent allows for lateral movement and supply chain injection.
  2. Server-Side Extraction: If a web application accepts a TAR upload (e.g., "Upload your portfolio!") and extracts it to serve static files, an attacker can create a symlink to /etc/passwd named portfolio/index.html. When the web server tries to serve index.html, it reads the password file instead.
  3. Dependency Confusion: node-tar is used by npm itself (though npm usually validates package contents strictly). However, custom tooling that wraps node-tar is highly vulnerable.

The CVSS 4.0 score is 8.2 (High) because while it requires local interaction (uploading a file), the complexity is low and the impact on Confidentiality and Integrity is high.

The Fix: Stop the Bleeding

The remediation is straightforward, but urgency is required due to the ubiquity of the library.

Immediate Action: Update node-tar to version 7.5.3 or higher immediately. Check your package-lock.json or yarn.lock because this library is likely deeply nested in your dependency tree.

npm install node-tar@latest
# Or if it is a sub-dependency
npm update node-tar --depth 99

Defensive Coding: If you are using node-tar programmatically, ensure you are NOT setting preservePaths: true unless you absolutely trust the source of the archive. That setting explicitly disables protections.

Also, consider validating the contents of archives before extraction using a stream parser to check for suspicious Link or SymbolicLink targets starting with / or containing ...

Fix Analysis (1)

Technical Appendix

CVSS Score
8.2/ 10
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N

Affected Systems

Node.js applications using node-tarCI/CD pipelines processing untrusted archivesServer-side applications with file upload/extraction features

Affected Versions Detail

Product
Affected Versions
Fixed Version
node-tar
isaacs
<= 7.5.27.5.3
AttributeDetail
CWE IDCWE-22 (Path Traversal)
CVSS 4.08.2 (High)
Attack VectorLocal (Archive Upload)
Affected Componentsunpack.ts (Link/SymbolicLink handling)
ImpactArbitrary File Overwrite / Symlink Poisoning
Exploit StatusProof of Concept Available
CWE-22
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Vulnerability Timeline

Vulnerability Disclosed
2026-01-16
Patch Released (v7.5.3)
2026-01-16
CVE-2026-23745 Assigned
2026-01-16

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.