Safeguard
Open Source Security

The torchtriton Dependency Confusion Attack on PyTorch-Ni...

How a namespace gap on PyPI let a malicious "torchtriton" package hijack PyTorch-nightly installs for five days, and what it teaches about ML supply chain security.

Vikram Iyer
Security Researcher
7 min read

In late December 2022, a threat actor uploaded a malicious package called torchtriton to the Python Package Index — the same name as an internal dependency used by PyTorch-nightly builds. Because pip, by default, will fetch a package from the public index if the name matches, anyone who installed PyTorch-nightly between December 25 and December 30, 2022 could have silently pulled the attacker's code instead of the real one. This is the torchtriton dependency confusion attack, and it remains one of the clearest real-world demonstrations of how a naming collision between a private build dependency and a public registry can turn an ordinary pip install into a remote code execution event. The PyTorch team disclosed the incident on December 30, 2022, and the malicious package had already been live on PyPI for five days.

What is the torchtriton dependency confusion attack?

It's an incident in which a malicious PyPI package impersonated torchtriton, a Triton compiler dependency that PyTorch-nightly builds normally pulled from PyTorch's own package index, not from PyPI. Dependency confusion attacks exploit a simple but dangerous ambiguity: when a project uses multiple package sources (an internal or vendor index plus the public PyPI registry) and doesn't explicitly pin which source a given package must come from, the installer can end up choosing the attacker's public upload over the legitimate internal one — especially if the public version has a higher version number. In this case, torchtriton had never been published on PyPI at all, which meant the namespace was sitting open for anyone to claim. On December 29, 2022, an unknown actor registered the name and uploaded a package containing malicious code, and PyPI's default resolution behavior did the rest.

How did the attacker exploit PyPI's package resolution?

The exploit worked because PyTorch-nightly's install instructions referenced an extra index URL (PyTorch's own repository) alongside the default PyPI index, without pinning torchtriton to that specific source. When pip resolves a dependency across multiple indexes with --extra-index-url, it does not treat the additional index as the trusted or exclusive source — it can still consider packages hosted on PyPI itself, and in many resolver configurations PyPI is effectively treated as equally or more authoritative. Since the legitimate torchtriton build artifact only lived on PyTorch's index and had no real counterpart on PyPI, the attacker's upload had no competition: it was the only "torchtriton" pip could find on the public registry, and depending on install order and caching, it could be selected instead of the internal artifact. This is the same class of vulnerability that security researcher Alex Birsan popularized in 2021, when he used dependency confusion to get code executed inside the internal networks of Apple, Microsoft, Tesla, and dozens of other companies simply by publishing packages under their internal library names to public registries like PyPI and npm.

What data did the malicious torchtriton package steal?

The malicious torchtriton package ran an install-time script that harvested a specific, targeted set of information from any machine it landed on. According to PyTorch's own disclosure, it collected system information (hostname, username, current working directory, and environment variables), the contents of /etc/passwd, the contents of /etc/hosts, the machine's nameserver configuration, and — most seriously — the first 1,000 files in the user's $HOME directory along with the contents of $HOME/.gitconfig and $HOME/.ssh/*, meaning SSH private keys were a direct target. All of this was exfiltrated via encoded DNS queries to a hard-coded command-and-control domain, a technique chosen specifically because DNS traffic is far less likely to be inspected or blocked by egress filtering than a normal HTTP callout. For any developer running PyTorch-nightly on a machine with SSH keys or cloud credentials cached locally — a common setup for ML engineers working against GPU clusters or cloud training environments — this was a credential-theft pipeline disguised as a routine compiler dependency.

Who was affected and when, and how did PyTorch respond?

Anyone who installed PyTorch-nightly via pip between December 25, 2022 and December 30, 2022 on Linux was potentially exposed; PyTorch explicitly noted that the compromised dependency only affected the Linux nightly builds, since torchtriton wasn't bundled into stable release channels. PyTorch's engineering team moved quickly once the malicious upload was discovered: they removed the torchtriton dependency from PyTorch-nightly's requirements, renamed the legitimate package to pytorch-triton to eliminate the naming collision going forward, and — critically — registered a placeholder pytorch-triton package on PyPI themselves so no one could squat on the new name either. They also published clear remediation steps: uninstall torchtriton, and uninstall and reinstall torch, torchtriton, and torchvision/torchaudio nightly builds from the official index. The advisory recommended treating any machine that had installed the compromised nightly package as potentially exposed and rotating SSH keys and credentials that may have been read from $HOME.

Why does this matter for the ML supply chain specifically?

It matters because machine learning workflows are unusually dependency-heavy and unusually trusting of nightly or bleeding-edge builds, which widens the attack surface compared to typical application dependencies. ML engineers routinely install nightly wheels to get access to the latest CUDA kernels, compiler backends, and hardware support — exactly the population that installed the compromised torchtriton build. Those same engineers' machines are also disproportionately likely to hold SSH keys to GPU clusters, API tokens for cloud training jobs, and credentials for data infrastructure, which is precisely what the malicious payload went after. The torchtriton incident showed that the ML supply chain has the same PyPI-based trust assumptions as the rest of the Python ecosystem, but with a blast radius that extends into compute infrastructure and, potentially, training data and model weights. It also wasn't an isolated event: PyPI has repeatedly been used as a distribution vector for malware disguised as ML tooling, from typosquatted packages mimicking popular libraries to fake "helper" packages targeting model developers.

Could pinning and lockfiles alone have prevented this?

Not entirely, because the vulnerability lived in how the package was sourced, not just which version was requested. Pinning a package to a specific version number doesn't stop an attacker from publishing malicious code under that exact version string on a different index if the resolver doesn't enforce which index is authoritative for which package. The real fix required index-scoping — explicitly telling pip that torchtriton (or pytorch-triton) must only ever come from PyTorch's own repository, never from PyPI — combined with proactively claiming the name on PyPI as a defensive placeholder, which is exactly what PyTorch did after the fact. This is the underlying lesson of nearly every dependency confusion attack since Birsan's original research: version pinning protects against known-bad updates, but it does nothing against a same-name, same-version package injected from an untrusted source unless the build system is explicitly told which registry to trust for which package.

How Safeguard Helps

Safeguard is built for exactly this failure mode: the moment where a dependency resolver, not a developer, makes the wrong trust decision. Safeguard continuously maps your software supply chain — including internal packages referenced from private indexes, extra-index-URL configurations, and nightly or pre-release build pipelines like the ones PyTorch-nightly relies on — and flags any internal package name that has no corresponding claim on public registries such as PyPI or npm, closing the exact namespace gap that made torchtriton possible. It enforces index-scoping and source-pinning policies across your CI/CD and developer environments, so a package name can never silently resolve from an untrusted public source when an internal one is expected. Safeguard also monitors public registries in real time for newly published packages that match your internal dependency names or common typosquats, alerting your security team before a malicious upload can sit live for five days the way the torchtriton package did. For ML and data science teams specifically, Safeguard extends this coverage to the nightly-build and bleeding-edge-dependency patterns that are standard practice in the field, treating the ML supply chain with the same rigor as production application dependencies — because as torchtriton demonstrated, a compromised compiler dependency can be just as damaging as a compromised production library.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.