A Python package stole everything on your machine. Here’s how.
On March 24, 2026, two versions of a popular Python package called LiteLLM were uploaded to PyPI — the central repository where Python developers download software. The versions looked normal. They installed normally. They ran normally.
They also collected every secret on your computer and uploaded them to a server in another country.
SSH keys. AWS credentials. Google Cloud credentials. Azure credentials. Kubernetes tokens. Database passwords. API keys. Git credentials. Shell history. TLS private keys. Cryptocurrency wallets. Every .env file. Everything.
If you installed LiteLLM version 1.82.7 or 1.82.8 during a roughly six-hour window on March 24, every credential on your machine was compromised. Not theoretically. Actually. Encrypted, packaged, and sent to a domain the attackers registered the day before.
This is called a supply chain attack. It’s not new. But this one is worth understanding in detail, because it reveals something important about how most developers work with AI tools today — and how exposed that makes them.
What actually happened
LiteLLM is a Python library used by developers to connect to AI models — OpenAI, Anthropic, Google, and others — through a single interface. It’s downloaded roughly 3.4 million times per day. It’s one of the most widely-used AI infrastructure packages in production.
The attackers didn’t hack LiteLLM directly. They did something more elegant.
Step 1: Compromise a security tool
The attack group — known as TeamPCP — first compromised Trivy, a widely-used security scanner made by Aqua Security. They exploited a vulnerability in Trivy’s GitHub automation to steal credentials from an internal account. Then they rewrote Trivy’s release tags to point to a version they controlled.
Anyone who ran the latest version of Trivy after March 19 was actually running the attackers’ version.
Step 2: Let the security tool steal the keys
LiteLLM, like many projects, runs security scanners in its automated build pipeline. It ran Trivy. It ran the compromised version. And because LiteLLM hadn’t pinned Trivy to a specific version, it pulled whatever was latest.
The compromised Trivy didn’t scan for vulnerabilities. It scanned for credentials. Specifically, it found and stole LiteLLM’s PyPI publishing token — the credential that allows uploading new package versions.
Step 3: Publish the poisoned versions
Using LiteLLM’s own publishing token, the attackers uploaded two backdoored versions directly to PyPI. No code review. No maintainer approval. No corresponding entry in LiteLLM’s GitHub repository. To anyone checking PyPI, the versions looked like routine updates from the same trusted publisher.
The payload
The malicious code was sophisticated. Version 1.82.7 embedded the payload in the proxy server module. Version 1.82.8 went further — it installed a file that triggers every time Python starts, regardless of whether you import LiteLLM. Just having Python installed with this package present was enough.
The payload ran in three stages:
First, it harvested every credential it could find. SSH keys, cloud provider configs, environment variables, Kubernetes tokens, Git credentials, .env files, shell history, private keys — a comprehensive inventory of everything a developer accumulates on their machine.
Second, it used any Kubernetes credentials it found to spread to other machines in the network. It deployed itself across cluster nodes, accessing host filesystems to install persistent backdoors.
Third, it set up a disguised system service that checked in with the attackers’ server every fifty minutes for additional instructions.
All harvested data was encrypted with a randomly generated key (itself encrypted with the attackers’ public key) and sent to models.litellm.cloud — a domain designed to look like legitimate LiteLLM infrastructure.
How it was discovered
Ironically, the attackers’ own code had a bug.
The file that triggers on every Python startup was too aggressive. Each time it spawned a subprocess, that subprocess started Python, which triggered the payload again, which spawned another subprocess. An accidental fork bomb. Affected machines ground to a halt.
This made the compromise immediately obvious instead of silently persistent. Security researchers were already investigating, and multiple detection systems flagged the malicious versions within hours. PyPI pulled both versions roughly six hours after publication.
Without that bug, the payload could have run quietly for days or weeks before anyone noticed.
Why this matters beyond LiteLLM
This story isn’t really about LiteLLM. LiteLLM was well-maintained, widely trusted, and run by a competent team. They didn’t make a catastrophic mistake. They made an ordinary one — running an unpinned dependency in their build pipeline — and a sophisticated attacker exploited it.
The real story is about what was there to steal.
The developer’s machine is a vault with the door open
If you work with AI tools — or any cloud services — count the credentials on your machine right now. OpenAI API key. Anthropic API key. Google Cloud credentials. AWS access keys. Maybe a Replicate token. A Pinecone key. A database connection string. Stripe keys. Twilio credentials. SSH keys to production servers.
These accumulate naturally. Every new service hands you an API key. You add it to your .env file or your shell profile. Over months and years, your development machine becomes a keyring for every service you’ve ever connected to. Each key is a direct line to a paid service, a production database, or a private system.
A supply chain attack doesn’t need to be targeted at you specifically. It just needs to land on your machine. Once it’s running as your user, it can read everything you can read. And what you can read is everything.
This will happen again
TeamPCP’s campaign didn’t stop at LiteLLM. They compromised Trivy, then Checkmarx’s KICS scanner, then LiteLLM — each compromise enabling the next. The pattern is cascading: compromise one piece of trusted infrastructure, use it to compromise the next.
The Python ecosystem has roughly 600,000 packages. The JavaScript ecosystem has over 2 million. Every pip install or npm install pulls a tree of transitive dependencies, each of which is a potential entry point. The surface area is vast, the trust model is implicit, and the incentive for attackers is enormous.
Supply chain attacks are not a new phenomenon. SolarWinds (2020), Codecov (2021), the ua-parser-js npm hijack (2021), the PyTorch nightly compromise (2022), the xz utils backdoor (2024). The frequency is increasing and the techniques are maturing.
What you can do about it
There’s no single fix for supply chain attacks. But there are structural decisions that dramatically reduce your exposure.
Pin your dependencies
LiteLLM was compromised because its build pipeline pulled the latest version of Trivy without specifying which version. If they’d pinned Trivy to an exact version and verified the artifact’s digest, the compromised version would never have been pulled automatically.
This applies to your own projects too. Lock files (package-lock.json, poetry.lock, go.sum) exist for a reason. Use them. Review updates before applying them. Don’t auto-upgrade in production pipelines.
Reduce the number of secrets on your machine
This is the most important structural change you can make, and the one most developers overlook.
Every API key in your .env file is a key that a supply chain attack can steal. The LiteLLM payload harvested environment variables, config files, and credential stores indiscriminately. If the key was on the machine, it was taken.
The question to ask is: does this key need to be on my machine at all?
In many cases, the answer is no. If a service can manage provider credentials on your behalf — holding the OpenAI key, the Anthropic key, the Google key on its infrastructure instead of yours — then your machine holds one credential instead of twelve. The blast radius of a compromise shrinks from “every service you’ve ever used” to “one service that can be quickly revoked.”
This is the difference between leaving a dozen house keys under the doormat and keeping them in a lockbox. The lockbox can still be broken into. But the lockbox can also have an alarm, a camera, and a log of every time it’s opened. A doormat can’t.
Bind credentials to devices
A stolen API key is only useful if the attacker can use it from their own machine. If the key is bound to your specific device — verified on every request — then exfiltrating it to a server in another country accomplishes nothing. The key works on your laptop and nowhere else.
Device binding isn’t a feature most tools offer today. But it’s the kind of architectural decision that makes the difference between “your key was stolen, rotate everything” and “your key was stolen, and it didn’t matter.”
Use short-lived credentials
A key that expires in an hour is worth far less to an attacker than a key that lives forever. If your tools support token rotation — short-lived access tokens refreshed by a longer-lived credential that’s bound to your device — a stolen access token is a rapidly closing window rather than an open door.
Monitor for anomalies
If your API key is suddenly being used from an IP address in a country you’ve never visited, something is wrong. Services that track per-key usage patterns — geography, volume, timing — and alert on anomalies turn a silent compromise into a visible one. You can’t respond to what you can’t see.
The uncomfortable truth
The developer workflow that most of us take for granted — storing a dozen API keys in environment variables and .env files on our laptops, installing packages from public registries with hundreds of transitive dependencies, running builds with broad network access — is structurally optimized for supply chain attacks.
Each of these practices made sense when the threat model was “someone might guess my password.” They don’t hold up when the threat model is “a compromised package in my dependency tree will silently exfiltrate every credential on my machine in under a second.”
The LiteLLM attack was sophisticated in execution but simple in outcome: it read files that were sitting there waiting to be read. The fix isn’t better file permissions or more careful dependency review (though both help). The fix is making sure that when — not if — the next supply chain attack lands on your machine, there’s nothing valuable for it to find.
Learn more
- Agents of Chaos — How the Northeastern red-teaming study documents what happens when agents operate with human tools
- Bigger Cages, Better Tools — Why sandboxing agents isn’t enough, and what the alternative looks like