LiteLLM Got Poisoned Again: What to Do When an AI Package Turns on You

By Andrew Owens |

On October 21, someone published a malicious LiteLLM release. Version 1.107.0 went out to PyPI and to the project's official Docker images, and it stole credentials from whatever machine it landed on. If you installed it, uninstalling it isn't enough. Every secret that machine could reach should be treated as gone.

LiteLLM is the library a lot of teams use to talk to OpenAI, Anthropic, Bedrock and the rest through one interface. Plenty of people also run it as a proxy that holds all their provider keys in one place. That's exactly why it's worth attacking. And this is the second time this year: in March, versions 1.82.7 and 1.82.8 were poisoned on PyPI in an attack that LiteLLM traced back to a compromised security scanner in its CI.

What we know

These are the parts the sources agree on, starting with LiteLLM's own incident post:

  • Bad version: 1.107.0. On PyPI, all seven wheels. On Docker Hub and GHCR, the v1.107.0, latest, main-latest and main-stable tags.
  • Clean version: 1.106.2. LiteLLM says every earlier release is safe, and that the v1.107.0 source tag on GitHub never contained the malicious files. Customers on the private enterprise registry weren't affected.
  • How it shipped: the attacker used stolen GitHub credentials belonging to LiteLLM engineers to point the release workflow at a lookalike repository, Berri-AI/litellm instead of BerriAI/litellm, and used a second engineer's credentials to pass the approval step.
  • Reach: about 49,000 downloads of the bad version, according to ClickPy data cited by Wiz. That's in a window of roughly two hours.

Wiz also saw the stolen credentials get used. About four and a half hours after exfiltration, the attacker was checking harvested AWS credentials from CI runners and Kubernetes nodes and testing whether they could reach Bedrock. That's LLMjacking: someone else running up a model bill on your account. Four and a half hours is faster than most small teams notice anything.

Where the sources disagree

The exposure window. LiteLLM gives PyPI as 11:39 to 13:23 UTC and Docker Hub as 11:46 to 14:20 UTC, with GHCR gone by 13:51. Datadog Security Labs and Wiz put the PyPI quarantine at 13:20 and the Docker removal at about 13:53. The gaps are minutes, not hours, but if your install logs land near the edges, use LiteLLM's wider window.

How the attacker got in. This one moved a lot. In the first days, LiteLLM said it suspected the GitHub tokens were pulled from logs of an internal, internet-facing LiteLLM deployment, possibly through a recently patched vulnerability, and Help Net Security reported it that way. Then on October 23, LiteLLM published a root cause analysis that it calls the canonical record, and it tells a different story. An admin key for its Agent Platform was committed to a public GitHub repo on May 27. The team force-pushed over the commit but never revoked the key. On October 10, someone used it to pull a GitHub CLI token belonging to an organization owner, and worked outward from there. The Hacker News reports that this rules out the earlier log theory. Microsoft's analysis says plainly that it hasn't independently determined how the attacker first got in. So: the most detailed account points at a leaked, unrevoked key, and nobody outside LiteLLM has confirmed it.

If the root cause holds, the lesson is uncomfortable. Force-pushing a secret out of git history doesn't un-leak it. The key sat there for four and a half months before anyone used it.

You didn't have to import it

This is the part most people get wrong. The malicious build dropped files into Python's site-packages that run every time a Python interpreter starts. Your code never had to import litellm. If it was installed in an environment and anything started Python there (a test run, a script, even pip itself), it ran.

Two consequences. First, "we only use LiteLLM in one service" doesn't limit the damage if that service shares a virtualenv, a container base image, or a CI runner with other things. Second, those startup files sit outside the litellm package directory, so pip uninstall leaves them behind. LiteLLM's own advice is not to start with pip install on an affected machine, because that starts Python and runs the malware again. Microsoft says the same: inspect suspect environments from a known-clean system, and don't treat removing the package as recovery.

LiteLLM's guidance is blunt: if 1.107.0 was ever installed on a host with outbound internet access, assume the theft worked.

The bigger problem is how we install AI packages

Most small teams I talk to install AI libraries the way they'd install a font. pip install litellm with no version, a Dockerfile that pulls :latest, a CI job that rebuilds from scratch every morning. That's how a two hour window turns into 49,000 downloads. Nobody chose 1.107.0. Their tooling chose it for them.

And AI packages tend to live in the worst possible places to get popped. The build host has your cloud role. The proxy has every provider key you own. The dev laptop has SSH keys, browser sessions and a .env file with production secrets in it. I wrote about the same pattern with MCP servers: AI tooling is someone else's code running with your keys, and it gets installed with less scrutiny than anything else in the stack.

What a pen test does and doesn't see here

I'll be straight about this. Our $495 test is an external test of your web app. It doesn't read your lockfiles, inspect your dependency tree, or audit your CI pipeline, so it wouldn't have told you that you installed 1.107.0. A dependency scanner and your own install logs are the tools for that.

What it does cover is what an attacker can reach from the internet. That includes an exposed LiteLLM proxy or admin UI sitting on a public URL, known vulnerable components we can fingerprint from outside, and secrets leaking into pages, scripts or error messages. LiteLLM's own trouble started with a key that ended up public. Finding things that ended up public is our job. Here's the full list of what's in and out of scope.

The checklist

  1. Check whether you pulled it. Search lockfiles, CI logs, image registries and package caches for litellm 1.107.0 and for the four Docker tags, between 11:39 and 14:20 UTC on October 21. Compare against the hashes LiteLLM, Datadog and Microsoft published.
  2. Don't clean it with pip. Follow LiteLLM's removal steps from a clean machine, or better, throw the environment away.
  3. Rebuild from known-good artifacts. Fresh virtualenvs and images, pinned to litellm==1.106.2 or a later release you've checked.
  4. Treat every reachable secret as burned. Model provider keys first, then cloud roles and keys, CI secrets, Git and package registry tokens, SSH keys and browser sessions. On a proxy, rotate the stored provider keys, the master key, virtual keys and the database password. Rotate from a clean machine.
  5. Look for use, not just theft. Check CloudTrail for unexpected GetCallerIdentity calls and Bedrock activity, and your provider dashboards for usage spikes after October 21.
  6. Pin everything going forward. Exact versions with hashes in your lockfile, image digests instead of tags like latest. Upgrades become a decision someone makes, not something that happens overnight.
  7. Revoke leaked secrets, don't just delete them. If a key ever hit a public repo, rotate it that day, even if you scrubbed the history.
  8. Check what's public. Run the free scan to see what's visible on your domain, and if you're running a LiteLLM proxy or other AI gateway on the internet, tell us about it when you book a pen test.

Ready to fortify your defenses against cyber threats?

Start Your Penetration Test Now