12 weird things that can happen after installing the npm package
A couple of months ago, I started a project called malicious packages (aka "malicious packages"). It monitors for updates in the npm repository, downloads all new modules, and then checks them for lice - searches for network activity, suspicious operations with the file system, etc. Even small projects on node.js often have a large dependency tree, and developers are physically unable to check them all. This gives the attackers a huge room for maneuver, and the question arises - how much nastiness hiding in the dark corners of the npm registry? 180,000 checked packages later I received a rough answer.
And this answer is probably not so much.
[note: on medium there is an English version of this article, also under my authorship]
What can npm package do with your system?
The package has two main ways to harm you - when installing / uninstalling and when launching your application. Let's look at both options with examples.
NPM scripts allow packages to execute arbitrary commands at the time of installation and removal. These include preinstall
, install
, postinstall
, preuninstall
and postuninstall
, which is automatically executed at the appropriate time npm package lifecycle. What can they do? All the same that your current user can do - for example, delete all your photos from the last vacation, or merge the history of your browser in the FBI (although, most likely, they already have it). This behavior can be disabled by passing the flag --ignore-scripts
, but, firstly, no one does, and secondly, in this way it is possible to break a bunch of quite trustworthy packages. It was through the scripts that the sensational attack on ESLint was carried out , which affected the users of eslint-scope(6 million installations per week) and eslint-config-eslint (2 thousand installations per week).
The package gets a second chance to make life difficult for you at initialization (usually occurs during the first call require
). Now he has the opportunity to modify global variables and other packages, for example, to steal a private key from your bitcoin wallet, or to make the method crypto.randomBytes
not so random .
How many malicious packages were detected?
Well, the list cannot be called impressive, in total 3 packages were found, which to date have been removed from the npm repository through the efforts of the npm security team . Let's go over it:
- commander-js tries to disguise itself as a real one
commander.js
(which is https://www.npmjs.com/package/commander ), but it contains one unusual detail, namely thepostinstall
script that downloads and executes the contents of http://23.94.46.191/update.json ( at the time of publication does not contain anything criminal). Security advisory: https://www.npmjs.com/advisories/763 . - rrgod through all the same scripts loads and executes the script from http://static.ricterz.me , which, in turn, tries to load another script that is currently unavailable. Security advisory: https://www.npmjs.com/advisories/764 .
- portionfatty12 cannot be called completely malicious, but the way in which the author collects statistics about his child’s installations is very dubious - sending your public ssh-key (
~/.ssh/id_rsa.pub
) to a server that is currently unavailable. Security advisory: https://www.npmjs.com/advisories/765 .
Despite the very modest results, in the process of analyzing all the suspicious packages (and I looked at more than 3000 reports to find these three pearls), a lot of funny and not-so-many things were found that you usually don’t think about (or try not to think carefully) npm install
. So let's imagine that you randomly selected one of the many packages from the repository and install it. What can go wrong?
1. A package can override methods in other packages (including those from the standard Node.js delivery)
However, if you have read a couple of previous paragraphs or have been working with the javascript ecosystem for more than a week, then this is unlikely to be news to you. But the scale of this disaster could easily slip away from you. So, if your project has at least a few dependencies, then most likely the fs.closeSync method is already redefined (and, perhaps, more than once). A huge number of packages modifies someone else's API, but only a few of them have any valid reason for this. Among the champions are graceful-fs with 12 million installations per week, which overrides a dozen methods from fs . It is also worth noting async-listener , redefining 46 different methods , including the ill-fatedcrypto.randomBytes
that strained me a little when I first discovered it.
Imagine what it would look for a bug caused by such a redefinition from any dependency in the depths of the hierarchy. However, there is no reason to worry, because ...
2. A package may define a modified API to make additional corrections to it.
Yes, some packages do this (most often with respect to the same graceful-fs
) using acrobatic miracles like /graceful-fs/.test(fs.closeSync.toString())
. So, if you suddenly encounter incomprehensible problems in the standard library, try simply installing a couple of random npm packages. Or just turn off the computer and walk through the nearest park, life is too short to understand all this.
3. He can send analytics
Adblock will not protect you if it happens in the console, and the authors of some packages successfully use it. Some send the most basic information, such as ecdsa-csr :
// POST https://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="
}
Some are not so shy. Here, for example, part of the serverless report (original 2 times more):
// 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
}
}
}
Fortunately, jquery
no statistics are sent, so it can still be installed in secret from everyone. For the time being.
4. Your computer can be used instead of the CI / CD server.
Why do you need to compile your package if your users can do it during installation ? You can get additional progress if you specify only the major version of the required compiler, for example typescript@3
. One hope for literate guys from Microsoft who can do the right semver.
Can I go even further? Of course !
"postinstall": "eslint --ext .js,.vue --fix src"
Now you can sleep peacefully - all users will receive the perfectly formatted source code of your package.
5. He may try to scare you
If you watched all the series Mr. Robot, but still not motivated enough to read Computer Networks and do something really impressive, that is, a simple solution - show your skills to the world with a couple of lines in the postinstall
script. This is exactly what the author of pizza-pasta did (and at the same time gave me a CDRV).
{
"name": "pizza-pasta",
"author": "Zeavo",
"scripts": {
"install": "mkdir -p ~/Desktop/hacked && touch ~/Desktop/hacked/pwnddddd && wget https://imgur.com/download/KTDNt5I -P ~/Desktop/hacked/",
"postinstall": "find ~/.ssh | xargs cat || true && printf '\n\n\n\n\n\nOH HEY LOOK SSH KEYS\n\n\n\nHappy Birthday! Youve been h4ck0red\n\n\n'"
}
}
6. Package can load and execute bash scripts.
Have you heard what to do is curl|bash
not a good idea ? If not, then you may well be an ORESoftware employee who has a whole bunch of packages with similar lines in the postinstall
script:
curl --silent -o- https://raw.githubusercontent.com/oresoftware/realpath/master/assets/install.sh | bash
So far there is nothing criminal in this script ... Bye. I hope that everyone who has access master
to oresoftware/realpath
exceptionally honest and decent people.
7. You may be asked for a password
The author of magicleap came up with a rather unusual way to distribute a private package through a public repository. Its project consists of an encrypted archive and utilities for decrypting it - but only if you put the correct key in the environment variable MAGICLEAP
:
// Оригинал: https://github.com/modulesio/magicleap/blob/master/decrypt.jsvar key = process.env['MAGICLEAP'];
console.warn('Decrypting magicleap module with MAGICLEAP environment variable');
const ws = fs.createReadStream(path.join(__dirname, 'lib.zip.enc'))
.pipe(crypto.createDecipher('aes-256-cbc', Buffer.from(key, 'base64')))
.pipe(fs.createWriteStream(path.join(__dirname, 'lib.zip')));
8. Package can patch itself during installation.
The author of the fake-template does not have time to separate the tests from the direct code, especially since it is easy to do by adding a special comment to the end of the test lines:
// Оригинал: https://github.com/framp/fake-template/blob/master/index.jsconst template = (string, tag=defaultTag) => {
if (mode !== 'literal') thrownewError('Invalid template')
return(context={}) => tag(literals, ...expressions.map(evalInContext(context)))
}
assert.equal(template('')(), ``) // TEST
assert.equal(template('abc')(), `abc`) // TESTconst dog = 'Orlando'// TEST
assert.equal(template('abc ${dog} lol ${cat}')({dog}), `abc ${dog} lol ${cat}`) // TEST
And then remove them after sed
:
"postinstall": "sed -i '/\\/\\/ TEST/d' index.js"
Simple and elegant!
9. Package can change settings npm
In my humble opinion, this package-json.lock
is a great thing that can save you from a whole heap of problems caused by the negligence of other developers. However, some opponents of this idea have rather strong arguments against:
"preinstall": "npm config set package-lock false"
10. He can change the background of your desktop on the photo of Nicolas Cage
Here is the link, just in case - https://www.npmjs.com/package/cage-js . Perhaps it was worth reporting this package as malicious.
11. A package can hook you up.
This is what ember-data-react is doing , revealing the famous video during installation. Unfortunately, no data from Ember to React can be transferred with its help - there is not a single line of javascript code in it.
12. It may simply not be installed.
Non-existing dependencies, incorrectly specified versions, private repositories that have sunk into oblivion — you cannot install about 0.6% of all packages from the npm repository.
Instead of conclusion
NPM packages may do strange things with your system, and you do not have many options to protect against this. Use package-lock.json
to avoid sudden updates (and make sure that no one disconnects it without your knowledge), configure the CSP on the front end, so that the backdoor in the third-party module at least cannot merge the data to its author. And make a backup of your photos, just in case.
If you feel strong enough to dive into the wonderful world of npm packages, you can find all the sources here: https://github.com/malicious-packages/core . The utility is full of hacks and sub-optimal solutions, but it copes well with its task. Also in the repository there is a MongoDB dump with the results of the analysis of more than 180,000 packages, most importantly, do not forget to add a filter {'reports.status': 'unverified'}
. I do not plan to develop this project anymore due to lack of time, but I will try to help with all the questions and problems, if any.
Take care of yourself and your applications!