If you ran pip install litellm or pulled a LiteLLM Docker image on October 21, your machine was almost certainly compromised. Attackers took over the release pipeline of LiteLLM, the widely used Python proxy library that sits in front of LLM providers, and published a poisoned version 1.107.0 that steals credentials and installs a remote code execution backdoor. The malicious wheels were live on PyPI for roughly two hours, from 11:39 to 13:23 UTC, and compromised Docker tags circulated until 14:20 UTC. Security firms flagged the release within minutes, but minutes count for little when the download count is in the millions.
This is the second supply chain compromise of the project this year, and the way this one got in deserves more attention than the malware itself.
What the payload actually did
The compromised artifacts dropped three files into site-packages: cook.pth, cook.py, and sitecustomize.py. Python executes .pth files and sitecustomize.py automatically every time the interpreter starts. That means any Python process in the environment ran the malware, even one that never imports litellm. Running pip triggers it again, which is why the project’s own recovery instructions start with a warning not to begin with pip install.
Per Datadog Security Labs’ analysis, cook.py is a credential collector, self-named “privcollect”, that sweeps for cloud credentials from AWS, Azure and GCP including instance metadata endpoints, Kubernetes and Docker secrets, Vault and Terraform secrets, SSH and GPG keys, .env files, npm and PyPI tokens, browser passwords and cookies, and sessions for AI coding tools like Claude, Codex, Cursor and Copilot. If a LiteLLM proxy database is configured, it queries the tables where the proxy stores upstream provider keys. The collected data is encrypted with AES-256, the key wrapped with RSA-4096, and POSTed to a lookalike domain, www.litellm[.]dev/healthcheck, disguised as a health check call.
sitecustomize.py is quieter. It launches a detached daemon that checks a GitHub-hosted pointer file hourly and downloads and executes Python code from whatever domain the attacker names. In this case the pointer file only ever held test values, so no second stage payload was observed in the wild. That is a lucky break, not a design flaw in the malware.
How the attackers got in
The project’s own incident report reconstructs the chain, and it is a study in how a few stolen credentials defeat a well-defended pipeline. The attackers held GitHub CLI OAuth tokens from three separate developer accounts. One token belonged to a GitHub organization owner, and the attackers used it to disable branch protections and force push a change to the private release workflow. The modified workflow then ran a build against an attacker-controlled source repository named Berri-AI/litellm, one hyphen away from the legitimate BerriAI/litellm. The build pipeline signed and published the resulting artifacts as if nothing were wrong.
LiteLLM’s team suspects the GitHub CLI tokens were harvested from logs of an internet-exposed internal deployment, possibly reached through a recently patched vulnerability. They also identified a contributing factor worth pausing on: in some cases Claude Code embeds a GitHub CLI OAuth token in its response bodies, which LiteLLM then logs. An AI coding assistant’s output may have been the exfiltration channel for the credentials that took down the assistant’s own infrastructure layer.
One detail from the timeline: branch protection actually worked at 10:08 UTC and rejected the unauthorized workflow change. It failed five seconds later, when a privileged credential overrode it. The control was there. The person holding the keys to the control was the problem.
Why signing did not save anyone
After the March incident, which pushed malicious versions 1.82.7 and 1.82.8 through a compromised CI chain, the project hardened its pipeline with a private release repository, Trusted GitHub Publishers, immutable Docker tags and image signing. None of it mattered, because the compromised artifacts were built and signed by LiteLLM’s own legitimate pipeline. As coverage from CSO Online notes, the build provenance attached to the Docker images recorded the malicious fork as the source, in valid, verifiable form.
A valid signature proves an artifact was not altered in transit. It says nothing about whether the code that produced it was trustworthy. Build provenance is only as strong as the binding between the signing workflow and the expected source repository, and here that binding was controlled by whoever held the org owner token that day.
If you might be affected, do this now
The affected artifacts are litellm==1.107.0 on PyPI, and Docker Hub or GHCR tags v1.107.0, latest, main-latest and main-stable pulled during the window, all pointing to compromised image index sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e. The npm package was not affected, and the official v1.107.0 source tag on GitHub is clean. The enterprise private release was not impacted.
Recovery steps, in order:
- Do not start with
pip. Any Python launch in an affected environment re-runs the malware. From the shell, deletecook.pth,cook.py,sitecustomize.pyand matching.pycfiles from every affectedsite-packagesdirectory. - Kill the detached daemon holding
/tmp/remote_code_loader.lock. - Rebuild affected containers and virtual environments from scratch rather than patching in place, and pin to
litellm==1.106.2, the latest clean release. - On any affected host with outbound internet, assume exfiltration succeeded. The absence of the marker file proves nothing; container cleanup removes it.
- Rotate the instance or task IAM role, every provider key in the LiteLLM proxy database plus master and virtual keys, the database password, SSH and GPG keys, and anything else in process environments. Check CloudTrail for
GetCallerIdentityfrom the instance role around install time.
What this means for anyone running an AI stack
Three takeaways. First, dependency hygiene for AI infrastructure needs the same paranoia as for anything internet-facing: pin versions, check digests, and treat unpinned installs of high-value libraries as an incident waiting to happen. LiteLLM processes tens of millions of downloads weekly, and it is a natural target precisely because it holds other people’s provider keys.
Second, lookalike organization names are now an attack surface. One hyphen in a repository name was enough to slip a malicious fork through a pipeline that had already survived one compromise and hardened accordingly. If your release workflow can be pointed at an arbitrary source repo, alert on names that differ from the expected one by a single character.
Third, treat AI assistant tooling credentials as secrets that can leak into logs. The possibility that a GitHub CLI token reached attacker hands via logged response bodies is unconfirmed, but the mechanism is real enough to check in your own proxy logs. If your gateway records response bodies, it may be recording credentials.
Two compromises in one year for the same pipeline, after hardening between them, suggests the defenders are losing this particular race. The lesson for the rest of us is not that signing and Trusted Publishers are worthless. It is that they protect against the last attack, not the next one.