12 weird things that can happen after installing the npm package

Published on January 15, 2019

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.


image


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, preuninstalland 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 .


image


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:



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.randomBytesthat 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.


image


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, jqueryno 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


image


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 postinstallscript. 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 postinstallscript:


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 masterto oresoftware/realpathexceptionally 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.js
var 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.js
const template = (string, tag=defaultTag) => {
  if (mode !== 'literal') throw new Error('Invalid template')
  return (context={}) => tag(literals, ...expressions.map(evalInContext(context)))
}
assert.equal(template('')(), ``) // TEST
assert.equal(template('abc')(), `abc`) // TEST
const 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.lockis 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


image


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.


image
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.jsonto 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!