Skip to main content
95 million downloads a month, and one row with all the keys
  1. MindSecSet/

95 million downloads a month, and one row with all the keys

·1956 words·10 mins·
Author
Virtue of Vague
Table of Contents

I almost skipped this one. Not because it isn’t important — it is. But because it’s two incidents wearing the same name, and keeping them straight took real work. One supply chain attack in March. One SQL injection in April. Different techniques, different attackers, same target. Same lesson underneath.

LiteLLM is a Python library and proxy that routes requests across every major LLM provider. OpenAI, Anthropic, Azure, Bedrock — all in one place. 95 million downloads a month. 45,000+ GitHub stars. And because it sits in front of everything, it holds all the keys. Literally. A single credentials row can contain an OpenAI org key with five-figure monthly spend, an Anthropic console key with workspace admin, and an AWS Bedrock IAM credential. At the same time.

That’s not a web app breach. That’s a cloud account compromise waiting to happen.

So here’s the breakdown — both incidents, honest about what we know and what’s still disputed.


why LiteLLM matters as a target
#

The “gateway” pattern is everywhere now. AI agent frameworks, MCP servers, orchestration tools — a lot of them pull LiteLLM in as a transitive dependency without anyone realising it. That means the attack surface isn’t just “organisations that run LiteLLM proxy.” It’s everyone who has it in their dependency tree.

And the centralization cuts both ways. Convenient for developers. Catastrophic when compromised. If an attacker owns one row in the credentials table, they may get access to every LLM provider your organisation uses.

That’s the throughline for both incidents below.


Incident 1: the supply chain attack (March 2026)
#

This one wasn’t a vulnerability in LiteLLM’s code. It was a compromise of the trust chain around it. The attacker never touched LiteLLM directly — they compromised a security tool LiteLLM’s own pipeline depended on.

timeline
#

Date/time (UTC)Event
Late Feb 2026TeamPCP compromises Trivy’s CI/CD via a misconfigured GitHub Actions workflow. Steals Trivy’s PyPI publishing token.
Mar 1, 2026Aqua Security discloses the Trivy compromise, starts rotating credentials.
Mar 19–23, 2026LiteLLM’s pipeline, which used Trivy, inadvertently leaks LiteLLM’s own PyPI publishing tokens downstream.
Mar 24, 10:39TeamPCP publishes malicious litellm v1.82.7 to PyPI. Payload hidden in proxy_server.py.
Mar 24, 10:52Second malicious version v1.82.8 published. Uses a .pth file that executes on any Python interpreter startup — even if litellm is never imported.
Mar 24, 11:48Security researcher Callum McMahon opens GitHub issue after his machine crashes from a fork-bomb side effect.
Mar 24, 12:44Attacker, still holding maintainer access, closes the issue and floods the thread with bot comments to suppress discovery.
Mar 24, 13:38PyPI admins quarantine the entire litellm package. Exposure window: roughly 40 minutes.
Mar 26, 2026CISA adds CVE-2026-33634 to KEV.
Aug 2026CloudSEK / Hudson Rock analysis attributes 118,829 CI runner dumps to 2,488 affected corporate domains — NVIDIA, Cisco, Volkswagen, X, FedEx, Zscaler, Roku, Vodafone among high-confidence matches. Note: these are researcher-attributed exposures, not confirmed breaches.

plain English attack chain
#

This is a supply-chain-of-supply-chains attack.

  1. Compromise upstream. Trivy’s CI/CD had a misconfigured pull_request_target workflow. TeamPCP used it to steal Trivy’s PyPI publishing token.
  2. Propagate downstream. LiteLLM’s pipeline trusted Trivy. That trust became the infection vector. The attacker pivoted through it to steal LiteLLM’s own PyPI credentials.
  3. Publish malicious releases. Two backdoored versions went straight to PyPI. The .pth file trick in v1.82.8 is nasty — it runs at Python interpreter startup, not on import. So even transitive installs (e.g., an AI framework pulling litellm as a dependency) triggered it.
  4. Three-stage payload.
    • Stage 1 — Credential theft: scans env vars and config files for AI provider keys, cloud metadata, .kube/config, .aws/credentials.
    • Stage 2 — Lateral movement: if in Kubernetes, uses the local service account token to probe the K8s API for pods, services, and secrets.
    • Stage 3 — Persistence: attempts privilege escalation to install a systemd unit disguised as “System Telemetry Service,” then beacons to C2.

And here’s a useful defender signal: the .pth launcher had a bug that caused an exponential fork loop. Machines running the malicious version suddenly saw severe RAM exhaustion and system lag. A crash wasn’t a failure — it was a side effect of the malware working.

what a Security analyst watches for
#

  • Sudden severe RAM exhaustion on any host that recently ran pip install litellm or a dependency that pulls it.
  • Outbound connections to models.litellm.cloud or checkmarx.zone.
  • New systemd user services with a benign-sounding name like “System Telemetry Service.”
  • Kubernetes pods in kube-system named node-setup-* using alpine:latest images and privileged access.
  • Repository clone/checkout activity tied to a repo that referenced the malicious pinned versions — risk exists even if the version was later bumped, because the exfiltration triggers on interpreter startup, not just at pin time.

key IOCs
#

CategoryIndicator
Affected versionslitellm==1.82.7, litellm==1.82.8
Malicious fileslitellm_init.pth, litellm/proxy/proxy_server.py
Persistence files~/.config/sysmon/sysmon.py, sysmon.service
Service display name“System Telemetry Service”
C2 domainsmodels.litellm.cloud, checkmarx.zone
Exfil archivetpcp.tar.gz
K8s workloadsnode-setup-* in kube-system, alpine:latest
Dead-drop C2github.com/tpcp-docs-*

mitre att&ck mapping
#

TacticTechnique
Initial AccessT1195.001 — Supply Chain Compromise
Initial Access (upstream)T1078 — Valid Accounts (stolen Trivy PyPI token)
ExecutionT1059 — Command and Scripting Interpreter
PersistenceT1543.002 — Systemd Service
Credential AccessT1552 — Unsecured Credentials
DiscoveryT1613 — Container and Resource Discovery
Lateral MovementT1610 — Deploy Container
C2T1071 — Application Layer Protocol
ExfiltrationT1041 — Exfiltration Over C2 Channel

remediation
#

  1. Immediate rollback — pin to litellm==1.82.6, don’t just uninstall.
  2. Audit and purge CI/CD — clear build caches; treat self-hosted runners as compromised.
  3. Rotate every secret the LiteLLM environment could reach — all LLM API keys, cloud IAM keys, Kubernetes service account tokens. Assume compromise.
  4. Egress filtering — the backdoor needed to reach C2. Strict egress on production clusters and dev VPCs would have blocked exfiltration even if the payload executed.
  5. Note: official LiteLLM AI Gateway/Proxy Docker image users were not affected because that deployment path pins dependencies via requirements.txt rather than pulling from PyPI live.

one reporting wrinkle worth naming
#

The exact mechanism of the malicious publish is still a little fuzzy. CloudSEK says a poisoned build process produced and published the malicious releases. LiteLLM’s own incident report points to a direct PyPI upload that bypassed the official CI/CD workflow. Unit 42 frames it as attackers targeting PyPI publishing tokens directly after the Trivy breach. CloudSEK’s response: these are “different stages of the same attack chain, not competing explanations.” Maybe. I’m okay with saying I don’t know the exact mechanism — it matters less than the fact that the trust chain broke.


Incident 2: the SQL injection CVE (April–May 2026)
#

A separate, later vulnerability. Not the same bug. But it hits the same project and the same underlying risk: centralised credential exposure.

key facts
#

  • CVE-2026-42208, CVSS 9.3. Advisory: GHSA-r75f-5x8p-qvmc.
  • Root cause: a database query used during proxy API key checks concatenated the caller-supplied key directly into the query instead of using a parameterized query.
  • Exploitation path: unauthenticated attacker sends a crafted Authorization header to any LLM API route (example: POST /chat/completions), reaching the vulnerable query through the proxy’s error-handling path. Result: read access to the proxy database, potentially write access — meaning unauthorised access to the proxy and the credentials it manages.
  • Affected versions: >=1.81.16, <1.83.7.
  • Patched in: 1.83.7-stable, released April 19, 2026.
  • First exploitation observed: April 26, 2026, 16:17 UTC — roughly 26 hours 7 minutes after the GitHub advisory was indexed.
  • Added to CISA KEV: May 8, 2026. Patch deadline for FCEB agencies: May 11, 2026.

attack pattern
#

Per Sysdig researcher Michael Clark, the activity originated from IP 65.111.27.132 and split into two phases from adjacent egress IPs, followed by a brief unauthenticated probe of key-management endpoints.

The attacker specifically targeted two database tables:

  • litellm_credentials.credential_values
  • litellm_config

Those are the tables holding upstream LLM provider keys and proxy runtime environment settings. Notably, no probes against litellm_users or litellm_team. The attacker knew the exact schema — verbatim Prisma table names — and went straight for secrets, not user metadata. About 20 minutes into the first phase, they switched to a second IP, 65.111.25.67, to run a similar probe. That’s deliberate tradecraft.

why this one is scarier in a different way
#

No public PoC was required. The advisory and the open-source schema alone were enough to reconstruct working exploitation. The attacker enumerated column counts, used exact internal table names, and targeted the high-value rows. The ~26-hour exploitation window reflects a broader pattern: attackers increasingly don’t wait for a proof-of-concept to drop. They read the advisory and get to work.

security analyst watch-list for this CVE
#

  • Malformed or unusually crafted Authorization headers hitting any LLM proxy route, especially /chat/completions — not just the auth endpoint itself.
  • Unexpected read/write activity against litellm_credentials or litellm_config tables, without corresponding activity on litellm_users or litellm_team. That asymmetry is a tell.
  • Traffic showing a probe from one IP, a pause (~20 min), then a second probe from an adjacent IP.
  • Any LiteLLM proxy instance still running >=1.81.16 and <1.83.7.

mitre att&ck mapping
#

TacticTechnique
Initial AccessT1190 — Exploit Public-Facing Application
ExecutionT1059 — SQL injection via crafted header
Credential AccessT1555 — Credentials from Password Stores
DiscoveryT1590 — Gather Victim Network Information
ImpactT1565 — Data Manipulation

remediation
#

  1. Patch to 1.83.7-stable or later immediately.
  2. If patching isn’t possible right now, set disable_error_logs: true under general_settings — this closes the specific path through which untrusted input reached the vulnerable query.
  3. Treat the KEV listing as mandatory-patch-tier even if you’re not a federal agency. The same urgency applies to anyone running LiteLLM proxy in production.

two incidents, one lesson
#

Different techniques. Different months. Same target. Same lesson underneath.

A gateway that centralises credentials is a single point of catastrophic failure by design. Convenience and blast radius are the same coin. LiteLLM’s 95 million monthly downloads are not a bug — they’re the whole value proposition. But every one of those downloads is a potential entry point.

The March attack worked because LiteLLM trusted Trivy, and Trivy trusted a CI/CD workflow, and one misconfigured workflow undid the whole chain. The April attack worked because one database query didn’t parameterize input, and that one mistake exposed every credential row to an unauthenticated attacker.

Neither was exotic. Neither required a zero-day in the model itself. Both hit the infrastructure around the AI — the code, the pipeline, the database. That’s where the real risk is now. Not the model weights. The plumbing.


what I’m taking away
#

  • Trust is transitive, but compromise is too. You may not control what you depend on. But you can control how you verify it. Pin dependencies, review CI/CD workflows, rotate credentials like it’s a habit, not a project.
  • Assume day-zero exploitation the moment an advisory posts. The 26-hour window in Incident 2 isn’t an anomaly. It’s the new normal.
  • Centralised secrets are a red flag, not a feature. If one row in one database holds keys to every provider, the blast radius is the whole org. Maybe design for that.
  • Egress filtering would have stopped the March attack. The backdoor needed to beacon out. If the network blocked it, the payload was dead on arrival. Basic defense, huge payoff.
  • Honesty still matters. The exact mechanism of the malicious publish is still disputed. I’d rather say “we don’t know exactly” than pick a narrative that’s convenient.

That’s the whole thing. Not a single vulnerability post. Not a single vendor. A case study in how AI infrastructure is just infrastructure, with all the old trust problems — and a few new ones wearing API keys.

Stay patched. Stay paranoid. Maximum effort.

Sources: LiteLLM incident page, Cycode, Snyk, Trend Micro, The Hacker News / Sysdig, CloudSEK / Hudson Rock. IOCs and MITRE mappings as reported; I’ve flagged where accounts differ.

Related