typosquatting attacks

What is typosquatting and how typosquatting attacks are responsible for malicious modules in npm

You may have heard about malicious packages in a variety of contexts, such as a malicious Docker container or perhaps an open source malicious package in a public registry of one ecosystem or another. Snyk even published extensive research on malware in mobile applications, dubbed SourMint.

We reviewed some of these security incidents in the past, and they encompass more than just one ecosystem:

However, not all malicious packages are the same in nature and with regard to ecosystem registries, we can broadly classify them into one of the following:

  • Typosquatting attacks – bad actors pushing malicious packages to a registry with the hope of tricking users into installing them. We’ve seen these types of packages in both the PyPI and Node.js registries, with the most noteworthy of them being crossenv. It took the similar name of the popular package cross-env and had wrapped the same functionality except it also captured environment variables and sent them to an attacker controller remote server.
  • Compromised accounts – open source project maintainers may not realistically have a better security grasp than other developers, which may result in unfortunate and insecure handling of secrets related to their publishing rights.
    Things get even more complicated when a project has several maintainers and project policies need to be enforced for all members. Another reason for compromised accounts could also be attributed to a lack of proper authentication controls in some registries.
    Nevertheless, compromised accounts can lead to severe security incidents due to the potentially large popularity of projects maintained by these accounts and their immediate effect on that ecosystem.
  • Social Engineering in Lieu of the Dependency Graph – open source embraces and welcomes collaboration wholeheartedly. However, while a malicious line of code in a pull request will probably alarm many eyes very quickly, it is a different story if a malicious line of code is added to a dependency of a project.

Typosquatting attacks

Out of the above-mentioned types of malicious packages and versions published to repositories, typosquatting is one of the most common because the “barrier of entry” for attackers is very low and there aren’t many countermeasures against it.

What are typosquatting attacks?

Typosquatting attacks take place when bad actors push malicious packages to a registry with the hope of tricking users into installing them. Public software registries, such as npm or PyPI, are examples of ecosystems where we’ve witnessed such attempts happening already.

This is very similar to website phishing attacks that exploit typos made by individuals who may accidentally type-in a wrong address, such as typing in https://bankofamerca.com instead of https://bankofamerica.com. If the former domain address was in control of an attacker, they could fake a website appearance in order to masquerade as the real one and steal sensitive financial information such as credentials, bank accounts, and so on.

Examples of typosquatting malicious modules found in Snyk’s vulnerability database, going back as early as 2017 with a stream of vulnerabilities:

Typosquatting attacks on the npm ecosystem as found on the Snyk vulnerabilities database

And if you think typosquatting is dead in 2020, think again:

Malicious packages on the npm ecosystem as found on the Snyk vulnerabilities database

The story of crossenv

One particularly noteworthy case of typosquatting attack is crossenv. The attacker used a similar name to the popular package cross-env and had even wrapped the exact same functionality of the original module in order to convey that the module is indeed working as expected, only in practice it also captured environment variables and sent them to an attacker controller remote server.

On July 19th, the npm user hacktask published crossenv as one of over 30 other malicious packages used to steal environment variables from victims who had been tricked into installing them.

As C J Silverio shared in his blog, here’s the full list of packages along with their total downloads count for the length of time that they existed on the public npm registry:

babelcli: 42
cross-env.js: 43
crossenv: 679
d3.js: 72
fabric-js: 46
ffmepg: 44
gruntcli: 67
http-proxy.js: 41
jquery.js: 136
jquery.js: 136
mariadb: 92
mongose: 196
mssql-node: 46
mssql.js: 48
mysqljs: 77
node-fabric: 87
node-opencv: 94
node-opensl: 40
node-openssl: 29
node-sqlite: 61
node-tkinter: 39
nodecaffe: 40
nodefabric: 44
nodeffmpeg: 39
nodemailer-js: 40
nodemailer.js: 39
nodemssql: 44
noderequest: 40
nodesass: 66
nodesqlite: 45
opencv.js: 40
openssl.js: 43
proxy.js: 43
shadowsock: 40
smb: 40
sqlite.js: 48
sqliter: 45
sqlserver: 50
tkinter: 45

What does a malicious package like crossenv look like?

To explore the case of the crossenv malicious package, we’ll begin with the package.json file:

Let’s take note of several things that look out of order just by examining the package.json file:

  • Line 2: the name of the package is obviously incorrect, but let’s assume it went unnoticed.
  • Line 13: bundles the real cross-env package, which as you can see, also carries a different version (5.0.1) from that of the module itself (6.1.1 on line 3)
  • Line 8: suspicious-radar should kick in here. What’s in package-setup.js and what kind of setup is necessary for this package?

Just a moment before we dive into the whole story behind node package-setup.js, let’s take a step back and explain what makes that line so important. The run-script referred to as postinstall is one of npm’s built-in package lifecycle hooks which gets executed automatically when a package is installed.

Any command value in a postinstall run-script will get executed by an npm install task, regardless of whether you have required the script from your own code or not.

Act 1: the malicious payload for the npm package

As we further proceed with our investigation and examine the contents of the package-setup.js file, a world of evilness unravels:

As this script executes part of the npm package installation process, it collects all of the environment variables using process.env converts them to a base64 encoded payload that is ready to be sent to an attacker-controlled remote server as an HTTP POST request.

The malicious act is executed once, on installation, and from that point on the crossenv package continues providing the capability for which it was originally installed, by wrapping the real cross-env package, and thus go overlooked.

Act 2: let’s reflect on malicious packages impact

Finding and shedding light on this security vulnerability is important but it is not sufficient. In order to understand the damage this could have caused, and the consequences of undergoing this attack, let’s take a moment and reflect on some questions:

  1. What information do we store in our environment variables?
  2. What would have happened if this was merged by a developer to a branch and ran on a CI server? And what would happen if this was further promoted to a production server?
  3. How would this affect you if the attack didn’t stop by just reading environment variables but instead took further malicious steps such as placing backdoors, infecting environments with self-replicating worms, and other such nightmares?

Epilog: how did we uncover this malicious package on npm?

Oscar Bolmsten, a Swedish software engineer, shared a tweet about potential malicious activity for the crossenv package. Apparently, it went unnoticed for 2 weeks:

What can we do about it?

  • Don’t store anything sensitive in environment variables.
  • Connect your repository to Snyk so we can monitor your project’s dependencies on a daily basis and alert you if we find any malicious packages or other vulnerabilities. If we find a security vulnerability that can be fixed, we’ll automatically open a pull request (or merge request) to your repository to fix it!
  • Consult the Snyk Advisor before installing packages, so you get a glimpse of the overall package health, popularity, maintenance, security, and other traits.
  • When installing packages use the npm CLI flag --ignore-scripts to avoid executing scripts from 3rd party packages during the install phase.
  • Consider using npq as a way to safely install packages by collecting package health information before you actually install.

For more npm security best practices and tips I invite you to take a peek at the npm security cheatsheet.