Someone Poisoned LiteLLM: 23M Downloads, 3 Malicious Files


 
Security / Deep Dive

Someone Poisoned LiteLLM: 23M Downloads, 3 Malicious Files

Version 1.107.0 of the AI gateway pip-installed a credential stealer that runs every time Python starts — even if you never import LiteLLM. Here's exactly what happened, and the checklist to know if you're affected.

SECURITY 7 MIN READ UPDATED: OCT 2026
LiteLLM supply chain compromise — malicious 1.107.0 release
The poisoned release lived on PyPI for roughly two hours on Oct 21, 2026 — and on Docker's floating tags even longer. (Image: The Hacker News)

If you use LiteLLM — the popular open-source Python gateway that routes your app's calls to OpenAI, Anthropic, Google, and dozens of other providers — check which version you installed. On October 21, 2026, version 1.107.0 went live on PyPI and Docker carrying something the real project never wrote: a credential stealer plus a remote-code backdoor.

What makes this one genuinely scary isn't just the payload. It's the delivery. The wheels shipped through LiteLLM's own release pipeline, with seemingly legitimate build provenance. Nothing about the download looked wrong. And the attacker's window was tiny — about two hours on PyPI — but with roughly 23 million weekly downloads, two hours is plenty of surface.

Security teams at SafeDep, Datadog Security Labs, Socket, StepSecurity, and Upwind all flagged it the same day. This is the second LiteLLM supply-chain compromise of 2026 — but as I'll explain below, it's not the same attack coming back. It's worse in a different way.

How the malware actually worked

Installing litellm==1.107.0 dropped three files into your Python site-packages directory: cook.pth, cook.py, and sitecustomize.py. Those names are the whole trick.

Python automatically processes .pth files at interpreter startup, executing any line that starts with import. cook.pth contains a single line — import cook — so every Python process on the machine runs the stealer: your app, pip, IDE language servers, CI jobs, everything. Likewise, sitecustomize.py is imported by Python automatically at startup. The upshot, confirmed by multiple researchers: simply having the package installed was enough. You never had to call a single LiteLLM function.

SafeDep's analysis found cook.py is roughly 61 KB of Python wrapped in a single layer of base64+zlib obfuscation. TheHackerNews reported StepSecurity's finding that the stealer calls itself "privcollect." And here's the detail that should make any platform team flinch: deleting the litellm package directory does not remove these files. They sit at the site-packages root, so pip uninstall litellm leaves the malware behind — and the next time python or pip starts, it runs again. Datadog's team confirmed this explicitly.

What the stealer went after

This wasn't a targeted skim of one API key. SafeDep's teardown reads like a "everything on the host" grab. The stealer collected:

  • Cloud credentials — AWS, GCP, and Azure keys, plus AWS instance-metadata credentials pulled straight from the metadata service on EC2-class hosts. Kubernetes, Docker, Vault, and Terraform secrets too.
  • Developer keys — SSH, GPG, and TLS private keys, .env files, shell history, Git/npm/PyPI tokens.
  • CI/CD secrets — GitLab runner configs, Jenkins credentials, Vault tokens.
  • AI tool tokens — sessions for Claude, Codex, Gemini, Cursor, Copilot, Windsurf, and other coding tools. Yes, your AI assistant's own credentials were the prey.
  • Browser data — saved passwords and cookies from Chromium and Firefox profiles.
  • LiteLLM's own gold — on proxy servers, it read DATABASE_URL and dumped the tables holding upstream LLM provider keys and model configs.

Everything was encrypted with AES-256 plus RSA, packed into a tar archive, and POSTed to a lookalike domain, www.litellm[.]dev. A run-once marker (/tmp/.pc_once_72129118c59f5636c0bb) ensured it phoned home one time per host.

The part that should worry you

The second payload, sitecustomize.py, spawned a detached background daemon that woke up every hour, read a server address from a GitHub-hosted dead drop, and downloaded and executed whatever Python the attacker pointed it at. The GitHub account behind the dead drop was deleted within about two hours of the release — Datadog reports no second-stage payload was observed in the wild, and SafeDep couldn't recover the address — but hosts that ran the daemon before the deletion could theoretically have received and run second-stage code. The window for damage was narrow; the uncertainty is the reason remediation has to be thorough.

How the attacker got the official release to sign its own poison

This is the part of the story with the longest tail. According to LiteLLM's incident report (via Datadog and SafeDep), the attacker compromised the GitHub CLI OAuth tokens of three LiteLLM engineers. One account triggered the project's private release workflow; a second approved it. SafeDep notes LiteLLM believes the tokens were taken from the logs of an internal deployment exposed to the internet — an attribution the company says is still unconfirmed.

The clever bit: the attacker pointed the release workflow at a lookalike fork, Berri-AI/litellm, which differs from the legitimate BerriAI/litellm by a single hyphen. Datadog's writeup says the near-identical name appears designed to evade casual detection by a human reviewer. Branch protection was overridden, the official pipeline built the wheels and Docker images, and — here's the nightmare for anyone relying on build provenance — the provenance metadata faithfully recorded the malicious fork as the source. It looked legitimate because, from the pipeline's point of view, the process was legitimate.

Oliver Jensen, LiteLLM's director of security, told TheHackerNews that all involved tokens have been revoked, all but one unaffected user has been removed from the GitHub organization, and trusted links to the GitHub org have been stripped from public registries. The original attack vector for the token theft itself is still under investigation.

What to do right now: the checklist

If your team, your CI, or your containers touched LiteLLM recently, walk through this — in order. Skip nothing.

  1. Check for 1.107.0 everywhere. Inspect lockfiles, caches, pip caches, and running container image digests for litellm==1.107.0. Remember the floating Docker tags — latest, main-latest, main-stable served the poisoned image for part of Oct 21, so latest pullers are in scope even if they never typed a version number.
  2. Look for the implants on every affected host. Check site-packages for cook.pth, cook.py, and an unexpected sitecustomize.py. Warning signs: the /tmp/.pc_once_72129118c59f5636c0bb marker, the /tmp/remote_code_loader.lock lockfile, and egress traffic to litellm[.]dev.
  3. Remove the three files manually — pip uninstall is not enough. They live at the site-packages root and survive package removal. Kill any detached loader daemon, then clean.
  4. Rebuild or revert to a clean version. Safe versions are 1.106.2 and earlier, plus the 1.107.0rc1/rc2 release candidates. Pin by hash or digest. (The companion packages litellm-proxy-extras==0.4.110 and litellm-enterprise==0.1.78 that 1.107.0 pinned are clean, per SafeDep.)
  5. Rotate every credential the host could see — from a clean machine. Cloud keys, SSH keys, CI secrets, AI provider keys, browser-saved passwords, the works. Don't rotate from the infected host.
  6. Audit logs for any use of exposed credentials during the exposure window.
23M LiteLLM's approximate weekly PyPI downloads — the blast radius for a 2-hour window
~2h How long the poisoned wheels were live on PyPI (11:39–13:23 UTC, Oct 21)
3 Malicious files dropped at the site-packages root — surviving pip uninstall
7 Poisoned wheels across Linux, macOS, and Windows — plus poisoned Docker tags
3 Engineers' stolen GitHub CLI OAuth tokens the attacker chained through the release pipeline
1 The hyphen that separated the lookalike fork (Berri-AI) from the real one (BerriAI)

Why this one is a template, not an exception

This is the second LiteLLM supply-chain compromise of 2026. Back in March, versions 1.82.7 and 1.82.8 were hit via a stolen PyPI token in what Datadog linked to TeamPCP activity — same startup-hook technique, different infrastructure. Datadog says no connection has been established between the two incidents. Twice in one year tells you something: AI gateway projects are high-value targets because they sit exactly where the keys are. A proxy that routes your calls to OpenAI, Anthropic, and Google holds every provider's API keys by design.

The deeper lesson is the one Datadog spells out plainly: the attacker didn't hack the build system — they used stolen developer tokens to point a trusted pipeline at the wrong source, and the pipeline produced legitimate-looking provenance. Locking release workflows to approved repositories, and treating developer CLI tokens as the crown-jewel secrets they are, is no longer optional hygiene. It's the whole game.

Frequently asked questions

How do I know if I installed the poisoned LiteLLM?

Check your lockfiles, pip caches, and running container digests for litellm==1.107.0 — and don't forget the floating Docker tags (latest, main-latest, main-stable) that served the poisoned image on Oct 21. On hosts, look for cook.pth, cook.py, or an unexpected sitecustomize.py in site-packages, the marker file /tmp/.pc_once_72129118c59f5636c0bb, and any network egress to litellm[.]dev. A missing marker does not prove the host is clean.

Is uninstalling litellm 1.107.0 enough to remove it?

No. The three payload files sit at the site-packages root, outside the package directory, so pip uninstall litellm leaves them in place — and any running loader daemon keeps going. You must manually delete cook.pth, cook.py, and sitecustomize.py, kill the daemon, rebuild the environment, and rotate every credential the host could read.

Which LiteLLM versions are safe?

Version 1.106.2 and earlier are safe, as are the 1.107.0rc1 and 1.107.0rc2 release candidates. The companion packages that 1.107.0 pinned — litellm-proxy-extras==0.4.110 and litellm-enterprise==0.1.78 — are also clean, per SafeDep's analysis.

Did the hourly backdoor actually download anything?

As reported, no second-stage payload was observed in the wild. The GitHub dead-drop account behind the backdoor returned a 404 within about two hours of the release, and SafeDep could not recover the server address it once pointed to. But hosts that ran the daemon before the deletion could theoretically have received second-stage code — which is exactly why full remediation and credential rotation are recommended regardless.

Is this the same attack as the March 2026 LiteLLM compromise?

No. The March incident affected versions 1.82.7 and 1.82.8 via a stolen PyPI token in activity Datadog linked to TeamPCP. The 1.107.0 attack used a similar startup-hook technique but different infrastructure, and Datadog reports no connection has been established between the two.

The bottom line

A package with 23 million weekly downloads shipped a credential stealer through its own official release pipeline, signed and provenance-stamped, off a fork that was one hyphen away from the real thing. The window was two hours. The cleanup is manual. And the fix isn't a new version — it's a posture: pin your dependencies by hash, lock release pipelines to approved repos, treat developer tokens like production secrets, and assume the next pip install in your CI could be the one that counts.

If this story made you want to go audit your lockfiles, that's the right instinct. Go do it now.



×