LiteLLM 1.107.0 Shipped a Credential Stealer: Find It, Clean It, Rotate

Written by the Rafter Team

As of October 22, 2026, 21:00 UTC. LiteLLM's incident post was last updated October 21 at 20:30 UTC; later updates are on GitHub, the latest October 22 at 15:54 UTC. The investigation continues.
If any environment installed litellm==1.107.0, or started a container from a LiteLLM image between 11:46 and 14:20 UTC on October 21, treat it as compromised.
Rotate everything it could reach, starting with the provider API keys your proxy holds, because the malware went looking for them by name.
What happened
On October 21 an attacker used LiteLLM's own release workflow to publish a poisoned 1.107.0. LiteLLM says the release "contained compiled malware which steals credentials and loads remote-code execution tools." The bad artifacts:
- PyPI: all seven
litellm==1.107.0wheels, live from 11:39:51 to 13:23 UTC. PyPI now lists 1.106.2 as the latest release. - Docker Hub:
v1.107.0,latest,main-latestandmain-stablepointed to the compromised image indexsha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339efrom 11:46:56 to 14:20 UTC. - GHCR: the same four tags on
litellm,litellm-databaseandlitellm-non_root, until they were made unavailable by 13:51 UTC.
The v1.107.0 tag on GitHub is clean; the artifacts "were built from an attacker-controlled repository."
Earlier versions are not affected, and neither is LiteLLM's private enterprise registry.
How it got in
After a compromised release in March, LiteLLM moved releases to a private repo, published through Trusted Publishers, made its Docker release tags immutable and started signing images. The attacker went around all of it with stolen tokens.
LiteLLM's account, which it says it is still confirming:
- The attacker held GitHub CLI OAuth tokens for three developers.
- One token belonged to a GitHub organization owner. It was used to override branch protection and force-push a change to the private release workflow.
- The changed workflow built from a lookalike repository,
Berri-AI/litellminstead ofBerriAI/litellm, and "signed and published the poisoned artifacts." - A second developer's credential approved the production deploy, and a third deleted the workflow run records afterward.
The tokens came from a log. LiteLLM found an unexpected user account on its own internal LiteLLM deployment, which was exposed to the public internet, and believes the attacker read the tokens from that deployment's logs. It says it has "observed specific situations in which Claude Code will embed a user's GitHub CLI OAuth token into a response body," and its deployment logged response bodies. LiteLLM has not described those situations. It suspects the attacker got into the deployment through "a recently patched vulnerability," but has not confirmed how. While that stays open, it is revoking or rotating every authentication token in its organization.
Checking signatures would not have caught it. The release workflow signs whatever it builds, so the poisoned images carried LiteLLM's real signature.
Why "nothing imports it" doesn't help
Each 1.107.0 wheel dropped three files into the root of site-packages: cook.pth, cook.py and sitecustomize.py.
The technical analysis LiteLLM links to, by Upwind's research team, shows cook.pth holds one line: import cook.
Python's site module reads every .pth file in site-packages when the interpreter starts, and lines starting with import are executed.
The same module imports sitecustomize at startup.
So every Python process in that environment ran the payload: a test runner, a script that never touches LiteLLM, and pip itself.
That is why LiteLLM says not to start with pip install; reinstalling reruns the malware.
This is the same trick as March, when 1.82.8 shipped a litellm_init.pth.
Per Upwind, cook.py collects SSH keys, cloud and Kubernetes credentials, browser passwords, .env files, CI and package-registry tokens, AI coding tool configs, the environment of every readable process, and cloud instance credentials, which it checks with an STS GetCallerIdentity call.
If DATABASE_URL is set, it dumps the proxy's credentials and model tables: every upstream provider key the proxy stores.
It encrypts the bundle and posts it to http://www.litellm.dev/healthcheck.
litellm.dev is not the project's domain, which is litellm.ai.
sitecustomize.py starts a daemon that checks a file in the GitHub repo letters9813/pythonclient-test every hour, and when the file names a domain, fetches code from it and runs it.
Upwind found the file empty, so that stage is dormant until the operator fills it in.
The gateway is the prize
An AI gateway holds keys to every model provider a company pays for, plus the master and virtual keys it hands to its own users. One compromised proxy host gives an attacker all of them.
The entry point this time was a gateway too. LiteLLM's own internal deployment sat on the internet, and its logs held coding-agent traffic with GitHub tokens in it. A gateway logs what passes through it; when coding agents pass through, the log holds their credentials. That is the pattern in the coding agent is the supply chain: the agent's access becomes the attacker's access.
What to do now
- Find it. Run
pip show litellmin every venv, image and CI cache. Search disks forlitellm-1.107.0.dist-info,cook.pthandsitecustomize.pyin site-packages. Check internal PyPI mirrors too; PyPI's removal does not reach them. For containers, comparedocker images --digestsand running containers against thesha256:1bbcb144…index above. A container started in the window still runs the bad image even though the tag has moved. Per LiteLLM, finding a file confirms exposure; not finding one proves nothing. - Clean before you reinstall. From the shell, not Python, delete
cook.pth,cook.py,sitecustomize.pyand any matching__pycache__/cook*.pycandsitecustomize*.pycfrom every affected site-packages. Kill the detached Python daemon holding/tmp/remote_code_loader.lock. Rebuilding the image or venv from scratch is better. - Rotate. If 1.107.0 ran on a host with outbound internet, assume the data left. LiteLLM's list: the instance or task IAM role, every provider key in the proxy database, the master and virtual keys, the
DATABASE_URLpassword, and any secret in any process environment on the host. Add the host's SSH keys and its GitHub, npm and PyPI tokens. - Hunt. Check CloudTrail for
GetCallerIdentityfrom the instance role around install time, then for that role's credentials used from outside your network. Search DNS and proxy logs forlitellm.dev, and block it at egress. Upwind says a marker file,/tmp/.pc_once_72129118c59f5636c0bb, is written only after a successful upload; its absence proves nothing. - Pin. Use
litellm==1.106.2, or the image by digest:v1.106.2on Docker Hub and GHCR resolves tosha256:7294b90f868c5c38c99b83021c7ba46f9b439e95f8e32964c925c7d2563a03f7, whichlatestandmain-stablealso point to today. Stop tracking moving tags. No newer release exists: LiteLLM says its registries "remain locked to new releases, and regular LiteLLM releases remain paused." When releases resume, do not take the next one automatically until LiteLLM publishes the pipeline changes it has promised. - If you run an older proxy, the pin is also a patch. CISA's Known Exploited Vulnerabilities catalog lists three LiteLLM bugs: CVE-2026-42208, a SQL injection in API key checks fixed in 1.83.7; CVE-2026-42271, command execution through MCP test endpoints, fixed in 1.83.7; and CVE-2026-59822, an MCP authentication bypass fixed in 1.84.0. 1.106.2 carries all three fixes.
- Keep the proxy off the public internet. LiteLLM says a fix for another vulnerability is embargoed until October 27 and ships only through its enterprise registry for now. No advisory for it has been published yet, and LiteLLM has not said whether open-source 1.106.2 includes the fix.
Where Rafter fits
Rafter does not detect this malware, and no check on your own code would have stopped a poisoned wheel from a trusted publisher.
What rafter secrets . can do, locally and with no account, is find provider keys left in repos and .env files, one of the places this stealer looked.
Fewer keys on disk means less to rotate after a night like October 21, though keys in process environments and the proxy database were taken all the same.