In todays interconnected digital world, the integrity of software supply chains has never been more critical. A software supply chain encompasses all the elements involved in the creation and delivery of a software product, from its initial development to its final deployment. This complex network of dependencies, libraries, and third-party components forms the backbone of modern applications, ensuring they operate smoothly and efficiently. However, this interdependency also presents a unique set of vulnerabilities, among which dependency confusion has emerged as a particularly insidious threat.
Understanding Dependency Confusion: A Primer
Dependency confusion is a sophisticated attack vector that exploits the way package managers fetch dependencies during a build process. It occurs when a package manager inadvertently installs a malicious package from a public repository instead of a legitimate, private one.
This typically occurs due to namespace confusion, where the public and private packages share the same name (or, in some cases, the organization hosting the package simply does not exist).
Attackers capitalize on this ambiguity, seeding public repositories with malicious packages that masquerade as internal ones. When a build system automatically pulls in the rogue package, it paves the way for code execution, data theft, or worse, on a target system.
The Link Between Dependency Confusion and Supply Chain Security
The relationship between dependency confusion and supply chain security is direct and profound. As organizations increasingly rely on open-source libraries and external codebases, the attack surface for software applications expands.
Dependency confusion attacks exploit these connections, highlighting the vulnerabilities inherent in the software development lifecycle. By targeting the very mechanisms that manage these dependencies, attackers can infiltrate and compromise systems without direct access, making it a paramount concern for supply chain security.
A Journey into the Abyss: Uncovering the Depths of Organization Hijacking via Dependency Confusion
My journey into the realm of dependency confusion began with the goal of obtaining a bug bounty, but it soon grew into a quest to gauge the vulnerability’s impact on applications worldwide. To exploit this issue, I:
- identified a website that relied on non-existent code from a non-existent organization.
- registered the non-existent organization, thereby assuming its identity and exploiting the trust relationship between the organization and the dependent applications.
- uploaded public proof-of-concept code to my organization that performed an action of my choosing once executed by a vulnerable application.
- obtained proof that I could execute commands on vulnerable servers worldwide.
The Proof of Concept: A Closer Look
Initial bug bounty research involved deeply analyzing JavaScript files on neglected subdomains using BurpSuite, a well-known application security tool. One such subdomain appeared to rely on a JavaScript library whose organization did not exist.
Upon visiting the link provided by the JS Miner BurpSuite extension, I confirmed that the organization was not registered on NPM.
This meant that if an individual registered under the name of the absent organization, they could publish code that the impacted server would subsequently download.
I registered an organization on NPM using the identical name that the server’s code attempted to download from, effectively taking the place of the non-existent entity. By default, NPM requires you to wait 24 hours before publishing code once you register as an organization, likely in an attempt to prevent or discourage dependency confusion attacks.
In the meantime, I began creating my malicious package, starting with package.json. This file tells the application’s NPM client what to install, where it can be found, and what other dependencies are required before installing the project. There’s a specific section called “preinstall” that tells the NPM client to execute whatever is inside before the package installation occurs.
I created the below package.json file that, when parsed by vulnerable applications, would send a POST request to a server I control, including its hostname and the contents of its /etc/passwd file. The /etc/passwd file was used specifically because it proved I could read local files on the vulnerable systems.
In a matter of seconds after publicly publishing this file to my organization, I began receiving POST requests from vulnerable systems around the world, each one including the contents of their /etc/passwd files.
To limit the amount of information I was receiving, I modified the package such that the victims would send me the name of the user executing my code as well as the hostname of the system. This provided a bird’s-eye view of the number of hosts affected by this issue.
As seen near the bottom of the above proof of concept, a fair number of instances were executing my commands as root. This meant that not only was I executing code on the affected hosts, but I was also executing this code with the highest level of privileges offered on the systems.
After witnessing the potential impact of the vulnerabilities on the affected hosts, I neutralized the payload in my package and rendered it harmless. In doing so, I indirectly safeguarded the affected organizations from this dependency issue, since I now manage the resource.
Mitigating the Threat: Strategies and Best Practices
In light of these findings, it is imperative that organizations adopt comprehensive strategies to safeguard against dependency confusion attacks. Some effective measures include:
- Implementing Package Signing: Sign internal packages and verify their authenticity before installing them.
- Reserve Public Namespaces: Create a public namespace to prevent others from squatting on organizations, projects, or individual packages.
- Prioritize Private Package Registries: Configure package managers to prioritize private registries over public ones to reduce the risk of dependency confusion.
- Dependency Review and Audit: Regularly review and audit the dependencies for anomalies or unnecessary packages.
- Educating Developers: Raise awareness among developers about the risks associated with dependency confusion. Implement and promote secure coding practices according to industry standards.