Exploring extensions of dependency confusion attacks via npm package aliasing
Nishant Jain
2021年11月4日
0 分で読めますDependency confusion attacks are a form of open source supply chain security attacks in which an attacker exploits how package managers install dependencies. In a prior post, we explored how to detect and prevent dependency confusion attacks on npm to maintain supply chain security.
In this article, we will present an extension of the dependency confusion problem utilizing npm’s package aliasing capabilities. This package aliasing capability, as provided and instructed by the npm command-line application, is a user-only way to install a package under a different alias. Per npm’s own documentation, refer to the following as an example:
1npm install my-react@npm:react
This creates the following package.json
entry:
1dependencies: {
2 “my-react”: “npm:react”
3}
The repercussions of npm package aliasing reveal themselves in the way other package-related tooling handles package manifests, and in fact, applies to the official npmjs.org registry itself. Although this attack scenario has no direct security consequences (because the aliased package would always download the version of the package specified in the npm command), we believe that it does create more room for other attack vectors.
Carrying out an npm package aliasing attack scenario
Let’s reproduce the impact of npm package aliasing attacks to demonstrate how this can result in potential dependency confusion and the installation of malicious rogue packages.
We begin by creating a package named deneuve-package-parent
that installs two different versions of the deneuve-package-test
package: versions 1.0.0 and 1.2.0. Version 1.0.0 gets installed due to it being aliased with the name of a made-up package deneuve-package-private
, thanks to npm package aliasing.
This is parsed on npmjs.com as one of the dependencies of the deneuve-package-parent
.
Consider the following package.json
as a full reference for the package dependency tree described above:
1{
2 "name": "deneuve-package-parent",
3 "version": "1.2.0-beta",
4 "description": "This is the parent package!",
5 "main": "index.js",
6 "scripts": {
7 "test": "echo \"Hello\""
8 },
9 "author": "deneuve@wearehackerone.com",
10 "license": "ISC",
11 "dependencies": {
12 "deneuve-package-test": "^1.2.0",
13 "deneuve-package-private": "npm:deneuve-package-test@1.0.0"
14 }
15}
We went ahead and published the package to npmjs. Then we browsed over to the list of dependencies.
As you can see, the list of dependencies on the npmjs registry actually lists the made-up alias deneuve-package-private
as one of the dependencies' names:
The above screenshot shows a dependency named deneuve-package-private
, which we created as an alias in our package.json
, and not as a real dependency. This alias (being used as a package name) doesn't actually exist on the npmjs registry — unless someone publishes it! And that's where supply chain security concerns kick in.
This then begs the question: what if someone would find such aliased packages and then publish them as malicious packages to npm? Confused users, may then see a dependency listed on an official npmjs package page, and may consume them by simply running a local npm install on their development machine:
1$ npm install deneuve-package-private
This is a possible scenario if a developer chooses to debug the application and decides to download each library individually. Let's be honest, how often do developers verify whether or not they are downloading a package that is meant to be private? All it takes is a simple error or oversight, which can happen even when companies have policies against the downloading of unscoped packages.
We went ahead and published a null package of that name to the npmjs registry. As you can see, if you browse the Dependents tab, it actually links back to the original package that referred to it in one of its dependents' aliases:
This should be a call for attention to any developer tooling that handles package names, such as the npmjs registry itself, to ensure they don’t confuse their users with regard to package names.
The fact that typosquatting package names is still an effective supply chain attack vector demonstrates that even the slightest air of legitimacy (as seen in package aliasing) can boost an attack's success rate significantly.
Some background on this finding
This form of supply chain attack was recently discovered by myself and fellow security researcher Mario Stathako, and we reported it to GitHub and npm. I'm a graduate student who has been spending time researching and developing bug bounty security resources. I'm also a Snyk Ambassador! Mario is a penetration tester who regularly participates in Capture the Flag (CTF) and bug bounty programs.
Dependency Confusion caught my interest because it was something so basic that had such a big impact! So Mario and I began searching for this issue on HackerOne's private programs. We wanted to see how successfully firms responded to/mitigated popular research after a specific period of time had elapsed. We discovered some strange behavior on the NPM website while constructing POC packages for the attacks, and that was the way the package.json file was being processed for different packages and how it listed the dependents and dependencies of that package.
Protect yourself from supply chain security concerns
Snyk has developed and published an open source command-line application called snync to assist you in detecting and warning of potential dependency confusion (and related) attacks. You can learn all about that tool in this npm dependency confusion detection and prevention blog.
Additionally, the Snyk Advisor is a helpful tool to flag security issues in packages, across their versions, and explicitly shows if a package has been found to be malicious:
Finally, here's some recommended following-up reading on open source supply chain security best practices:
Detect and prevent dependency confusion attacks on npm to maintain supply chain security
Preventing malicious packages and supply chain attacks with Snyk