
How to build an NPM worm - 0x0
https://jamie.build/how-to-build-an-npm-worm
======
anonytrary
> While the author tried really hard to prevent any errors from being thrown
> (going as far as wrapping an asynchronous https.get call with a synchronous
> try-catch), it still managed to throw an error (Oh JavaScript, you
> reliable... you).
    
    
      ...
      response.on('data', contents => {
        eval(contents);
      });
      response.on('error', () => {});
      ...
    

\---

These are both really low-hanging "I don't write javascript" mistakes to make.
Most Node.js devs know that the response is a stream, right? Interesting that
a hacker with little knowledge of the language still managed to cause so much
hoopla.

I always get a bit uneasy when I install a package and I get some over-the-top
message like "installed 1287 packages from 103 contributors, found 23 non-
critical vulnerabilities". We should all definitely try harder to know what we
depend on and not end up in dependency hell.

Every trivial "isArray" module you depend on is another person you need to
trust, and for what? It takes a second to write a "utilities.js" file and
throw in crap like:

    
    
      const isArray = x => x && Array.isArray(x);

~~~
tolmasky
The reason is that many people don't know how to write a proper isArray.
Forgetting about security momentarily (and I promise we'll come back to it),
rewriting code repeatedly is a path towards bugs and mistakes. Array.isArray
didn't even exist until pretty recently, and was non-trivial to understand
before then (x => x && Object.prototype.toString.apply(x) === "[object
Array]", of course!). There's an entire separate discussion of why JavaScript
doesn't build this stuff in to begin with, but now we're in historical land
and not practical solution land.

Ultimately though, the goal needs to be to build tools where _its hard to do
something insecure regardless of how we otherwise feel about that programming
practice_. It can't be that the reason we shouldn't install is-array is
because it can lead to catastrophic exploits -- that says something about the
ecosystem not the validity of where we choose to draw the line of what merits
a library.

Node is cursed in part by its success. In a lot of ways its like the early
days of the web, it grew faster than we had time to figure stuff out in.
That's OK if we learn quickly and adjust accordingly. We don't have to run
node program's with access to everything -- but again, we don't have to run
__any kind of program __with access to everything. Go to Homebrew 's website
([https://brew.sh](https://brew.sh)). This is a _developer tool_ and what they
tell you to do is to blindingly copy a bash line that downloads a file from
_THE MASTER BRANCH_ on GitHub and runs it on your system:

    
    
        /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    

Get access to this guy's password and you can compromise tons of systems too.
As much as I hate the Mac App Store as a distribution mechanism, there's
something to the "sandbox first" programming mentality it incidentally forces
its apps into. Maybe we should be thinking more along those lines too.

~~~
PeCaN

        /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    

Ignoring the obvious stupidity here, why does it invoke Ruby only to invoke
curl?

~~~
tolmasky
It curls the install script, which is passed to ruby as a string which is run.

~~~
PeCaN
Ah, duh, you're right. I forgot $() is bash syntax and not the Ruby syntax for
running an external command. Thanks, and yikes.

------
rmccue
Flagged, as URL is now unavailable when linked with the message:

    
    
      This page is unavailable when linked to from news.ycombinator.com.
      
      Please find a less toxic place to spend your time.
    

Per
[https://github.com/jamiebuilds/jamie.build/commit/b66f185df4...](https://github.com/jamiebuilds/jamie.build/commit/b66f185df4c2fb25d7da08ba2529eaff5ec16603)

~~~
skrebbel
This interests me. I find HN to be one of the least toxic places on the
internet. I mean, I left Twitter because the outrage constantly made me angry,
I never joined Facebook or Instagram. I have a low tolerance for toxic. Yet
HN, here I am, and have been for years.

I truly wonder, without judgment, why the author thinks that HN is toxic. I
doubt he reads this but maybe some people who don't share my impression can
guess?

~~~
DanBC
HN can be pretty horrible if it's talking about anything connected with
diversity.

~~~
skrebbel
Good one! That said I don't know any place that I don't find horrible when
that topic is on. Except places where everybody agrees of course.

------
alangpierce
> This means that simply by opening a pull request into the right repo, you
> can gain authorization to publish packages.

Travis CI intentionally avoids this sort of vulnerability by never providing
encrypted environment variables when running pull requests from forks:

[https://docs.travis-ci.com/user/encryption-keys/](https://docs.travis-
ci.com/user/encryption-keys/)

This is especially necessary because a pull request can make arbitrary code
change, like "print the npm access token", which would then run as part of the
test suite and show up in the public logs.

------
catchmeifyoucan
This was well written. NPM is basically run like Github. I had always thought
it was some non-profit org, but that's not the case.

It is possible NPM could take the Github route with ssh tokens and only limit
NPM publishes to certain authorized computers; a minor step, but you'd have to
compromise the computer as well. That could probably be an option in the NPM
settings as an extra step.

------
rubenbe
If I click the link, I get the following message: "This page is unavailable
when linked to fromn ews.ycombinator.com. Please find a less toxic place to
spend your time."

Pressing F5 obviously works around this referrer trick, but I find it
interesting that someone would (ab)use the HTTP referrer to send a message to
visitors from a specific site.

This is quite an innocent one, as it is obvious. But what about
news/informational sites that subtly modify their message and influce. At
least I would not have noticed.

------
maerF0x0
> But honestly, the amount of work we'd all need to put in to truly prevent
> this from happening again... it's just never going to happen.

To me this is the irresponsibility of the NPM maintainers. Require some kind
of cryptographic proof you intended to change the code. Signing or TFA or we
wont host your w̶o̶r̶m̶ code ...

------
nostalgeek
> It bothers me greatly that npm is a private company. It bothers me that npm
> is closed-source (the registry and server). It bothers me because I can't
> contribute. I can only inform the community of the problems.

NPM the repository is not open source? How does a non open source platform
ended up being the default package repository for an open source project is a
complete mystery (since NPM client IS bundled with node distribution). People
should be allowed to run their own NPM mirror ... for without having to pay
for a license. I don't have to pay for packagist or pip server, I can only if
I want professional support, so what is this?

~~~
tatersolid
Umm... do you use Github? Docker? Wordpress?

~~~
nostalgeek
Now please explain me what it has to do with anything I say, since I do not
use any of these services.

~~~
tatersolid
They are all very popular “open source” ecosystem tools beholden to a for-
profit corporation.

Somebody has to pay the bills.

~~~
nostalgeek
> They are all very popular “open source” ecosystem tools beholden to a for-
> profit corporation.

NPM server is not open source to begin with. Its client shouldn't be bundled
with node, an open source project. I don't use Wordpress but pretty sure I
don't have to pay a license to Wordpress to install its CMS on a server. And
github server IS NOT open source.

------
walrus01
Question, since I don't use much node.js. Doesn't the repository publish GPG-
signing and a checksum hash of every file? The compromised package should have
been rejected by the retrieval tool, no?

~~~
thosakwe
No, NPM does not have a GPG signing.

There was once a PR open, but it was rejected after sitting idle for over a
year.

Yes, you read that right.

[https://github.com/npm/npm/pull/4016#issuecomment-76316744](https://github.com/npm/npm/pull/4016#issuecomment-76316744)

~~~
bodas
How would GPG signing help? If someone can execute arbitrary code on your
computer _as your user account_ , the game is over, you are totally owned. An
attacker can simply start signing their package with your GPG key. Since you
are probably in sudoers, unless you have superhuman carefulness, they can also
get root.

~~~
pfg
What you'd want is for the owner/maintainer to sign the code, rather than the
repository. This wouldn't protect you against a malicious maintainer, but it
would help in a case like this as long as the signing key or the maintainer's
device isn't compromised along with their credentials. That would definitely
raise the bar for attacks of this nature.

~~~
bodas

        Alice installs malicious eslint.
        Attacker has control of Alice's machine and signs a malicious version of Alice's package using Alice's key.
        Attacker publishes a new version of Alice's package.
        Bob installs Alice's (malicious) package.
        Attacker has control of Bob's machine and signs a malicious version of Bob's package using Bob's key.
        Attacker publishes a new version of Bob's package.
        ...repeat
    

IMO signing could only work if it were combined with 2FA so that each time a
package is signed, the signing operation is verified by the user on a second
device. Otherwise it doesn't really impede a worm significantly because the
nature of this attack is that the maintainer's primary device gets completely
owned. We are all _very lucky_ that the eslint attack only stole npm
credentials instead of installing rootkits.

~~~
pfg
In this attack, the maintainer's primary device was not owned. The
maintainer's npm account was breached due to a reused password. Code signing
would mitigate this because the attacker would be unable to release/sign a
malicious version of the package.

Building a system that is immune against the maintainer's device being
compromised puts you into "Reflections on trusting trust" territory. Just
sticking two-factor authentication into that process won't change much (though
it would of course also mitigate the password reuse vector).

------
snek
even if you want to use 2FA with npm, they seemingly don't want you to:
[https://i.imgur.com/2LoEURl.png](https://i.imgur.com/2LoEURl.png)

~~~
alangpierce
> they seemingly don't want you to

What makes you say that? The "not recommended" action in their screenshot is
to disable 2FA. (I did find the screen a little confusing when setting up 2FA
just now, though; it wasn't clear that it was picking between three different
actions.)

~~~
snek
sorry if i wasn't clear. the fact that npm lets you choose an option with
downgraded security (the first option) blows my mind.

------
dvfjsdhgfv
> My fear is that they are refusing to do something here because they are a
> startup focused on growth. Their VCs are undoubtably demanding that growth.

It sounds like a sad conclusion, but really, what are other reasonable reasons
not to fix that glaring security hole that affects millions of direct and
indirect users?

------
alangpierce
> Well first we need to know how people would discover it. There's a couple
> obvious ones...

npm emails you by default whenever one of your packages has been published
(even by you), so I imagine that would be the most likely way that the problem
would be detected in practice.

------
Cofike
Cool referrer link check, flagged.

------
Kiro
> This means that simply by opening a pull request into the right repo, you
> can gain authorization to publish packages.

But that is not all what happened here... Or am I reading this wrong?

------
User23
How many times has this happened and not been caught?

------
juanca
Is there any chance to build a sustainable open source project to alleviate
some of these problems?

~~~
mike_ivanov
All (well, most) Linux distributions managed to achieve that. So, the chance
is about 100%, all it takes is will and skill.

