Skip to main content
SaaSCity.io
DirectoriesLive LaunchesBlogWrite for UsAdvertise
Submit
Home/Blog/LiteLLM 1.107.0 Shipped a Credential Stealer on PyPI. Here's What to Rotate (2026)
Back to Blog

News

LiteLLM 1.107.0 Shipped a Credential Stealer on PyPI. Here's What to Rotate (2026)

For about two hours on October 21, 2026, installing litellm handed an attacker your SSH keys, your cloud IAM role and every provider key your proxy stores. We dug through the incident report, the GitHub issue and the technical write-ups to build the rotation checklist you actually need. SaaSCity runs its own small stack through tools like this, which is exactly why we read incident reports instead of skipping to the summary.

ghosty
ghosty
Founder, SaaSCity
October 22, 202612 min read
LiteLLM 1.107.0 Shipped a Credential Stealer on PyPI. Here's What to Rotate (2026)
Contents (8)
  1. Key Takeaways
  2. What actually happened on October 21
  3. The payload: three files, one of which never stops running
  4. How a hardened pipeline got hijacked anyway
  5. What is actually clean
  6. The rotation checklist, in the right order
  7. The structural fixes worth making regardless
  8. The thought worth sitting with

Quick answer: On October 21, 2026, LiteLLM's release pipeline got hijacked and a malicious litellm==1.107.0 landed on PyPI, Docker Hub and GHCR. All seven PyPI wheels carried a credential stealer (cook.py) that runs on every Python startup and a dormant remote-code loader (sitecustomize.py). PyPI pulled it by 13:23 UTC, roughly two hours after upload. If you installed it, assume your SSH keys, cloud IAM role, AI coding tool tokens and every provider key stored in your LiteLLM proxy are gone — pin to 1.106.2 and start rotating.

Official LiteLLM incident report page for the October 21, 2026 compromise, titled "Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised" and signed by the CEO, CTO, director of security and lead Rust engineer.

You're reading this on a startup directory's blog, not a security vendor's, so here's the honest framing up front: we're not LiteLLM's incident responders and we weren't in the room. What follows is built from LiteLLM's own report, the GitHub issue that broke the news, and three independent technical write-ups we actually read end to end. SaaSCity runs its own modest stack of API keys and provider integrations, same as any SaaS, which is exactly why an incident like this gets our full attention instead of a two-line summary.

Key Takeaways

  • Two-hour window, full wheel set: All seven litellm==1.107.0 PyPI wheels, every supported platform, uploaded 11:39:51-11:40:13 UTC and removed by PyPI around 13:23 UTC.
  • The payload runs on any Python start, not just LiteLLM imports: cook.pth triggers cook.py at interpreter startup — even a plain pip command in the same environment re-triggers it.
  • It reads your AI coding tools too: Claude Code credentials, Codex auth, Gemini OAuth, Cursor's mcp.json — the stealer targets the exact tools most developers now run daily.
  • A second, dormant stage exists: sitecustomize.py checks an hourly dead-drop for remote code to execute. It was empty at observation time but can be re-armed with one GitHub commit.
  • The git tag and source tarball are clean: The malicious files only exist in the built wheels, so reading the repo tells you nothing.
  • Root cause was credential logging, not a code flaw: LiteLLM says a GitHub CLI OAuth token that Claude Code embedded in a response body ended up in its own request logs, and that log sat on an internet-reachable internal deployment.
  • This is LiteLLM's second hijacking in 2026: A March 2026 incident used a different unpinned-dependency path. JFrog says there's no evidence linking the two campaigns.

What actually happened on October 21

LiteLLM is the open source gateway that sits between an application and more than 100 LLM providers — Anthropic, OpenAI, Azure, Bedrock, the works. It's genuinely popular: roughly 23 million downloads a week, according to Socket's analysis. Most AI SaaS products that route model traffic through anything other than a single provider's raw SDK are routing it through LiteLLM or something shaped exactly like it.

That popularity is what makes this incident sting. At 11:39:51 UTC, a compromised build pipeline uploaded seven malicious wheels — macOS x86_64 and arm64, manylinux aarch64 and x86_64, musllinux aarch64 and x86_64, and win_amd64 — covering every platform LiteLLM ships for. By 11:46:56, matching Docker images had landed on Docker Hub and GHCR under the tags v1.107.0, latest, main-latest and main-stable, all pointing at the same compromised image digest, sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e.

PyPI pulled 1.107.0 around 13:23 UTC. GHCR tags went dark by 13:51, Docker Hub by 14:20. Total exposure window on PyPI: roughly an hour and forty minutes. On Docker: closer to two and a half hours. Anyone who ran docker pull litellm/litellm:latest in that stretch, or had a CI job auto-bumping to the newest version, got the payload without doing anything unusual.

MilestoneTime (UTC, Oct 21)
Branch protection overridden10:08:34
Staging build from lookalike repo10:46:39
Production pipeline starts11:08:53
Wheels uploaded to PyPI11:39:51-11:40:13
Docker images published11:46:56
GitHub release page published11:50:24
Workflow logs deleted11:53
PyPI removes 1.107.0~13:23
GHCR tags unavailable~13:51
Docker Hub tags clean~14:20

Here's the detail that makes the official git tag useless for checking yourself: the malicious files aren't in the repository at all. According to the GitHub issue that broke the story, filed by Upwind's research team, the tagged source tree at 66c8f5b1a9cefaa7c55b758f86f4ab552f2e859e is clean. The source tarball on PyPI is clean too. One line was added to the sdist's pyproject.toml — data = "cook" under [tool.maturin] — that quietly tells the Rust-based build tool to fold a cook/ directory into the wheel during compilation. That directory doesn't exist in any tagged commit. Someone modified the build tree after checkout, before the wheels were built, and nobody who only reads the repo would ever see it.

The payload: three files, one of which never stops running

PyPI project page for litellm showing 1.106.2 as the current release after the yanked 1.107.0, with the install command and the open source gateway description under it.

Three files land in the root of site-packages, outside the normal litellm/ package folder, which is itself a tell — legitimate packages don't scatter files at that level.

cook.pth is twelve bytes: import cook. Python auto-processes any .pth file it finds in site-packages at interpreter startup, no import statement from your code required. This is the mechanism that makes the whole thing nasty. It's not "import litellm and get owned." It's "start Python in that virtual environment, for any reason, and get owned." Run pip list to check what's installed. Congratulations, you just ran the stealer again.

cook.py, about 22 KB, zlib-compressed and base64-wrapped, calls itself privcollect internally. It spins up in a background thread and works through roughly sixteen collection modules: SSH keys and shell history; AWS, Azure, gcloud, kube, Docker, gh and .netrc credentials; browser-saved passwords and cookies from Chromium and Firefox; .git-credentials, .npmrc, .pypirc, and language-specific credential files for Maven, Gradle, Cargo, Gem and Composer; .env files and any *.pem/*.key/*.pfx; CI secrets for GitLab runners, Jenkins, Vault, Terraform and Packer; the Kubernetes service account token and anything under /run/secrets; and — this is the part that should worry anyone doing AI-assisted development in 2026 — credential files for Claude Code (~/.claude/.credentials.json), Codex (~/.codex/auth.json), Gemini's OAuth tokens, Cursor's mcp.json, plus GitHub Copilot, Windsurf, Kiro, Zed, Continue, OpenCode and Hugging Face tokens.

It also reads /proc/*/environ for every process it can see, which means it's not limited to its own environment variables. And if DATABASE_URL is set — which it is, on basically every real LiteLLM proxy deployment — it queries the LiteLLM_CredentialsTable and LiteLLM_ProxyModelTable directly. That's every upstream provider key the gateway was ever configured to hold, in one database dump.

Everything gets AES-256-CBC encrypted, the key RSA-4096-OAEP wrapped, packaged as a tar.gz, and POSTed to http://www.litellm.dev/healthcheck. Notice the domain. The real project lives at litellm.ai. litellm.dev was registered October 13, 2026, eight days before the attack, specifically to look plausible in a log line. A success marker file, /tmp/.pc_once_72129118c59f5636c0bb, only gets written after the upload returns HTTP 200 — but its absence proves nothing, since container restarts and tmp cleanup wipe it just as easily as a failed upload would.

sitecustomize.py, roughly 25 KB, is the second stage. Python also auto-imports sitecustomize at startup if one exists on the path, same mechanism, same guarantee of execution. It forks a detached daemon, holds a lock at /tmp/remote_code_loader.lock, and every hour fetches https://raw.githubusercontent.com/letters9813/pythonclient-test/main/domain.txt. If that file names a domain, it hits <domain>/codepy and executes whatever comes back. At the time researchers checked, the file was empty — so this stage was dormant, not disarmed. One commit to that GitHub file and it's live again. The GitHub account letters9813 was created October 13, 2026, same week as the lookalike domain. On Windows, the fork step fails silently, but cook.py still runs fine there.

While you are here

Get your SaaS listed on SaaSCity

A permanent listing on the live city map, a DR 68+ dofollow backlink and a launch week in front of founders. Free with a badge, or skip the queue with Quick Pass — live within 24 hours.

Submit your SaaSWhat you get

How a hardened pipeline got hijacked anyway

This is the part that should worry you more than the malware itself. LiteLLM had already been burned once — a March 2026 incident, documented separately by JFrog's research team, traced to an unpinned Trivy 0.69.4 dependency that exposed CI credentials and let attackers ship backdoored 1.82.7 and 1.82.8 releases. After that, LiteLLM moved to a private release repo, Trusted GitHub Publishers, immutable Docker tags and signed images. Real hardening, not theater.

The Monday before this incident, LiteLLM temporarily widened release permissions so two internal engineers could push daily bugfix backports — a normal, bounded operational decision. The attacker had GitHub CLI OAuth credentials for three LiteLLM developers. One belonged to a GitHub organization owner and was used at 10:08 UTC to disable branch protections and force-push a workflow change. A second satisfied the production approval gate at 11:11. A third deleted the production and staging workflow records at 11:53, which is why nobody can fully reconstruct the build logs after the fact. The pipeline itself was pointed at Berri-AI/litellm — one hyphen different from the real BerriAI/litellm — and Docker's build provenance recorded that lookalike repo as the source without anyone flagging it.

Where did three developers' gh tokens come from? LiteLLM's own report says their internal deployment was reachable from the public internet, and its logs held the tokens. The mechanism, per their own disclosure and discussed on the resulting Hacker News thread: Claude Code, in some situations, embeds a user's GitHub CLI OAuth token inside a response body. When that response gets logged — which is a completely ordinary thing to do for debugging an AI gateway — the token lands in plaintext in your own logs. LiteLLM suspects initial entry through a since-patched vulnerability in that exposed deployment.

Credit where it's due: LiteLLM's public incident report names its own CEO, CTO, director of security and lead Rust engineer, posts a minute-by-minute timeline, and commits to two-hour status updates. That's not the move of a team trying to bury a bad week. The failure here wasn't silence. It was architecture — specifically, logging AI agent traffic turned a routine debugging habit into a credential leak, and a credential leak turned into a supply-chain compromise with real teeth.

What is actually clean

GitHub security issue 47826 in the BerriAI/litellm repository, titled as the 1.107.0 compromise with the cook.pth credential stealer and sitecustomize RCE loader, showing the status block confirming PyPI removed the release.

If you're staring at a lockfile wondering what's safe to pin, here's the full confirmed-clean list from LiteLLM and the independent researchers who checked wheel contents directly, not just version numbers.

PackageClean versions
litellm1.103.6, 1.103.7, 1.104.4, 1.104.5, 1.105.2, 1.105.3, 1.106.0, 1.106.1, 1.106.2, 1.107.0rc1, 1.107.0rc2
litellm-proxy-extras0.4.108, 0.4.109, 0.4.110
litellm-enterprise0.1.69.post2, 0.1.73.post1, 0.1.76.post1

LiteLLM's own recommendation is to pin 1.106.2. Enterprise customers on the private registry weren't touched by this at all — that registry is currently carrying an embargoed fix for an unrelated vulnerability, scheduled for disclosure October 27, which is a separate matter entirely and not part of this incident.

The rotation checklist, in the right order

Do not run pip first. Any Python start in an affected environment re-triggers cook.pth, including the pip command you'd use to fix the problem.

StepAction
1Do not run pip, python, or anything in the affected venv/container yet.
2Manually delete cook.pth, cook.py, sitecustomize.py and matching __pycache__ entries from every affected site-packages directory.
3Kill the detached python process holding /tmp/remote_code_loader.lock.
4Rebuild the image or venv from scratch, pinned to litellm==1.106.2 or a reviewed digest. Don't patch in place.
5If the host had outbound internet access while 1.107.0 was installed, assume exfiltration happened. A missing marker file proves nothing.
6Rotate the instance or task IAM role.
7Rotate every provider key stored in the LiteLLM proxy database, plus master and virtual keys.
8Rotate the DATABASE_URL password and any other secret present in any process environment on that host.
9Check CloudTrail for GetCallerIdentity calls around install time, since the stealer validates stolen AWS credentials that way before exfiltrating them.

A provider key with no spend limit is a credit card with no limit. A cloud IAM role is the whole account. One .pth file in one container handed over the model budget, the database, the CI tokens and the deploy keys, all at once, to a process that started the moment someone ran pip list.

The structural fixes worth making regardless

Beyond this specific incident, a few habits generalize to any dependency you pull into a system that holds secrets:

  • Pin digests, not floating tags. latest and main-stable are exactly the tags that got silently repointed at a compromised image for two and a half hours.
  • Treat your AI gateway as a secrets system, not a routing utility. It holds every provider key you've ever given it, often unencrypted in a database row.
  • Keep provider keys in a vault the gateway reads at runtime instead of storing them in the gateway's own database.
  • Allowlist egress so a POST to an unfamiliar domain fails closed instead of succeeding quietly.
  • Verify wheel RECORD contents and startup hooks before trusting a package that ships compiled Rust bridges or anything with build-time code generation.
  • Never let CLI OAuth tokens land in request logs. If your AI coding assistant embeds tokens in response bodies, logging those bodies is logging credentials, full stop.

If you're a founder shipping an AI product and wondering what you should actually publish about your own security posture, this incident is a decent template: name the people responsible, give exact timestamps, say precisely what was and wasn't affected, and commit to a cadence of updates instead of one vague statement. That kind of transparency is also exactly what buyers go looking for before they connect your product to their provider keys — it's a big part of why a listing page, not just a marketing site, is often where someone checks you out first. We built SaaSCity as a gamified startup directory with a live city map and human editorial review for that reason: every founder gets a free listing page and a building on the map, and adding the SaaSCity badge to your site earns a dofollow backlink plus a Monday launch slot. Quick Pass ($19.99) goes live within 24 hours, Premium ($39.99) adds a written launch post with three dofollow links, and the domain sits at DR 66 as of our last Ahrefs check in October 2026. None of that replaces a real incident response plan, but a product people can actually find and vet tends to be a product people trust with their keys.

This is also a good moment to remember LiteLLM isn't the only recent reminder that your dependency tree is an attack surface, not a shopping list — see the zod npm supply chain attack for a different flavor of the same lesson, and the Supabase exposed-database incident for what happens when the thing holding your data is misconfigured instead of backdoored.

The thought worth sitting with

Nobody in this story was lazy. LiteLLM hardened its pipeline after March, named names in its report, and shipped updates every two hours while still mid-incident. The thing that broke wasn't effort. It was the assumption that a gateway holding every provider key you own is just infrastructure, logged the same casual way as everything else. If your AI stack's most privileged component is also the one nobody's threat-modeled this year, October 21 is the reminder to go fix that before someone else finds the gap for you.

Get your SaaS in front of founders

List your product on the SaaSCity live city map - a permanent listing, real discovery, and a backlink from a high-DR directory. Free to start; upgrade for a dofollow link and a building on the map.

Submit your SaaSSee pricing

Founder resources

Done-for-you directory submissionBest SaaS & startup directoriesFree dofollow directoriesBest AI directoriesHigh-DR directoriesFree DR checkerLive launches

Related articles

Cloudflare Just Put Web Search Inside AI Gateway — at $0.25 per 1,000 Queries (2026)

Cloudflare Just Put Web Search Inside AI Gateway — at $0.25 per 1,000 Queries (2026)

Modal Raised $750M at $15.75B on $1B ARR - and $300M of It Comes From Letting Agents Run Code (2026)

Modal Raised $750M at $15.75B on $1B ARR - and $300M of It Comes From Letting Agents Run Code (2026)

Claude Haiku 5.5 Is Cheap Enough to Change Your AI SaaS Margins (2026)

Claude Haiku 5.5 Is Cheap Enough to Change Your AI SaaS Margins (2026)

Contents

  1. Key Takeaways
  2. What actually happened on October 21
  3. The payload: three files, one of which never stops running
  4. How a hardened pipeline got hijacked anyway
  5. What is actually clean
  6. The rotation checklist, in the right order
  7. The structural fixes worth making regardless
  8. The thought worth sitting with

List your SaaS

$19.99one-time
  • Dofollow DR 68+ backlink
  • Live within 24 hours, no queue
  • Permanent listing on the city map
Submit your SaaS

Or list free with our badge

Done for you

We submit your SaaS to up to 370 directories

Every form filled in by us, every submission and its screenshot in a report. Three packages.

from$99one-time

Pick your project

City Sponsors

  • Nick LaunchesShip, launch, and get your product in front of real founders.
  • @ListBulbGet your product discovered.
  • @peregrineintellPeregrine OS: pre-call intel for agency new business
Become a sponsor
Write for this blog, from $99.99
SaaSCity.io

Directories are boring. We built a city instead. First isometric SaaS directory on the planet.

Platform
Submit SaaSLive LaunchesPricingBlogWrite for UsMCP for AgentsFree ToolsAdvertise
Directories
Best SaaS DirectoriesBest AI DirectoriesDirectory ReviewsBest Indie Hacker CommunitiesBest Subreddits for FoundersFree DR CheckerFree DR BadgeHow to Get SaaS BacklinksSEO GlossaryHow to Submit to DirectoriesDirectory Submission ServiceDirectory Submission Services Compared
SaaSCity Alternatives
All ComparisonsSaaSCity vs Nick LaunchesSaaSCity vs BetterLaunchSaaSCity vs PeerPushProduct Hunt AlternativesSaaSHub Alternatives
Legal
Terms of ServicePrivacy PolicyRefund PolicyCookie PolicyCopyright & DMCASecurity
Company
AboutghostyContact

© 2026 SaaSCity.io

llms.txt