
Understand filesystem takeover vulnerabilities in NPM JavaScript package manager - lirantal
https://snyk.io/blog/understanding-filesystem-takeover-vulnerabilities-in-npm-javascript-package-manager/
======
Normal_gaussian
First:

One package can attack by declaring a malicious executable (bin) with the same
name as one from a package that you would intentionally use.

Second:

If the malicious package is installed globally, it is injected into your
regular PATH, and can similarly "hijack" regular commands.

-

At first I read tfa and said "and? Thats what I expect it to do". Then I
noted:

* for a given package installation, a dependencies dependencies bins are available. This is generally counter intuitive.

* conflicting bin entries do not generate warnings or errors.

On a personal note: \- I dont install node modules globally. Wrt development
it should stay in repo, and I'm not a fan of managing system utilities from
anywhere other than my distros package manager.. largely because its a PITA
(I'm looking at you Python). \- I cant think of any reason why non top level
dependencies should have their bin files exposed. That is madness. \- if it
was just top level files, this would be a meh - I _should_ get a conflict
warning but I (or my cocontributors) deliberately installed those packages, so
im not too bothered there.

~~~
latchkey
I personally don't ever install globally either. But I do have
'./node_modules/.bin' in my PATH because well... I'm lazy.

If two packages try to install the same filename in that directory, I would
want a full stop failure so that I can work it out myself. I'm honestly a bit
surprised this isn't already the case.

~~~
ergothus
I did that in the past, but now I can run "npx WHATEVER"

The only problem is that npx ALSO defaults to installing things, which is
ridiculous.

------
lioeters
The fact that "bin" files of globally installed dependencies can overwrite
each other silently - it's an understandable oversight, and I'll be glad if
npm/yarn notifies me in the future.

What surprised me was that the "bin" names can be a relative or absolute path,
like:

"bin": { "/usr/bin/date": "./fake-date" }

That is scary! I'm guessing these paths will be sanitized in the newest
versions of package managers, but that seems like it was a pretty big security
hole.

As others have commented, I prefer not to install things globally - mostly
locally linked packages of my own. I believe projects should not require or
recommend globally installed commands, everything it needs for building should
be in its local node_modules folder.

~~~
tomxor
> What surprised me was that the "bin" names can be a relative or absolute
> path, like:

> "bin": { "/usr/bin/date": "./fake-date" }

This will only possibly work if you install it with root privileges... at
which point you need to have a reasonable level of trust of the script you are
running. It's unreasonable to give the same level of trust to npm packages
that you do your OS package manager since anyone can publish an npm package
without anyone elses permission.

For this reason I think npm's defaults are a really shitty choice (global
prefix under root). I always change the global prefix to live in my user
directory for this reason so I can npm install -g without root:

    
    
        sudo npm config -g set prefix '~/.local'
    

You will need to add the local bin to your PATH (if it doesn't already contain
it)

    
    
        PATH="$HOME/.local/bin:$PATH"
    

The order above is important, local must come before existing PATHs for lower
precedence so that it cannot override higher privilege bins... e.g so that if
an npm package added a bin for '~/.local/bin/cat', it would never be called
implicitly since '/usr/bin/cat' exists.

~~~
ryukafalz
>The order above is important, local must come before existing PATHs for lower
precedence so that it cannot override higher privilege bins

You've got the order reversed there - the first item found in your PATH will
be used, and therefore earlier directories in your PATH have higher
precedence:

    
    
      jfred@lambdacrypt ~$ cat dir1/pathtest 
      #!/run/current-system/profile/bin/bash
      echo "script in dir1"
      jfred@lambdacrypt ~$ cat dir2/pathtest 
      #!/run/current-system/profile/bin/bash
      echo "script in dir2"
      jfred@lambdacrypt ~$ PATH=$HOME/dir1:$HOME/dir2 pathtest
      script in dir1

~~~
tomxor
You are correct my mistake.

------
johannes1234321
Why would one use such a exploit? - Using a postinstall script to do evil
stuff is way more powerful and simpler to use. And thus more dangerous. If I
want to hide something I put it in my libraries code (the user installed it,
so at some point in time will `require` my code)

Depending on code loaded means you trust it.

------
mothershipper
There seems to be a typo in the blog post -- yarn needs to be updated to
1.21.1, not 1.12.1

~~~
mothershipper
source: [https://snyk.io/vuln/SNYK-JS-YARN-537806](https://snyk.io/vuln/SNYK-
JS-YARN-537806)

