What Is Dependency Confusion?
Dependency confusion is a supply chain attack that exploits how package managers resolve dependencies. When a company uses private/internal packages with names that aren't registered on public registries (like npm, PyPI), an attacker can register those same names publicly with a higher version number. The build system then pulls the attacker's malicious package instead of the internal one.
The Recon
I started by looking at the target's public-facing JavaScript files. Large applications often bundle their code and ship it to the browser, and the bundle sometimes contains references to internal packages.
By analyzing their JavaScript source maps and bundle manifests, I found several references to internal npm packages with a company-specific scope pattern. Some of these packages were scoped (e.g., @company/internal-utils), but a few were unscoped — just plain package names that didn't exist on the public npm registry.
The Attack
Identify Internal Packages: I found 3 unscoped internal package names referenced in the JavaScript bundles. A quick search on npmjs.com confirmed none of them were registered publicly.
Register the Package: I registered one of these package names on npm with a version number higher than what the internal registry was likely using (e.g., v99.0.0). The package contained a harmless proof-of-concept — a preinstall script that would make a DNS callback to my server with the hostname and username of the machine running the install.
Wait for the Callback: Within hours, I started receiving DNS callbacks from the target's internal build servers. The callbacks confirmed the package was being installed automatically during their CI/CD pipeline runs.
Confirm RCE: The DNS callback included the hostname of internal build machines and the service account username. This proved arbitrary code execution on their build infrastructure — the preinstall script ran as the build user with full access to the build environment.
Impact
- Remote code execution on internal build servers
- Access to build environment variables (secrets, API keys, tokens)
- Ability to inject malicious code into production builds
- Potential supply chain compromise affecting millions of end users
Timeline
Reported immediately after confirming the DNS callbacks. The team acknowledged within hours and began pinning all internal dependencies to their private registry. The malicious package was unpublished from npm within a day.
Takeaway
Dependency confusion is one of the most underrated attack vectors in modern software. If a company uses private packages, check if their names are available on public registries. Always look at JavaScript bundles, source maps, and package.json references in exposed files — they often leak internal package names. For defenders: always use scoped packages, pin your registry configuration, and enable namespace reservation on public registries.