CVE-2018-20834
Description
A vulnerability was found in node-tar before version 4.4.2 (excluding version 2.2.2). An Arbitrary File Overwrite issue exists when extracting a tarball containing a hardlink to a file that already exists on the system, in conjunction with a later plain file with the same name as the hardlink. This plain file content replaces the existing file content. A patch has been applied to node-tar v2.2.2).
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
node-tar versions before 4.4.2 allow arbitrary file overwrite via crafted tarball with hardlink and subsequent plain file.
Vulnerability
Overview
CVE-2018-20834 is an arbitrary file overwrite vulnerability in the node-tar library, a widely used Tar implementation for Node.js. The issue exists in versions before 4.4.2 (excluding 2.2.2) and is triggered when extracting a specially crafted tarball that contains a hardlink entry pointing to an existing file on the system, followed by a plain file entry with the same name as the hardlink. During extraction, the plain file's content overwrites the existing file content, bypassing normal protections [4].
Exploitation
Mechanism
To exploit this vulnerability, an attacker must supply a malicious tarball that exploits the order of extraction. The tarball first places a hardlink to a target file (e.g., a critical system configuration file), then later includes a plain file entry with the same name. The node-tar library's extraction logic processes each entry sequentially; when it encounters the plain file after the hardlink, it overwrites the inode that the hardlink points to, effectively modifying the original, existing file. This attack requires no special authentication beyond the ability to trigger extraction of the crafted archive, making it particularly dangerous in contexts where tarballs are extracted automatically, such as package installation with npm [1][3].
Impact
Successful exploitation allows an attacker to overwrite arbitrary files on the system with attacker-controlled content, potentially leading to privilege escalation, denial of service, or arbitrary code execution depending on the file targeted. Since node-tar is used by npm to extract packages from the registry, an attacker who publishes a malicious package or compromises a package could overwrite files outside the extraction directory, affecting the host system [2].
Mitigation
The vulnerability has been patched in node-tar version 2.2.2 and later 4.4.2. Users are strongly advised to update to these versions or later. Red Hat also released an erratum (RHSA-2019:1821) to address the issue in affected products [2]. As a general best practice, extraction should be performed in isolated directories and with the principle of least privilege, but the patch directly resolves the hardlink overwriting flaw.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
tarnpm | >= 3.0.0, < 4.4.2 | 4.4.2 |
tarnpm | < 2.2.2 | 2.2.2 |
Affected products
2Patches
27ecef07da6a9Bump fstream to fix hardlink overwriting vulnerability
5 files changed · +65 −5
lib/parse.js+0 −4 modified@@ -251,10 +251,6 @@ Parse.prototype._startEntry = function (c) { if (onend) entry.on("end", onend) - if (entry.type === "File" && this._hardLinks[entry.path]) { - ev = "ignoredEntry" - } - this._entry = entry if (entry.type === "Link") {
package.json+1 −1 modified@@ -13,7 +13,7 @@ }, "dependencies": { "block-stream": "*", - "fstream": "^1.0.2", + "fstream": "^1.0.12", "inherits": "2" }, "devDependencies": {
test/link-file-entry-collision/bad-link.hex+25 −0 added@@ -0,0 +1,25 @@ +-- header for the link target -- +6261642d6c696e6b2d74617267657400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000303030363434200030303037363520003030303032342000303030303030303030333420313334363636353530353620303134333731002030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # bad-link-target.....................................................................................000644..000765..000024..00000000034.13466655056.014371..0................................................................................................... +00757374617200303069736161637300000000000000000000000000000000000000000000000000007374616666000000000000000000000000000000000000000000000000000000303030303030200030303030303020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # .ustar.00isaacs..........................staff...........................000000..000000......................................................................................................................................................................... + +-- link target file contents (should not be overwritten) -- +746869732073686f756c642072656d61696e207468652073616d650a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # this.should.remain.the.same..................................................................................................................................................................................................................................... +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # ................................................................................................................................................................................................................................................................ + +-- header for the link named a.txt -- +612e74787400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003030303634342000303030373635200030303030323420003030303030303030303030203133343636363535303536203031353334320020316261642d6c696e6b2d746172676574000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # a.txt...............................................................................................000644..000765..000024..00000000000.13466655056.015342..1bad-link-target.................................................................................... +00757374617200303069736161637300000000000000000000000000000000000000000000000000007374616666000000000000000000000000000000000000000000000000000000303030303030200030303030303020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # .ustar.00isaacs..........................staff...........................000000..000000......................................................................................................................................................................... + +-- header for file entry which attempts to overwrite the link -- +612e7478740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000303030363434200030353737363120003030303032342000303030303030303034303120313136353133363033333320303132343531002030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # a.txt...............................................................................................000644..057761..000024..00000000401.11651360333.012451..0................................................................................................... +00757374617200303069736161637300000000000000000000000000000000000000000000000000007374616666000000000000000000000000000000000000000000000000000000303030303030200030303030303020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # .ustar.00isaacs..........................staff...........................000000..000000......................................................................................................................................................................... + +-- contents that threaten to overwrite the link target -- +61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161 # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # a............................................................................................................................................................................................................................................................... + +-- tar eof -- +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # ................................................................................................................................................................................................................................................................ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # ................................................................................................................................................................................................................................................................ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # ................................................................................................................................................................................................................................................................ +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # ................................................................................................................................................................................................................................................................
test/link-file-entry-collision/bad-link.tar+0 −0 addedtest/link-file-entry-collision.js+39 −0 added@@ -0,0 +1,39 @@ +// Set the umask, so that it works the same everywhere. +process.umask(parseInt('22', 8)) + +var tap = require("tap") + , tar = require("../tar.js") + , fs = require("fs") + , path = require("path") + , file = path.resolve(__dirname, "link-file-entry-collision/bad-link.tar") + , target = path.resolve(__dirname, "tmp/link-file-entry-collision") + , index = 0 + , fstream = require("fstream") + , mkdirp = require("mkdirp") + , rimraf = require("rimraf") + +tap.test("preclean", function (t) { + rimraf.sync(target) + t.pass("cleaned!") + t.end() +}) + +tap.test("extract test", function (t) { + var extract = tar.Extract(target) + var inp = fs.createReadStream(file) + inp.pipe(extract) + + extract.on("end", function () { + t.equal(fs.readFileSync(target + "/bad-link-target", "utf8"), + "this should remain the same\n") + t.equal(fs.readFileSync(target + "/a.txt", "utf8"), + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + t.end() + }) +}) + +tap.test("cleanup", function (t) { + rimraf.sync(target) + t.pass("cleaned!") + t.end() +})
b0c58433c22funpack: only reuse file fs entries if nlink = 1
2 files changed · +77 −2
lib/unpack.js+14 −2 modified@@ -12,6 +12,7 @@ const wc = require('./winchars.js') const ONENTRY = Symbol('onEntry') const CHECKFS = Symbol('checkFs') +const ISREUSABLE = Symbol('isReusable') const MAKEFS = Symbol('makeFs') const FILE = Symbol('file') const DIRECTORY = Symbol('directory') @@ -351,6 +352,17 @@ class Unpack extends Parser { entry.resume() } + // Check if we can reuse an existing filesystem entry safely and + // overwrite it, rather than unlinking and recreating + // Windows doesn't report a useful nlink, so we just never reuse entries + [ISREUSABLE] (entry, st) { + return entry.type === 'File' && + !this.unlink && + st.isFile() && + st.nlink <= 1 && + process.platform !== 'win32' + } + // check if a thing is there, and if so, try to clobber it [CHECKFS] (entry) { this[PEND]() @@ -360,7 +372,7 @@ class Unpack extends Parser { fs.lstat(entry.absolute, (er, st) => { if (st && (this.keep || this.newer && st.mtime > entry.mtime)) this[SKIP](entry) - else if (er || (entry.type === 'File' && !this.unlink && st.isFile())) + else if (er || this[ISREUSABLE](entry, st)) this[MAKEFS](null, entry) else if (st.isDirectory()) { if (entry.type === 'Directory') { @@ -422,7 +434,7 @@ class UnpackSync extends Unpack { const st = fs.lstatSync(entry.absolute) if (this.keep || this.newer && st.mtime > entry.mtime) return this[SKIP](entry) - else if (entry.type === 'File' && !this.unlink && st.isFile()) + else if (this[ISREUSABLE](entry, st)) return this[MAKEFS](null, entry) else { try {
test/unpack.js+63 −0 modified@@ -2286,3 +2286,66 @@ t.test('onentry option is preserved', t => { t.end() }) + +t.test('do not reuse hardlinks, only nlink=1 files', t => { + const basedir = path.resolve(unpackdir, 'hardlink-reuse') + mkdirp.sync(basedir) + t.teardown(() => rimraf.sync(basedir)) + + const now = new Date('2018-04-30T18:30:39.025Z') + + const data = makeTar([ + { + path: 'overwriteme', + type: 'File', + size: 4, + mode: 0o644, + mtime: now + }, + 'foo\n', + { + path: 'link', + linkpath: 'overwriteme', + type: 'Link', + mode: 0o644, + mtime: now + }, + { + path: 'link', + type: 'File', + size: 4, + mode: 0o644, + mtime: now + }, + 'bar\n', + '', + '' + ]) + + const checks = { + 'link': 'bar\n', + 'overwriteme': 'foo\n' + } + + const check = t => { + for (let f in checks) { + t.equal(fs.readFileSync(basedir + '/' + f, 'utf8'), checks[f], f) + t.equal(fs.statSync(basedir + '/' + f).nlink, 1, f) + } + t.end() + } + + t.test('async', t => { + const u = new Unpack({ cwd: basedir }) + u.on('close', () => check(t)) + u.end(data) + }) + + t.test('sync', t => { + const u = new UnpackSync({ cwd: basedir }) + u.end(data) + check(t) + }) + + t.end() +})
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- access.redhat.com/errata/RHSA-2019:1821ghsavendor-advisoryx_refsource_REDHATWEB
- github.com/advisories/GHSA-j44m-qm6p-hp7mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-20834ghsax_refsource_MISCADVISORY
- github.com/npm/node-tar/commit/7ecef07da6a9e72cc0c4d0c9c6a8e85b6b52395dghsax_refsource_MISCWEB
- github.com/npm/node-tar/commit/b0c58433c22f5e7fe8b1c76373f27e3f81dcd4c8ghsax_refsource_MISCWEB
- github.com/npm/node-tar/commits/v2.2.2ghsax_refsource_MISCWEB
- github.com/npm/node-tar/compare/58a8d43...a5f7779ghsax_refsource_MISCWEB
- hackerone.com/reports/344595ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.