Modern applications are built using logically isolated components, each performing a set of functions that can be used directly within the software. These software components, also known as dependency packages or libraries, are stored in public or private repositories that can be reused across multiple software supply chains.
This article discusses what dependency confusion relates to in modern application delivery, how it can lead to security vulnerabilities, and recommended practices to mitigate such vulnerabilities.
What is Dependency Confusion?
Dependency confusion in application delivery arises when two or more dependencies conflict with each other, and the system is unaware of which one to use first. Dependency confusion attacks are exploited in such scenarios when an adversary tricks the software package manager into pulling a malicious package from a public library instead of the legitimate one. Also known as supply chain substitution, such attacks leverage the fact that users don’t need to specify the source repository for a component, where the package installer automatically handles downloads and configuration.
The workflow of a typical supply chain substitution attack is similar to the following:
- A hacker discovers private package names used within a target organization
- The hacker builds a malicious version of the package
- The malicious package is subsequently added to a public code repository
- Next time a package installer requests the dependency packages from the repository, it ends up downloading the malicious code file from the public library instead of the internal registry
With dependency packages becoming commonplace in modern software development, popular programming languages have simplified their package installers to abstract the tasks involved in installing and managing third-party components. Unfortunately, automated package updates and installations facilitate a hacker’s intent to substitute malicious code files for legitimate packages. Unfortunately, hackers also adopt multiple approaches to tricking package managers into downloading the malicious dependency, including:
- DNS Spoofing – Directing the package manager to pull dependencies from external libraries by altering repository paths in the code base
- Namespacing – Uploading the code file to a public repository with the same name as the one used in internal libraries
- Scripting – Modifying Infrastructure as Code (IaC) and other configuration scripts to alter the dependency paths and trick the system into installing malicious packages
Dependency Confusion Attack Packages
Dependency confusion attack payloads can be categorized into different kinds, including:
Typosquatted Packages
The typosquatting dependency package targets users who mistakenly type in a wrong URL or search term. An attacker strategically places copycat packages within the wrong search query or web link to exploit such instances. A prevalent approach by hackers is to claim multiple wrongly spelled yet near identical package names in easily accessible public registries. For example, assuming the legitimate package is stored in a repository named darwin-qa, attackers tend to register numerous package sources with names similar to the original repository, such as darwin-qa, darwinqa, darwin-aq, and dawrin-qa. Hackers use these malicious repositories to store exploit scripts; victims end up downloading the wrong package when they input the wrong search query, CLI command, or URL.
Latest Version Packages
Hackers have assigned these versions of public repositories a higher version number than the latest legitimate package used in the production environment. After enumerating all packages in use and their respective version numbers, attackers craft malicious dependencies in a package, assign the package a higher version number, and store it in a public repository. If the application’s package managers are configured to fetch the latest version, it will ultimately pull the hacker’s dependency package from the public repository.
Let us understand this better with a sample scenario.
- Assuming a package called darwin-package is tagged as darwin-package:1.0.0 in an internal registry URL- https://localhost:4250
- To exploit this, hackers build a malicious code file named darwin-package, tag it darwin-package:1.0.2, and make it available publicly to a registry, such as https://www.npmjs.com/package/darwin-package
- In such a scenario, a vulnerable target’s package manager will pull the attacker’s dependency file when configured to fetch the latest package version and install it.
Dependencies with Differing Package and Import Names
Due to nomenclature rules and other constraints, a package name may vary slightly from the name used in its importation command. Most package managers boast of downloading, extracting, and installing dependency packages in a single click. Developers who quickly install these dependencies without appropriate validation often fail to check the name used and install the wrong component. Malicious actors exploit this confusion by placing corrupt code packages with other possible names, enforcing a simple yet effective attack strategy.
Following is a sample scenario to understand how this is orchestrated:
- Assume a package is named darwin-production, with its import name darwin-prod-pkg
- Hackers may develop a copycat package with the import name darwin-production containing harmful scripts
- An ignorant developer may use the import name darwin-production when installing dependencies, making the application pull the suspicious packages developed by hackers
Dependency Confusion Vulnerability Types
Dependency confusion vulnerabilities are categorized according to the programming language or system affected. Some common types of dependency confusion vulnerabilities include:
Microsoft Dependency Confusion
Developers of Microsoft applications rely on the .NET framework to leverage tools and libraries required for agile builds. Microsoft also recommends NuGet as the default packet manager for .NET software development tools. Organizations that do not own all upstream packages pulled by NuGet are susceptible to dependency confusion vulnerability. Besides this, organizations that lack a prefix in their NuGet galleries for internal packages are also vulnerable to supply chain substitution attacks.
NPM Dependency Confusion
Dependency confusion vulnerabilities are frequently found in JavaScript applications, where internal package.json files embed the entire project’s dependencies into a public script as part of the build process. Since NPM is the leading repository for Node.js packages, it is known to potentially contain dependency confusion exploit scripts in the absence of automated, sanity-checked package updates.
An organization that uses packages from the NPM repository is susceptible to dependency confusion attacks in instances when:
- The organization’s development pipelines lack an internal registry
- Developers fail to include appropriate configuration information in the package manager
- The NPM environment configuration file (.npmrc) fails to include the proper internal registry
PIP Dependency Confusion
PIP is the default installer of Python libraries hosted on the PyPi online repository. PIP is considered insecure by default since it checks for both specified and public package indexes; eventually installs the latest dependency version if more than one is found. To exploit such a vulnerable construct, hackers upload a malicious version with a higher version number, following which the malicious code is automatically pulled to replace the original package.
Mitigating Dependency Confusion Vulnerabilities
Techniques to mitigate dependency confusion attacks include:
Version pinning
Dependency pinning explicitly declares the version of a package the application will use. Some package managers allow developers to pin their applications to various versions instead of one. Freezing the dependency version enables iterative installations and finer control over dependencies by ensuring all deployments leverage the same Git (or other version control systems) repository.
Namespaces and dependency scopes
Most online repositories use scopes or namespaces to enable isolation and control of resources within an organization’s tech framework. In a typical scenario, an organization owns a scope or a namespace, while the repository establishes a registration and validation process to verify uploads. To ensure all components are pulled from trusted repositories, a recommended practice is to enforce policies that require developers to reference only defined namespaces or scope names when fetching packages.
Client-side validation
Package installers should support client-side verification, such as a hash-checking mode that evaluates all downloaded packages against a hash stored on the client side. Some package repositories also include plugins that verify PGP signatures of installed components. Such integrity validation helps strengthen the security posture while impeding successful exploits requiring control over the repository server and client machines.
FAQs
Which organizations are susceptible to dependency confusion attacks?
Dependency confusion is a novel class of attacks that exploits the modular requirements of modern application architecture. As attackers don’t require elevated privileges or sophisticated tools, these attacks are fairly simple to orchestrate. Furthermore, the attack requires no action on the part of the victim user as attackers trick continuous delivery builds into pulling and installing malicious versions of software packages. Since most enterprises use open-source tools within their tech stack, attackers craft exploits that abuse logic in package installers to deploy malware, perform remote code execution or exfiltrate sensitive files.
What are the common dependency confusion attack scenarios?
Attackers can deliver the malicious package into the deployment in several ways. These include:
- Through fake software updates sent to official organizations
- Labeling a legitimate dependency update as fake, making a component vulnerable by missing crucial security upgrades
- Using fake dependency signatures to make the installer think malicious packages are legitimate