\ /

Dependency confusion attack technique

When developing applications, we always rely in external dependencies.

This is very useful because we don't want to "reinvent the wheel" every time we need a functionality that someone already implemented (following the code reuse principle). For example, if we are writing some Node.js application, it's quite common to use the following command to install a third party library:

npm install <package_name>

In Python, we can do the same using:

pip install <package_name>

npm and pip are called package managers and many other programming languages provide similar mechanism to download and import dependencies in our project.

Although package managers are very useful, at the same time we are basically blindly trusting their contents. What would happen if an attacker would be able to upload arbitrary packages and force us to download and import his own package, instead of the one we are looking for? Of course bad things will happen, because our application will run untrusted code (maybe a malware or a backdoor), which can lead to data exfiltration and remote code execution.


Dependency confusion

Packages are often hosted in public repositories. So when we run:

npm install <package_name>

npm will look for "package_name" within the public npm registry and it will download and import the required package in our project. From now on, we are ready to use the newly imported package.

It's also possible for companies to host their own internal registry, which contains private packages (packages that are used by the organization and that are not available externally).

Let's say that our build server is configured to download the package private_package which exists within the internal registry server and its current version is 1.0 (package manifests are usually defined in a package.json file).

Also, let's assume that another package with the same name - private_package - is uploaded to the public npm registry but with higher version, let's say 9.9 .

It turns out that when our build server (or even the developer's PC) will try to download the private_package using npm install private_package command, it will first ask for the package to the internal npm registry (because we properly configured the local npm registry) but the internal npm registry will check if a newer version exists within the public repository. If it exists, it will download and install the newer version (in our example the 9.9 version).

So, if an attacker is able to leak private package names, he could potentially upload malicious version of the packages with a very high version (i.e. 99.99.99) to force the npm to install the malicious package.

Unfortunately, it turns out that is quite common for applications to embed package.json contents into public script files during their build process. And just by looking for these packages in public npm registry we can easily figure out the ones that are private (because they can't be found in the public registry).

The below screenshot shows the leaked package.json file for PayPal (private packages are highlighted in red).


Using this information, security researcher was able to upload arbitrary packages to the public npm registry using the same name of the private packages but with higher version. In order to test that the packages are actually downloaded and executed by the target, he took advantage from the preinstall script directive (which is executed automatically upon package installation) and he was able to exfiltrate data using DNS exfiltration technique (you can read more about the technique in this article by our colleague Carlo Mandelli).

How to defend against such attacks

Due to how these package managers are built, it can be tricky to identify if we are vulnerable to dependency confusion attacks (it also depends on which programming language and package manager we are using). By the way, there are few general rules that we can enforce to prevent these kind of attacks:

  • configure your private registry to never proxies requests to upstream public repositories to check for a newer version
  • raise a warning if a package is defined in our internal registry as well as in public registry and double check before importing it
  • use SCA tools to get a clear and up to date representation of project's dependencies

Additional information on how to deal with private package feeds can be found in this Microsoft's white paper.

Final results

Using the same technique, the security researcher (Alex Birsan) successfully exploited the same vulnerability for other package managers (pip, RubyGems) and other software used for hosting internal packages of all types (JFrog Artifactory, Azure Artifacts). He was able to execute arbitrary code and exfiltrate data from Apple, PayPal, Netflix, Microsoft and many other major companies.

Dependency confusion has been awarded as the best hacking technique for 2021 by the PortSwigger Web Security’s annual Top 10 Web Hacking Techniques.

comments powered by Disqus