LiteLLM got backdoored again. Check if it took your keys in 5 min

On this page · 12 sections
- What happened, in 2 hours
- The 5-minute check (don't run pip first)
- 20 of 30 AI frameworks would have let it in
- Twice in 7 months, and the second time after "hardening"
- LiteLLM blamed Claude Code. I checked 1,193 transcripts
- Railway, same day, same target
- The 15-minute rotation order
- What it wanted from a normal dev box
- The worst take of the day
- The numbers so far
- My bet, and your turn
- Rotate every key it touched, in order, tonight
TL;DR: You can find out in 5 minutes whether today's LiteLLM backdoor (or Railway's leak) touched your machine, and rotate what it could have taken in about 15. The commands and the order are below. Why it matters: for 1 hour 43 minutes this morning, pip install litellm handed you a credential stealer, and I checked 76 popular AI packages against the versions they had live at that moment. 20 of the 30 that use LiteLLM would have accepted it.
This is the second time in 7 months. LiteLLM's own status update now blames tokens that Claude Code put into responses. So I scanned 1,193 of our own Claude Code transcripts for GitHub tokens. I found 0. My read: the hole wasn't Claude Code. It was a logging proxy sitting on the open internet.

What happened, in 2 hours
All times ET, Oct 21 2026, from LiteLLM's incident post, GitHub issue #47826 and SafeDep's teardown:
- 7:39 AM:
litellm==1.107.0lands on PyPI, all seven wheels, Linux, macOS and Windows. - 7:46 AM: the Docker tags
v1.107.0,latest,main-latestandmain-stablestart pointing at the same poisoned image. - 8:22 AM: JFrog flags it. Socket follows at 10:03 AM.
- 9:23 AM: PyPI pulls the release. Docker Hub stays poisoned until 10:20 AM.
- 3:14 PM: Wiz tells LiteLLM it has started to see post-exploitation activity.
- 5:22 PM: LiteLLM posts the attack chain and points at Claude Code.
- 6:05 PM: a different fire. "Railway has been hacked." More on that below.

Plain English for what the package did. A .pth file is a tiny file Python reads every time it starts. The bad wheel dropped one called cook.pth that runs a stealer, plus a sitecustomize.py that starts a background process and phones home every hour for new code. You didn't have to import LiteLLM. If any Python process started on that machine, the stealer ran: your app, your IDE's language server, your CI job, even pip itself.
The 5-minute check (don't run pip first)
LiteLLM's own incident page says "You can check with pip show litellm". The same page, a few lines later, says any Python start in an affected environment runs the stealer again, "and that includes running pip." So don't. Check from the shell first. None of these commands start Python.
# 1. Is the bad version or its implant anywhere on this machine?
find / -path /proc -prune -o \( -name 'cook.pth' -o -name 'litellm-1.107.0.dist-info' \) -print 2>/dev/null
find / -path /proc -prune -o -path '*site-packages/sitecustomize.py' -size 25509c -print 2>/dev/null
# 2. Did it already run? (the stealer's marker and the backdoor's lock file)
ls -la /tmp/.pc_once_72129118c59f5636c0bb /tmp/remote_code_loader.lock 2>/dev/null
# 3. Lockfiles and requirements that pinned or resolved to it
grep -rnE 'litellm[^0-9]*1\.107\.0' --include='*.lock' --include='requirements*.txt' --include='pyproject.toml' . 2>/dev/null
# 4. Docker: the poisoned image index starts with sha256:1bbcb1443bc9
docker images --digests | grep -i litellm
On Windows, run this in PowerShell instead of step 1:
Get-ChildItem C:\ -Recurse -Force -ErrorAction SilentlyContinue -Include cook.pth,litellm-1.107.0.dist-info | Select-Object FullName
Then two questions only you can answer. Did any CI job, Dockerfile or fresh venv install LiteLLM without an exact pin between 7:39 and 9:23 AM ET? Did anything pull litellm/litellm:latest or main-stable between 7:46 and 10:20 AM ET? If yes to either, treat that machine as compromised, even if step 2 finds nothing. LiteLLM says it plainly: containers and tmp cleanup remove the marker, so a clean result proves nothing.
How big was that window? At yesterday's pace, LiteLLM got 3,650,152 downloads in a day, about 152,000 an hour. Not all of those resolve to the newest version. A lot of them do.
20 of 30 AI frameworks would have let it in
This is the experiment I ran tonight. I took 76 popular AI and agent packages on PyPI. For each one I found the release that was live at 7:39 AM ET, the minute the bad wheel went up, and read its declared LiteLLM requirement. Then I asked one question: does that range include 1.107.0?
- 30 of the 76 depend on LiteLLM.
- 8 accept 1.107.0 on a plain install: mini-swe-agent, dspy, langchain-litellm, guardrails-ai, llama-index-llms-litellm, gpt-researcher, open-interpreter and praisonai.
- 12 more accept it through an optional extra, like
pip install "crewai[litellm]": strands-agents, openai-agents, google-adk, crewai, agno, smolagents and six others. - 10 block it, only 3 of them with an exact pin.
The most-downloaded one is the best story. strands-agents does 37.1M downloads a month. At 7:39 AM its [litellm] extra allowed anything under 2.0. At 2:47 PM ET it shipped a new release capped at <1.107.0, seven hours after the fire started.

And here's the part that should make every maintainer wince. mini-swe-agent (11.6M downloads a month) learned from March. Its requirement says litellm!=1.82.7,!=1.82.8,>=1.75.5, which excludes the two versions backdoored last time by name. That blocklist let today's backdoor straight in. A blocklist only protects you from the last attack. The packages that were safe today were safe because of an upper bound or an exact pin, not because anyone remembered March.
Twice in 7 months, and the second time after "hardening"
On March 24 2026, versions 1.82.7 and 1.82.8 shipped a stealer through a compromised maintainer account and the CI pipeline. Version 1.82.8 used a file called litellm_init.pth. Same trick, same package, same targets.

LiteLLM's 5:22 PM update says that after March it "meaningfully hardened" the release pipeline: a private release repo, Trusted Publishers, immutable Docker tags, signed images, and publishing locked to "an account locked to a specific laptop physically located within our office." Then, by its own account, it widened release permissions on Monday for daily backports. The attacker had GitHub CLI tokens for three engineers, one of them an organization owner, disabled branch protection, force-pushed a change to the release workflow and pointed it at a lookalike repo, Berri-AI/litellm. The signed, official pipeline then signed and shipped the malware.
That's the lesson nobody wants to hear. Signing proves the package came out of your pipeline. It doesn't prove your pipeline was still yours.
LiteLLM blamed Claude Code. I checked 1,193 transcripts
Word for word from the status update: "we have observed specific situations in which Claude Code will embed a user's GitHub CLI OAuth token into a response body, and in situations where response bodies are logged, these tokens may appear in LiteLLM logs." They believe the attacker got into an internal LiteLLM deployment "exposed to the public internet," read the tokens out of its logs, and used them on the release workflow.
Our agent team runs on Claude Code, so I tested it on our own box. Every prompt, tool call and tool result Claude Code has seen here is in ~/.claude/projects: 1,193 transcript files, 3.1 GB. I counted credential-shaped strings with regexes and never printed a value.

Zero GitHub CLI tokens. Zero personal access tokens. Zero Anthropic or OpenAI keys. Two AWS-key-shaped strings, and one of those is Amazon's own documentation example. So in our logs, the thing LiteLLM describes never happened once.
I'm not saying it can't happen. If you ask an agent to run gh auth token, the token is in the response, and every proxy that logs response bodies now has it. But "Claude Code embedded a token" is the least interesting sentence in that update. The interesting one is that an AI gateway holding full response bodies was reachable from the internet. LiteLLM sells that gateway. Logging every prompt and response is a feature people turn on for cost tracking and debugging. My read: tonight a lot of teams are finding out their LLM logs are the juiciest credential store they own.
Railway, same day, same target
At 6:10 PM ET Railway's status page linked a security notice: an unauthorized party "may have accessed encrypted environment variables and, for some of them, obtained the decrypted contents." Then this line: "We believe this is part of a series of attacks recently targeting platform companies with the aim of compromising AI inference providers."

Read that twice. The target in both of today's stories is the same thing: the keys that buy AI inference. There's a name for reselling stolen AI keys, LLMjacking, and the reason is simple. A stolen database password needs work to turn into money. A stolen model key is money the minute someone sends a request on your bill. Look at the stealer's own module list: there's a module just for AI tools (Claude Code, Codex, Cursor, Gemini CLI, Copilot, Hugging Face) and one that dumps the provider keys out of a LiteLLM proxy database.
Railway users have it worse tonight. Builds are paused, so you can't redeploy with new keys. One user posted that his leaked keys were being used and he couldn't swap them. The fix is ugly but it works: revoke the key at the provider anyway. Your app breaks until builds come back. The bleeding stops now.
The 15-minute rotation order
If the check above says you were exposed, or you're on Railway, rotate in this order. It's sorted by how fast each secret turns into damage.
- AI provider keys (OpenAI, Anthropic, OpenRouter, Bedrock, Vertex). They're cash within minutes. Revoke, reissue, and set a hard monthly spend cap while you're in the dashboard. Then check usage since 7:39 AM ET.
- Anything that can publish code: GitHub (revoke the "GitHub CLI" app under Settings, Applications, Authorized OAuth Apps, plus PATs and deploy keys), PyPI, npm and crates.io tokens. This is how today's attack spread. Don't be the next one.
- Cloud credentials: AWS keys and instance roles (the stealer reads the instance metadata service), gcloud, Azure, kubeconfig.
- Payments: Stripe and anything with a live key.
- Databases: every
DATABASE_URL, especially a LiteLLM proxy database. - SSH keys: remove them from GitHub and your servers, then make new ones.
- Your browser's saved passwords: the stealer decrypts Chrome's and reads Firefox's. Email first, then anything with money in it.
Then rebuild the affected venvs and images from scratch, pinned to litellm==1.106.2 or older.
What it wanted from a normal dev box
LiteLLM isn't installed on our machine, and there are 0 implant files in the 5 Python environments we found on it. But I wanted to know what this stealer would have found if it had run here. So I checked its target list against our dev box, existence only, nothing opened.

10 of 23 places it looks were full, plus 9 .env files sitting in top-level project folders. That's one person's Windows PC. Yours is probably worse if you have cloud CLIs installed. This is why "I never imported litellm" isn't a defense. It only had to be installed next to anything that starts Python.
The worst take of the day
A few hours in, @KyeGomezB posted "LiteLLM got compromised AGAIN. Maybe it's time to rethink your stack and switch to RouteHub, built by @swarms_corp."

Funny thing, Kye. In our scan, your own swarms package requires litellm==1.76.1. That exact pin is the only reason swarms users were safe today. You had the best security story of the day and used it to sell a router. The lesson isn't "switch libraries." Any library you install can be the next one. The lesson is pin exactly, update on purpose, and keep secrets out of places Python can read on startup.
The numbers so far
- Bad window on PyPI: 1 h 43 min (7:39 to 9:23 AM ET). Docker: about 2 h 33 min (7:46 to 10:20 AM ET).
- LiteLLM downloads: 3,650,152 the day before (pypistats.org), 92.2M in the last month.
- AI packages checked: 76. Depend on LiteLLM: 30. Accepted 1.107.0: 20 (8 plain, 12 via an extra). Blocked: 10, exact pins: 3.
- Claude Code transcripts scanned: 1,193 (3.1 GB). GitHub tokens found: 0.
- Stealer targets present on our dev box: 10 of 23, plus 9 .env files.
- LiteLLM backdoors on PyPI in 2026: 2 (March 24, October 21).
My bet, and your turn
Prediction: by March 31 2027, another AI package in PyPI's top 1,000 ships a credential stealer through its own official release channel, and the stolen keys get used for inference before anyone posts about it. AI keys are the new crypto wallets. If I'm wrong, I'll write the correction with your name in the headline.
Your turn: exact pins plus a bot that bumps them, or version ranges plus a lockfile? Pick one and tell me why in the replies. @LiteLLM, you're welcome to answer too.
Rotate every key it touched, in order, tonight
The hard part of a night like this isn't revoking one key. It's remembering every key you have, where each one lives, and who else has a copy. The API Key and Secret Rotation Tracker ($5, Google Sheets and Excel) gets you there: every API key, token and password in one sheet, with where it lives, who has it, when it was last rotated, and a rotate-first list with what to update next. Names and locations only, never the secret values. A password manager stores secrets. This tells you which ones are overdue. $5 once, no account, no subscription.
Guaranteed: if it doesn't track your secrets and flag the overdue ones, email support@tensorust.com and we fix it until it does. A real person replies within one business day.
Related on tensorust.com: LLM proxy benchmark: 200 duplicate calls became 1, Theo's 60% stat is an outage chart, and DeepSeek gave away its engine. The $38-an-hour parts stayed home.
Method and data: package scan run 7:45 to 7:55 PM ET Oct 21 2026 against PyPI's JSON API; for each of 76 packages, the newest non-pre-release uploaded before 11:39:51 UTC, its requires_dist read with Python's packaging library, and a range counted as accepting if it contains 1.107.0 (dev and test extras excluded). Monthly downloads from pypistats.org. Transcript scan 7:33 to 7:40 PM ET over ~/.claude/projects, regex counts only. Dev box check: file existence only. X screenshots captured 7:34 PM ET, real and unedited. Incident facts from LiteLLM's incident post and GitHub issue #47826 (updates through 21:22 UTC), SafeDep, Railway's notice and status page (22:45 UTC update), NetSPI and Snyk for March.
First published here on tensorust.com on Oct 21, 2026. Also on X (@MattBusel) and dev.to. Related: LLM proxy benchmark, Claude Code vs Codex outages, DeepSeek DeepInfer, measured.
Get the next one by email.
What my AI agents tried this week, with the real numbers: what worked, what flopped, what made money. One email when there is something worth reading.
No spam, never sold. Every email has a one-click unsubscribe.
Read next
Free tool: LLM API cost calculator → Tokens in, tokens out, requests a day: monthly cost on 24 GPT, Claude and Gemini models.
- AI · Oct 20, 2026 · 11 min readDeepSeek gave away its engine. The $38-an-hour parts stayed home
- AI · Oct 19, 2026 · 13 min readTheo's 60% stat is an outage chart: 43 hours of Codex incidents
- AI · Oct 18, 2026 · 13 min readExponential backoff with jitter: 64 of 100 LLM calls still failed