On March 24, 2026, someone published two malicious versions of a popular Python library called LiteLLM to PyPI the central repository where Python packages live. The versions were live for less than an hour. In that time, anyone who ran a simple pip install litellm had their SSH keys, cloud credentials, database passwords, API keys, Kubernetes tokens, and more silently stolen from their machine.
LiteLLM has 97 million downloads per month. It's used by thousands of companies as the routing layer between their applications and AI providers like OpenAI, Anthropic, and Google. But the scary part isn't LiteLLM itself. It's the contagion.
If you installed a completely different package say, dspy, a popular AI framework and that package depended on LiteLLM, you were hit too. You never typed pip install litellm. You didn't know it was in your system. It got pulled in silently as a transitive dependency, and the malicious code ran anyway.
This is a story about how modern software is built, why it's more fragile than anyone wants to admit, and what this specific incident teaches us about the AI stack that's being assembled right now.

What actually happened
Two versions of LiteLLM v1.82.7 and v1.82.8 were uploaded to PyPI on March 24, 2026, around 10:39 UTC. They contained a credential stealer designed to scan your system for:
- Environment variables (where most API keys and database URLs live)
- SSH keys
- Cloud provider credentials AWS, GCP, Azure
- Kubernetes tokens
- Database passwords
- Git credentials
- SSL private keys
- CI/CD secrets
- Shell history
- Crypto wallets
The stolen data was encrypted and sent to a server at models.litellm.cloud a domain made to look like LiteLLM's official infrastructure. It is not. The attacker registered it to look close enough that most people wouldn't notice it in network logs.
The packages were removed by 16:00 UTC. But by then, the damage was done for anyone who had installed them.
How it was discovered: a bug in the attack
Here's the part that should keep you up at night. The attack was discovered by accident.
A developer named Callum McMahon was using an MCP plugin inside Cursor (an AI code editor). That plugin pulled in LiteLLM as a transitive dependency Callum didn't install it directly, didn't know it was there. When version 1.82.8 landed on their machine, the malicious code had a bug. It consumed all available RAM and crashed the system. That crash was unusual enough to investigate, and that's how the malicious package was found.
If the attacker had written slightly better code if the credential stealer hadn't crashed the machine this could have gone undetected for days or weeks. The malicious versions would have kept accumulating installs. More credentials would have been stolen. And nobody would have known.
A bug in the attack is what saved people. Not a security tool. Not a scanner. A bug.
How the attack worked technically
The most clever part of this attack is a file called litellm_init.pth that was included in v1.82.8.
When Python starts up, it automatically looks for files ending in .pth in its package directory and executes them. You don't need to import anything. You don't need to call a function. If the file is there, the code runs. Every time. For every Python process on that machine.
This breaks a common assumption people have: "If I don't use the package, I'm safe." With a .pth file, that's wrong. A background worker, a web server, a scheduled script anything running Python on that system would trigger the payload just by starting up.
The stolen data was sent over HTTPS, which means it looked like normal encrypted web traffic to any network monitoring tool. Without specific rules blocking unknown subdomains of litellm.cloud, the exfiltration would pass through most firewalls without a trace.
The entry point: a security tool got compromised
The attack didn't exploit a flaw in LiteLLM's code. The source code on GitHub was never modified.
According to the LiteLLM team, the entry point was Trivy a security scanning tool used in their CI/CD pipeline. Trivy itself was compromised in a broader incident. Credentials were stolen from that environment, including access to a LiteLLM maintainer's PyPI account. The attacker then uploaded the malicious packages directly to PyPI, completely bypassing LiteLLM's official build process.
Think about this. The tool LiteLLM was using to secure their supply chain became the thing that compromised it. Every security check in their build pipeline artifact scanning, build logs, reproducible builds was irrelevant because the attacker went around the entire build process.
Your security tools are themselves dependencies. They have their own dependencies. When one gets compromised, it doesn't just fail to protect you. It becomes a doorway.
The transitive dependency problem
This is the part that makes this attack genuinely terrifying for anyone who builds software.
LiteLLM is not just installed directly. It's a dependency of dozens of popular AI tools agent frameworks, MCP servers, RAG pipelines, orchestration layers. When you install one of those tools, LiteLLM comes along for the ride.
Here's the chain that caught Callum McMahon:
- Install an MCP plugin for Cursor
- That plugin depends on LiteLLM (specified as
litellm>=1.64.0no version pin) - pip pulls the latest version: 1.82.8
- The malicious code runs
Callum never chose to install LiteLLM. Never typed the command. Never saw it in their dependency list. But it was there, and the malicious version landed on their machine because the upstream package didn't pin the version.
This is how most dependency trees work in Python. Package A depends on Package B which depends on Package C. If C gets compromised and A and B don't pin versions, every user of A and B is affected. Most developers don't know the full tree of what they're installing. Most don't check.
With 97 million downloads per month, LiteLLM sits deep inside the dependency trees of a significant portion of the AI ecosystem. The blast radius of this attack wasn't just LiteLLM users. It was everyone who depends on something that depends on LiteLLM.
Who was safe
There is one group that was completely unaffected: people running the official LiteLLM Proxy Docker image.
That image pins every dependency to an exact version in its requirements file. It wasn't rebuilt during the attack window. It never pulled the malicious versions from PyPI.
Same application. Different deployment method. Completely different outcome.
This is the clearest proof that pinned dependencies work. Not as a nice-to-have best practice, but as an actual defense that prevented compromise for an entire category of users.
Why this keeps happening
Supply chain attacks on software aren't new. But they're getting worse, and the AI ecosystem is particularly vulnerable for a few reasons.
Speed over safety. The AI space moves incredibly fast. New packages drop daily. Developers experiment constantly. pip install latest-thing is the default behavior. Nobody wants to spend twenty minutes pinning dependencies when they're trying to get a prototype working. That culture carries into production.
Deep dependency trees. AI tooling has unusually deep dependency graphs. An AI application might pull in an agent framework, which pulls in an LLM gateway, which pulls in a vector database client, which pulls in a serialization library. Each link is a potential attack vector. Most teams don't know the full tree.
High-value targets. AI tooling now handles API keys for expensive LLM services, production data, and in some cases, customer information. The credentials flowing through these systems are worth real money. Attackers know this.
Weak registry defaults. PyPI the largest Python package registry doesn't require package signing, doesn't mandate build provenance verification, and doesn't enforce that published packages match the source code in the repository. Anyone with valid credentials can publish anything. Other ecosystems (Rust's crates.io, Node's npm, Go's module proxy) have stronger mandatory defaults.
The uncomfortable question about dependencies
There's a deeper conversation that this incident forces, and it's one the software industry has been avoiding.
The classical view of software engineering says dependencies are good. We're building pyramids from bricks each brick is a package, each layer builds on the last, and the result is something taller and more capable than any individual could build alone.
But every dependency is also a liability. It's code you didn't write, maintained by people you don't know, updated on a schedule you don't control, with access to your system that you may not fully understand. When a dependency deep in your tree gets compromised, you're not just affected you're affected without knowing you should be.
This is especially relevant in the AI era, where LLMs can generate functional code for many common tasks. For simple enough functionality a data parser, a file formatter, a small utility the question is increasingly valid: is it safer to have an LLM write 50 lines of code that you own and control, or to pull in a package with 15 transitive dependencies maintained by strangers?
This isn't anti-open-source. It's a recognition that the cost-benefit calculation of dependencies has changed. The upside (saved development time) is the same as it always was. The downside (supply chain compromise) is growing faster.
What you should do right now
If you might have been affected:
-
Rotate everything. API keys, cloud credentials, database passwords, SSH keys, Kubernetes tokens. Any secret that existed on a system where the compromised versions were installed should be considered stolen. Not reviewed. Not checked. Rotated.
-
Check for the malicious file:
find / -name "litellm_init.pth" 2>/dev/nullIf it's there, remove it and treat the machine as compromised. The payload had filesystem access you can't assume it only did what's been publicly described.
- Check your dependency tree:
pipdeptree | grep litellmYou might find LiteLLM in places you didn't expect.
- Rebuild any Docker images that were created during the attack window (March 24, 10:39–16:00 UTC). The PyPI packages are gone, but your images still contain them.
If you weren't affected:
- Pin your dependencies. In production, in CI/CD, in Docker builds. Every package gets a version number. No exceptions.
- Use lockfiles.
pip freeze,pip-tools,poetry lockwhatever tool you prefer, commit the lockfile and review changes in pull requests. - Check your transitive dependencies. Know what your dependencies depend on.
- Audit your CI/CD pipeline. The tools you use for security are themselves dependencies. What happens if one gets compromised?
- Add egress controls. Block outbound traffic by default, allow only what you explicitly need. The exfiltration in this attack would have been stopped by a firewall rule that didn't recognize
models.litellm.cloud.
The bigger picture: AI security needs layers
This attack bypassed the LLM entirely. It didn't manipulate prompts. It didn't poison outputs. It went after the plumbing around the LLM the Python packages, the CI/CD pipeline, the dependency tree.
Most of the conversation around AI security today focuses on the application layer: prompt injection, output manipulation, PII leakage. Those are real threats. But they're one layer, and this attack showed what happens when a different layer the infrastructure layer is where the attacker strikes.
You need all of these working together:
- Dependency integrity pinned versions, lockfiles, verified builds
- Pipeline security minimal credentials, OIDC-based publishing, tooling audits
- Runtime protection egress controls, process isolation, network policies
- Application security prompt validation, output sanitization, PII redaction
Most teams have one layer. Some have two. Almost nobody has all four. And the attackers only need to find the one you're missing.
What this means for HridaAI
We built SPRO to handle the application layer PII redaction, prompt injection detection, output validation, token monitoring. It wouldn't have stopped the LiteLLM supply chain attack. No prompt security tool could have.
But here's why it still matters: when infrastructure-layer attacks succeed and they will the damage shouldn't cascade through every layer of your stack. If a compromised package steals your API keys, that's a pipeline failure. If it also uses those keys to exfiltrate your users' data through manipulated LLM calls, that's a second failure that runtime security could have caught.
Defense in depth means each layer fails independently. One breach doesn't become a breach of everything.
Bottom line: A single pip install compromised systems across the AI ecosystem in under an hour. It was found by accident because the attack had a bug. The next one might not. Pin your dependencies. Know your dependency tree. And start treating AI security as a multi-layer problem, because the attackers already do.