Blog of Daniel Ruf

binary planting and arbitrary file (over)write vulnerabilities in npm, pnpm and yarn

12.12.2019

Abstract

The Node.js and npm ecosystem are the fastest growing ones and there are many types of attacks. Most of them are probably not yet widely known as the topic security is just becoming more relevant and it was never something that most of us thought about.

In the evening of 21.10.2019 I was thinking about possible ways how malicious actors and packages could cause harm on our systems. One feature that is very powerful and often overlooked is the possibility to define binaries through package.json, including their name and the used file. Can this be used to (over)write some files or drop binaries wherever we want to?

The problem

My tests confirmed my initial assumption that all three are vulnerable to binary planting and arbitrary file (over)write attacks.

While npm and yarn are most vulnerable, pnpm seems to prevent many of the attack types as my tests concluded.

pnpm seems to not resolve the path outside of node_modules in most cases. Also as pnpm uses symlinks in general to manage the dependencies, it prevents that symlinks can be overwritten by other packages then with which they were created and are owned by them.

The problem is that we can define any (valid) paths for the binary name and the file which is then symlinked.

"bin": {
    "../some/path": "../some/other/path"
}

This is basically everything that is needed to execute these attacks. What you can do with this depends on the package manager and the result can be pretty bad.

I have reported my findings to the team at Snyk which handled all the further steps. Many thanks to the experienced Snyk team, especially Liran, Sam and Simon for helping with this and doing a coordinated responsible disclosure.

Please make sure to take a look at the detailed report by Snyk which also includes details about the vulnerable and patched versions, available patches and remediation steps and much more.

PoCs

symlink .ssh/id_rsa

"keyfile": "/Users/danielruf/.ssh/id_rsa"

This allows that a malicious application can directly access the file without using the absolute path and bypass tools which check the path that is used. This might also work as some sort of privilege bypass depending on how the package manager is installed.

(over)write a file / binary

"webpack": "./test.js"

This overwrites the webpack binary with something else. This can be used to silently replace clean binaries or files with a manipulated or malicious version. If it is not yet created it will be created.

plant a binary

"/Users/danielruf/Desktop/yyy": "./fake-file"

This generates a new file on the desktop which contains the code from the relative path of the installed package.

A relative path and simple path traversal can be also used to achieve the same:

"../../../yyy": "./fake-file"

Possible solutions

Paths should not be resolved outside of the node_modules folder to prevent most of these ways, like pnpm already does.

Also it should not be allowed to overwrite symlinks which were created by other dependencies. In some situations it is prevented but in most cases the package managers allow this.

Users also get no information that a binary is installed by default so it is not very clear what happens behind the scene.

There should be also restrictions regarding the filesystem access which would prevent many other attacks too. But this is a general problem which has to be resolved in Node.js (which deno already tries to resolve at the root).

Remediation

Update to the latest available versions of npm (v6.13.4), pnpm (v4.5.0) and yarn (v1.12.1) which fix these issues.

CVEs

Links

media

blog post (npmjs)
blog post (NodeJS)
blog post (Snyk)
article (golem.de)
article (heise online)
article (Naked Security)
article (The Register)
article (ZDNet)

npmjs advisories

Arbitrary File Write (bin-links)
Arbitrary File Write (npm)
Symlink reference outside of node_modules (bin-links)
Symlink reference outside of node_modules (npm)
Global node_modules Binary Overwrite (npm)
Global node_modules Binary Overwrite (bin-links)

Snyk vulnerability database

GitHub security advisories

Timeline

21.10.2019: first tests using private repositories on GitHub
25.11.2019: started documentation of results
25.11.2019: contacted Liran Tal of Snyk
01.12.2019: reported as vulnerability to Snyk
01.12.2019: informed german media
02.12.2019: provided further information to Snyk including PoC
06.12.2019: Snyk forwarded the details to the developers of npm and yarn
10.12.2019: npm v6.13.3 released
11.12.2019: yarn v1.21.1 released
11.12.2019: npm v6.13.4 released
12.12.2019: coordinated vulnerability disclosure
16.12.2019: pnpm v4.5.0 released


Daniel Ruf

Written by Daniel Ruf. You should follow him on Twitter