LiteLLM 1.107.0 Compromised: Check, Clean and Rotate

TL;DR
LiteLLM 1.107.0 on PyPI shipped a credential stealer and a backdoor for under two hours on October 21. How to check if you ran it, what to clean first, and what to rotate.
Update, October 21: LiteLLM's own incident post says the release pipeline was compromised through three engineers' GitHub accounts, and that the investigation is still open. This post reflects what the vendor and SafeDep had published as of the evening of October 21.
If you run LiteLLM as your model gateway, the question for today is whether any Python process on a host or container ever started with litellm==1.107.0 installed, or pulled the latest Docker tag, in the windows on October 21, 2026 that LiteLLM lists (PyPI 11:39 to 13:23 UTC, Docker tags 11:46 to 14:20 UTC). If yes, treat the machine as compromised and rotate credentials. If you pinned to an earlier version, you are not in the affected set.
This is the second time in 2026 that a LiteLLM release has shipped credential-stealing code. The first, in March, was a 938-point Hacker News thread about versions 1.82.7 and 1.82.8. The earlier incident is context here, not the subject: I only opened the thread's headline, so the details of that attack are not covered below.
What the primary sources say#
Two sources carry the facts. LiteLLM's incident post is the vendor account. SafeDep's analysis is an independent teardown of the wheels. They agree on the shape and differ on one detail, which matters.
What shipped. LiteLLM says PyPI wheels for 1.107.0 were live from 11:39 UTC to 13:23 UTC, and that the Docker images v1.107.0, latest, main-latest and main-stable pointed at a compromised image from 11:46 UTC to 14:20 UTC. The official v1.107.0 tag on GitHub does not contain the malicious files; per the vendor, the artifacts were built from an attacker-controlled repository.
What it does. SafeDep decoded the payload. Installing the wheel drops three files into site-packages: cook.pth, cook.py and sitecustomize.py. A .pth file containing an import line runs at every Python start, so the stealer runs in every Python process on the host, not only ones that import litellm. SafeDep lists the stealer's targets: SSH keys, shell history, cloud credentials (AWS, Azure, gcloud, kube config, Docker config), CI/CD secrets, and the config and token files of AI developer tools including Claude Code, Cursor, Codex, Gemini CLI and opencode. On a LiteLLM proxy it also dumps the credentials table, which holds the provider API keys. A second payload starts a background daemon that, per SafeDep, fetches a server address from a GitHub file every hour and executes whatever code that server returns. The attacker's GitHub account returned 404 about two hours after the release, so the daemon found nothing to fetch at the time of SafeDep's check, but hosts that ran it earlier could already have received second-stage code.
How it got in. This is where the two sources differ. SafeDep, working only from public artifacts, assessed that BerriAI's private release pipeline built the wheels from attacker-modified source, and called a stolen PyPI token a worse fit for the evidence. LiteLLM's account is more specific: attackers appear to have used stolen GitHub CLI OAuth tokens belonging to three engineers, one to modify the release workflow and trigger the run, another to approve the production gate, and a third to delete workflow records. The vendor also says it has not yet found how the tokens were obtained, so it cannot rule out further access, and as a stopgap it removed all but one unaffected user from its GitHub organization.
One more detail from the vendor: customers on LiteLLM's private enterprise registry were not affected, and that registry is distributing an embargoed fix for a separate vulnerability scheduled for disclosure on October 27. We have no way to verify the contents of that fix.
What to do now#
The order matters, because running Python can re-trigger the payload. This follows LiteLLM's remediation steps, with the detection strings from SafeDep.
1. Find exposure without starting Python. From a shell:
find / \( -name cook.pth -o -name 'litellm-1.107.0.dist-info' \) 2>/dev/null
ls /tmp/.pc_once_* /tmp/remote_code_loader.lock 2>/dev/null
The first line looks for the files the vendor tells you to search for. The second checks the marker files SafeDep documents: /tmp/.pc_once_72129118c59f5636c0bb means the stealer finished an upload, and /tmp/remote_code_loader.lock means the backdoor daemon has run. Both vendor and SafeDep say a missing marker proves nothing, since containers and tmp cleanup erase them. sitecustomize.py is also a legitimate filename that other tooling can install, so a hit there needs a look at its contents, not an automatic verdict.
2. Clean before you reinstall. Delete cook.pth, cook.py, sitecustomize.py and the matching __pycache__ files from every affected site-packages, and kill the detached Python daemon holding the lock file. LiteLLM's guidance is explicit: do not start with pip install, because pip is itself a Python process. Better still, rebuild images and virtual environments from scratch.
3. Pin and rebuild. The vendor's instruction is to pin to litellm==1.106.2 or any explicit version published before the incident. For containers, stop using floating tags: reference an image digest you have verified, not latest.
4. Rotate by blast radius. If 1.107.0 was ever installed on a host with outbound internet, the vendor says to assume exfiltration succeeded. Rotate the instance or task IAM role, every provider key stored in the proxy database plus the master and virtual keys, the DATABASE_URL password, and any secret present in the environment of any process on the host. If you ran a coding agent on the same machine, add its tokens to the list, since SafeDep documents the stealer reaching for those config files. LiteLLM also suggests checking CloudTrail for GetCallerIdentity calls from the instance role around the install time, then for use of that role's credentials from outside your infrastructure.
The harder question: should the gateway be a dependency at all#
The incident lands on a design choice many agent teams made quietly: put one Python proxy in front of every model key. That makes the gateway the single most valuable thing on the host. It holds provider keys in its database, runs next to CI secrets and agent configs, and updates frequently. A compromise of that one package is a compromise of every credential it brokers.
That is not an argument that gateways are bad. Fallbacks, spend limits and a stable model name are real reasons to run one, and a commenter on the Hacker News thread described exactly those. It is an argument for treating the gateway like a privileged system, which our self-hosted vs managed gateway guide frames as an operations decision, not a library choice. Three concrete moves:
- Isolate it. Run the gateway on a host or container with nothing else in it: no developer config files, no agent tokens, no CI runner state. The stealer's target list is mostly things that should never share a machine with a proxy. Our agent config files post covers why those files are an attack surface of their own.
- Delay adoption of new releases. uv's documentation says its
exclude-newersetting accepts an RFC 3339 timestamp or a duration such as1 week, so a resolver can ignore anything published after a cutoff. A one-week window would have excluded 1.107.0, which was live for under three hours. Verify the exact flag against your uv version before rolling it out. The tradeoff is real: a cooldown also delays security fixes, a point several Hacker News commenters raised. - Pin by hash, and know what hashes do not prove. A pinned, hash-checked lockfile stops a floating install from picking up a surprise release. It does not tell you the published wheel matches the public source. In this incident the release tag and the shipped artifact diverged, and one HN commenter pointed out that provenance attestations would not have caught it either, since they prove which workflow published the wheel, not what source it checked out. Rebuilding from the public tag and comparing is the check that works, and almost nobody does it.
The wider pattern is the one we covered in the TanStack npm compromise and the Mastra package takeover: the attacker does not break the package, they break the release path, and a maintainer's developer token is the weakest link.
What people are actually saying#
The main Hacker News thread was at 263 points and about 100 comments when I read it. The comments split roughly three ways.
- Anger about the repeat. The dominant reaction, paraphrased, is that once was forgivable and twice is a pattern. Several commenters said they had already moved off LiteLLM after the March incident, and one said anyone still on it would struggle to justify that now.
- Quiet advice that holds up. The most practical thread is about cooldowns. One commenter argued package managers should default to a 24 to 48 hour delay. A commenter who runs a 7-day cooldown on an internal PyPI proxy noted it also blocks security updates, which they now have to exempt by hand. The counter-case, from another commenter, is that if everyone delays, nobody is the canary, and that many supply chain attacks have survived undetected for months. The story's submitter, who is affiliated with SafeDep, replied that a 24 hour cooldown would have been enough here because the attack used a newly registered look-alike domain and a known
.pthtechnique, which is a fair point about this incident and not a guarantee about the next one. - The enterprise-registry controversy. Commenters objected to the vendor saying a private registry carries an embargoed fix for an undisclosed vulnerability, and that only paying customers are shielded from the compromise. Some read it as a bad look and some as ordinary embargo practice, similar to how major vulnerabilities are distributed to vendors before public fixes. I take no side; the vendor's own post is the only source for what the private release contains.
There was also the predictable "just write your own proxy" camp, including commenters pointing to small Go and Rust alternatives they built or maintain. Treat those as the authors' own projects, not endorsements. A replacement gateway is another dependency to audit, and a commenter who runs one at work listed what the gateway buys you: resilience to upstream outages, centralised config and visibility into usage and cost. That value does not disappear when the supply chain gets uncomfortable.
What I would change this week#
Pinning to 1.106.2 is the right immediate move, but it is a stopgap, because you are now relying on a maintainer team that removed nearly all members from its own GitHub organization and has not yet found how the tokens were stolen. Watch the vendor's incident post, which the vendor says it will update every two hours, and the issue they link. If you need to decide whether to stay or move, the AI gateway decision guide lists the alternatives and what each one asks of your operations team.
Continue Reading#
- Self-Hosted vs Managed AI Gateways: A Decision Guide - when to run a gateway yourself and what it costs in operations
- Agent Config Files Are Executable Supply Chain - why the files this stealer targets deserve their own threat model
- TanStack's npm Compromise Is the CI Lesson Agent Teams Needed - the release-path attack pattern on npm
- Mastra npm Supply Chain Attack - a scope takeover on an AI agent framework
- The Agent Security Checklist I Use Before Connecting Tools - a pre-flight list for anything that touches your keys
Sources#
- LiteLLM: Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised - vendor incident post, timeline and remediation steps
- SafeDep: Compromised LiteLLM 1.107.0 on PyPI Installs a Credential Stealer and Backdoor - independent analysis, payload details and indicators
- Hacker News: Compromised LiteLLM 1.107.0 thread - community discussion
- Hacker News: Tell HN: Litellm 1.82.7 and 1.82.8 on PyPI are compromised - the March incident thread
- uv settings reference -
exclude-newertimestamp and duration formats
Get the next deep dive like this in your inbox
One email a week on Security and the rest of the AI dev stack. Free.
Read next
Self-Hosted vs Managed AI Gateways: A Decision Guide
Self-host LiteLLM or Kong, or use a managed gateway like Portkey, OpenRouter, or Cloudflare AI Gateway? A factual breakdown of cost, control, and ops tradeoffs.
11 min readAgent Config Files Are Executable Supply Chain
Agent config files like Claude Code hooks, Cursor rules and VS Code tasks can trigger code. Why they need review like dependencies, and what to check first.
8 min readTanStack's npm Compromise Is the CI Lesson Agent Teams Needed
The TanStack npm incident was not just a package-security story. It was a reminder that AI agent workflows inherit every weak trust boundary in CI.
9 min readNew here? Start with
Technical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.





