Dependency Confusion Attacks Let Attackers Hijack Internal Library Names in Corporate Builds

A widespread supply chain risk allows attackers to publish packages with internal company names on public registries such as npm and PyPI, causing build systems to pull malicious versions instead of internal ones. The attack works because package managers treat multiple registries as a single list and select the highest version number, with no inherent priority for internal sources. Researcher Alex Birsan demonstrated the technique in February 2021 by registering names harvested from open repositories and error messages, successfully injecting packages into builds at Microsoft, Apple, PayPal, Shopify, Netflix, Tesla and Uber. The malicious code executes during installation because setup scripts and lifecycle hooks run with the privileges of the build agent, exposing environment variables, tokens and internal network access. Mitigation requires a single internal proxy repository that never mixes public responses for internal package names, scoped namespaces bound to private registries, lock files with content hashes, and disabling install scripts where possible. The technique remains effective against any organization that lists both internal and public registries in its build configuration.

HabrTranslated from Russian

Read at source

A build of an internal library suddenly took minutes instead of seconds and began making unexpected outbound connections. All dependencies were internal and the component had no reason to reach the internet.

Initial suspicion fell on the proxy repository cache, yet its configuration was correct. Investigation revealed that the package manager was querying the company’s own package not only from the internal proxy but also from the public registry because a second source had been added to the settings.

Internal libraries are named with ordinary strings such as billing-common or auth-client. In most ecosystems a package is identified solely by this string; the first party to register the name on the public registry owns it. Because these human-readable names are rarely claimed publicly, they remain available for anyone to take.

When a package with the internal name and a deliberately high version such as 99.0.0 is published, the build system selects it over the legitimate internal version. Alex Birsan proved the attack in February 2021 by collecting internal names from public repositories and error logs, publishing high-version packages under those names, and waiting. The packages reached builds at more than 35 large organizations, including Microsoft, Apple, PayPal, Shopify, Netflix, Tesla and Uber.

Execution occurs at install time. In Python, setup.py runs when a source distribution is built; in npm, lifecycle scripts execute by default. Both run with the build agent’s privileges, granting access to tokens, signing keys and the internal network even if the resulting artifact is never promoted to production.

Effective defenses include using a single internal proxy repository that never mixes public answers for internal names, registering scoped namespaces such as @company and binding them to the private registry, maintaining lock files with content hashes, and disabling install scripts with flags such as --ignore-scripts.

Meridian digestThe week's most consequential security stories, distilled into one briefing.See the digest

More in Supply Chain & Open Source

Passwordless Kubernetes Image Pulling with Artifact Keeper via ServiceAccount Tokens
Malicious Code Injected into LiteLLM Open Source AI Package via Supply Chain Attack
GhostAction Campaign Compromises 790 GitHub Repositories via Malicious Workflows