VYPR
High severityOSV Advisory· Published Apr 30, 2019· Updated Aug 5, 2024

CVE-2018-20834

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.

PackageAffected versionsPatched versions
tarnpm
>= 3.0.0, < 4.4.24.4.2
tarnpm
< 2.2.22.2.2

Affected products

2

Patches

2
7ecef07da6a9

Bump fstream to fix hardlink overwriting vulnerability

https://github.com/npm/node-tarisaacsMay 15, 2019via ghsa
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 added
  • test/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()
    +})
    
b0c58433c22f

unpack: only reuse file fs entries if nlink = 1

https://github.com/npm/node-tarisaacsApr 30, 2018via ghsa
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

News mentions

0

No linked articles in our index yet.