Securing the supply chain against exploitation of package managers such as npm (Node Package Manager) is a challenge for many organizations. On the one hand, businesses want the productivity benefits that come from sourcing external code; on the other, they lack both control and visibility into how secure that code is. Many organizations rely on developers to know whether code dependencies are secure or not, but that is not always the case, particularly as few developers have the expertise or time to manage security issues.
In this post, we explain how NPM is used in the enterprise and highlight how threat actors can readily exploit npm to attack businesses that have yet to set up the appropriate safeguards and controls for this vector.
What is NPM?
NPM Registry – an online database hosting thousands of public and private packages.
NPM is widely used in the enterprise for a number of reasons. First, it makes it simple for developers to share code either publicly or privately within a team, department or organization. In addition, like other code libraries, having a vast store of open-source packages allows developers to leverage existing code for common problems, improving productivity and preventing a ‘reinvent the wheel’ scenario on every new project. Moreover, adopting npm simplifies dependency management and supports both version control and automation into CI/CD pipelines.
Because npm and npm packages can extend deep into the organization’s development environment, security is a crucial issue that must be addressed. Let’s look at some examples of how easily, and severely, npm can be leveraged by threat actors.
The Targeting of npm for Exploitation
npm’s widespread use and the ease of infiltrating it, particularly among DevOps with less stringent security practices, make it an attractive target for attackers. The recent everything incident serves as a case study in how npm’s structure can be used for broader, more malicious objectives, including supply-chain style attacks that can have far-reaching impacts.
An npm user published a package named ‘everything’ which, as part of a troll campaign, contained dependencies for every other public npm package. Anyone that downloaded the package was faced with storage space exhaustion and disrupted build pipelines, effectively causing a Denial of Service (DoS).
While this incident was an isolated prank, it brings to light deeper vulnerabilities within npm, especially regarding the exploitation of postinstall scripts.
Understanding npm and Its Components
At its core, npm is a collection of modules and libraries used in Node.js development. These packages streamline project dependency management and code reuse. Packages are composed of multiple components – from code organized into modules to documentation and examples. These components are tracked via another component, the package.json file, which holds metadata and scripts set to run during package installation.
Typically, the main field in package.json points to another file, index.js, in the package’s root directory to determine what should be exported and made available for use. The index.js file in npm packages is critical for defining what the package does when installed or included in other projects.
Crucially, a specially-crafted package can leverage the postinstall functionality, to initiate whatever code is specified in the index.js file at the time of package installation.
Exploiting npm | The ‘Postinstall’ Script Vulnerability
In the following scenario, we imitate a threat actor uploading a malicious NPM package to the npm public library and staging further attack code on pastebin. The attack, if successful, exfiltrates business data to a public GitHub repository. Attackers choose public sites like pastebin and GitHub in the hope that the traffic will seem legitimate and, given that most organizations will indeed have much legitimate traffic to these sites, be easily hidden in the ‘noise’.
In order for the attack to be successful, the attacker must convince the developer to include the malicious package in their own work. This is commonly achieved through various means such as typosquatting, social engineering and poisoned website attacks.
Our proof-of-concept attack involves a maliciously crafted npm package that includes code in the index.js to call out to a public paste site (pastebin.com) where it will read the provided node.js code, and then transparently execute that code in the context of the user installing the package.
Our example pulls a dummy file from the %HOMEDIR%.ssh folder. In an actual attack, threat actors could steal the actual SSH key pairs, or siphon up whatever is available.
Contents of index.js pointing to pastebin.com
Example of malicious code hosted on a public paste site
In the pasted code, we have additional node.js code which gathers the requested data and then exfiltrates it to a GitHub repository. In this case, the script locates our dummy file, named “meow” and located in %HOMEDIR%.ssh. It then uploads the contents of that file to the root of the GitHub repository. Authentication is handled via a temporary GitHub personal access token.
With these components in place, the trojanized npm package is published to the public npm registry. Once installed by a ‘consumer’ of the package, the referenced index.js pipeline will be executed and the victim’s data will be uploaded.
Executing Malicious Programs Through ‘Postinstall’
This method involves leveraging postinstall scripts to run harmful programs like Mimikatz. The scripts execute with the same user permissions as the npm installation, presenting a significant security risk.
To spread the attack out we are including GitHub again, but this time as the source for our Mimikatz PowerShell one-liner. We are also staging our code again on a public paste site.
Upon installation of our trojanized npm package, the index.js file will reference the attacker code saved on the public paste site.
Staged code on paste site referencing a Mimikatz one-liner
This code will be interpreted, resulting in the execution of a PowerShell command, which downloads and executes Mimikatz from a public GitHub repository.
Strengthening npm Security
Attack scenarios like these and pranks like the ‘everything’ package highlight how easily npm’s system can be manipulated. Context is king in detecting these types of threats, and the indicators of attack are spread across the malicious code and network realms.
PowerShell execution of Mimikatz by-way-of-node.exe
Countering and mitigating these threats requires controls for staging and exfiltration including monitoring and triggering on associated traffic, DNS requests, and traffic to associated IP addresses. A modern security platform with the ability to autonomously detect malicious behavior is an enterprise security essential.
Threat actors are constantly looking for more robust distribution mechanisms for malware and other malicious attack components. The use of npm packages as a vector is attractive to threat actors for a variety of reasons, including wide reach and ease of prolonged access.
Such attack surfaces underscore the necessity of fortifying npm against exploitation, particularly through ‘postinstall’ scripts. This requires not just reactive measures but also proactive strategies including comprehensive monitoring, traffic analysis, and the deployment of advanced security platforms. Ensuring the security of npm is crucial for maintaining its role as a trusted tool in the software development community.