New npm Malware Aims to Steal Cryptocurreny Secrets

Recently, Phylum's risk detection platform flagged a series of malicious npm packages. Ten "test" packages were uploaded that clearly intend to extract source code and confidential data, like environment variables. The attacker then replaced these test packages with identical code concealed inside legitimate sounding package names.

Here's the package.json file:

{
  "name": "binarium-client",
  "version": "4.0.0",
  "private": false,
  "publishConfig": {
    "access": "public"
  },
  "description": "",
  "main": "main.js",
  "scripts": {
    "postinstall": "node preinstall.js",
    "test": "echo \\"Error: no test specified\\" && exit 1"
  },
  "author": "lexi2",
  "license": "ISC",
  "dependencies": {
    "archiver": "^5.3.1",
    "ftp": "^0.3.10"
  }
}

The interesting bits are the postinstall and dependencies section. After installation, it executes a preinstall.js file. Additionally, the package requires archiver, for creating .zip files, and ftp for uploading and downloading from an ftp server.

The preinstall.js file contains code that should raise a few eyebrows by security minded devs.

const { spawn } = require('child_process');

const child = spawn('node', ['index.js'], {
  detached: true,
  stdio: 'ignore',
});

child.unref();

In short, it:

  1. Spawns a child node process running index.js
  2. Detaches that process from the parent node process
  3. Suppresses any output from the process, so you don't know it's running

The index.js source can be found here and is a few hundred lines long. But the TLDR is that it searches through certain directories for files that are likely to have source code and secrets, zips them up and uploads them via ftp to a remote server controlled by the attacker.

Attacks using npm packages are particularly difficult to sniff out because of the dependency tree complexity in the whole ecosystem. A malicious package could be 6 or more layers deep and you'd never know.

Frankly, it's an impossible problem to solve. But that's how security is. Nothing is 100% secure, but instead you strive for constant improvement. Though the JavaScript ecosystem does seem to be particularly good at giving bad advice.