12 strange things that can happen after installing an NPM package

Vladimir T
8 min readJan 14, 2019

A few months ago I started a pet project called malicious-packages with a rather self-explanatory name. It continuously downloads new packages, published to the npm registry, and analyzes them to find packages with suspicious activity, such as network requests, unusual filesystem operations and so on. Even small nodejs projects have thousands of dependencies, making it virtually impossible for a developer to monitor them all. That makes you wonder — how many evil lines of code are hiding in the dark corners of the npm registry? Now, after checking more than 180 000 npm packages, I’d like to think I know the answer.

The worst birthday ever

And the answer is — well, probably not so many as I’d expect.

What can npm package do to your system?

NPM package has two main ways to harm you — when you install it and when you actually use it in your application. Let’s take a brief look at some practical examples here.

Npm scripts allow for any package to execute an arbitrary piece of code when a package is installed or uninstalled. preinstall, install and postinstall scripts are automatically executed by npm when you install a package, preuninstall and postuninstall — when you remove it. You can disable this behavior by passing ‑‑ignore-scripts option, but let’s be real for a moment here — no one actually does that. What can the package do, using these scripts? Well, pretty much everything your current user can do. For instance, it is capable of removing all the photos from your last vacation or sending your browser history to the FBI (like they don’t have it yet) or your crush (which might be much more awkward). On July 12th, 2018, a hacker compromised the npm account of an ESLint maintainer and published malicious versions of the eslint-scope (6 million installs per week) and eslint-config-eslint (2 thousand installs per week) which extracts the _authToken from a user’s .npmrc and sends it to the remote server.

A package gets another chance to make your life harder when you require it. Now it can not only execute arbitrary code, but also modify your runtime environment — it can steal private keys from your bitcoin wallet or make crypto.randomBytes method not so random.

https://xkcd.com/221/

So how many malicious packages were found?

Well, not many really… three to be precise. They are all removed from the registry, thanks to the npm security team ❤️. Here they are:

While reviewing the suspicious reports (I’ve inspected about 3000 of them to locate those 3 gems) I’ve found a lot of things you usually don’t know or care when you run npm install. Now, let’s imagine that you downloaded a random package from registry — what might happen next?

1. A package can modify some of Node.js standard packages on initialization

Actually, if you have more than one dependency, it’s quite likely that the poor fs.closeSync is overridden by something. Maybe a couple of times. Hopefully less than 10 times. There are thousands of packages that modify the standard library — but barely any, which actually do have good reasons to do so. In 80% of the cases the blame lies on graceful-fs , which has more than 11 million installs per week and patches a bunch of fs methods. Another popular one is async-listener, responsible for the changes in 46 standard methods, including crypto.randomBytes, which made me quite nervous when I first discovered it.

Imagine that one of those packages has a bug, potentially breaking the standard library. It’d be a real pain to debug and find the actual cause of such a bug, wouldn’t it? But don’t you worry, because…

2. It can detect modifications in the standard library and apply additional patches to them

We need to patch a patch

Yeah, there are some packages which detect usage of graceful-fs, using some marvelous tricks like /graceful-fs/.test(e.closeSync.toString()), and change their behavior accordingly. Therefore, if you have some mysterious troubles with the standard library, try installing some random packages from npm. Or remove some of the already installed ones. Or just close your laptop and go for a walk, life is too short to deal with this mess.

3. It can send some information about your machine to a third-party server

I can’t say if analytics is a bad or a good thing. All I can say is that some package authors already have an answer to that question.

Some of them send only very basic information about your environment, such as ecdsa-csr:

// POST api.therootcompany.com/api/therootcompany.com/public/ping
{
"package":"ecdsa-csr",
"version":"1.1.1",
"node":"v10.14.2",
"arch":"x64",
"platform":"linux",
"release":"4.9.125-linuxkit",
"action":"install",
"ppid":"eDSeYr9XUNRi9WhWli5smBNAvdw="
}

Others send more detailed reports. Here’s one from serverless (slightly reduced):

// POST https://tracking.serverlessteam.com/v1/track
{
"userId":"0e32cba0-14ef-11e9-9f89-b7ed4ca5dbba",
"event":"framework_stat",
"properties":{
"version":2,
"general":{
"userId":"0e32cba0-14ef-11e9-9f89-b7ed4ca5dbba",
"context":"install",
"timestamp":1547135257977,
"timezone":"GMT+0000",
"operatingSystem":"linux",
"userAgent":"cli",
"serverlessVersion":"1.35.1",
"nodeJsVersion":"v10.14.2",
"isDockerContainer":true,
"isCISystem":false,
"ciSystem":null
}
}
}

4. It can use your computer as a CI/CD server

You won’t need to spend your precious time to compile the library yourself if you put all compilation instructions to postinstall script and make every user compile your package over and over. You can receive some bonus points if you pin only the major version of a compiler, such as typescript@3. Hopefully, Microsoft knows how to do a proper semver.

But can we go even further? Of course, we can:

Because it’s very important to make sure that every user will get the properly linted version of our package!

5. It can try to scare you

When you read about postinstall scripts

Have you watched all seasons of Mr. Robot twice? Are you too lazy to read Computer Networks By Andrew S. Tanenbaum and do some legit hacking? That’s fine because you can easily demonstrate your mad skillz using a simple postinstall script, like the author of pizza-pasta package (now removed from npm):

6. It can download and execute bash scripts

Do you know that it’s a really bad idea to pipe to your shell? If no, chances are, that you are an employee of ORESoftware, which has a lot of packages with the following line in the postinstall script:

curl --silent -o- https://raw.githubusercontent.com/oresoftware/realpath/master/assets/install.sh | bash

This script doesn’t contain any malicious code… yet. My best hopes are that everyone with write access in that repo are decent people with good intentions.

7. It can ask you for a password

Author of magicleap found an interesting way to store a private package inside a public repository. His package consists of an encrypted file and tools to decrypt it — but only if you put the right key in MAGICLEAP variable.

8. It can modify itself during an installation

The creator of fake-template can’t be bothered to separate tests from the actual code, so he just added comments to the end of all lines which aren’t needed in production:

And then removed them in postinstall script:

"postinstall": "sed -i '/\\/\\/ TEST/d' index.js"

Simple and elegant solution!

9. It can change your npm settings

I think that package-lock.json is one of the best things that have happened to npm recently. But the author of ontimize-web-ngx-tree has a different opinion, and his arguments are pretty convincing:

"preinstall": "npm config set package-lock false"

10. It can change your wallpaper to a photo of Nicolas Cage

His Majesty

Here’s the link if you need it — https://www.npmjs.com/package/cage-js. Not sure if this package should be considered malicious?

11. It can rickroll you

ember-data-react opens a famous video during the installation. Sadly, you won’t be able to bring any of your Ember data to React using it because it has no other functionality.

12. It can fail during installation

Don’t want it anyway

Non-existent packages as dependencies, wrong versions, vanished private repositories — you simply can’t install about 0.6% of all npm packages.

The (lack of) conclusion

NPM packages sometimes do various weird things, and there is not much you can do about it. Use package-lock.json (and don’t install ontimize-web-ngx-tree) to prevent unexpected updates, setup CSP on front-end to prevent unwanted network activity from the third-party modules and don’t forget to backup your photos, just in case.

If you are interested in exploring the wonderful world of wild npm packages, you can get the code here: https://github.com/malicious-packages/core. It’s suboptimal and full of hacks, but it does its job. You can also find the latest mongo dump in the repo, just don’t forget to add {‘reports.status’: ‘unverified’} filter or it will take ages to scroll to the end of the list. I won’t have time to support this project in the near future, but you are free to fork and do whatever you please with it. Don’t hesitate to ask me questions in the comments or GitHub issues.

Stay safe!

This story is published in Noteworthy, where 10,000+ readers come every day to learn about the people & ideas shaping the products we love.

Follow our publication to see more product & design stories featured by the Journal team.

--

--