LiteLLM Supply Chain Attack: 153 GB of Stolen Credentials Surfaced Five Months After the Breach
By ScruteXPublished

The theft happened in March. The proceeds appeared in August. Between those two dates, most affected organisations rotated one API key, closed the ticket and moved on. Researchers who analysed the archive found keys at a major US technology company that still worked, five months after the company said it had rotated them.
The LiteLLM supply chain attack was the publication of two backdoored versions of the LiteLLM Python package, 1.82.7 and 1.82.8, to PyPI on 24 March 2026 by the threat actor TeamPCP. The packages were live for roughly 40 minutes. In that window they harvested cloud access keys, CI/CD secrets, Kubernetes service account tokens, SSH keys and AI provider keys from every environment that installed them. On 11 to 13 August 2026, security researchers published their analysis of a 153 GB archive of the stolen material: 433,909 files, with 118,829 CI runner dumps attributed to 2,488 corporate domains.
If any build, container, notebook or laptop in your estate resolved
litellm during that window, this is a credential compromise, not a dependency upgrade. Removing the package fixes nothing. Rotation does.What was the LiteLLM supply chain attack?

LiteLLM is an open-source Python SDK and proxy that routes requests to more than 100 model providers. It is a common transitive dependency inside agent frameworks, MCP servers and LLM orchestration tools, and it carries roughly 95 million downloads a month on PyPI.
On 24 March 2026, an attacker with the project's stolen PyPI publishing token pushed two releases that never existed on GitHub. Version 1.82.7 went live at 10:39 UTC with a payload hidden inside
litellm/proxy/proxy_server.py. Version 1.82.8 followed at 10:52 UTC and added litellm_init.pth, a file Python executes at interpreter startup. PyPI quarantined the package inside the hour.The design choice here matters more than the timing. LiteLLM sits in front of every model provider key an organisation holds. An attacker looking for credentials picked the package whose job is to hold credentials.
Timeline

| Date | Event |
|---|---|
| Early Mar 2026 | A Trivy automation token leaks. It is rotated but access persists for roughly 20 days. |
| 19 Mar | TeamPCP publishes a malicious Trivy v0.69.4 and force-pushes malicious commits over 76 of 77 version tags in the trivy-action repository. |
| 22 Mar | Malicious Trivy Docker images v0.69.5 and v0.69.6 appear on Docker Hub. |
| 23 Mar | Attacker registers models.litellm[.]cloud, a typosquat of the project's own domain. |
| 24 Mar, 10:39 UTC | LiteLLM 1.82.7 published to PyPI. |
| 24 Mar, 10:52 UTC | LiteLLM 1.82.8 published, adding the .pth loader. |
| 24 Mar, 11:48 UTC | A researcher opens GitHub issue #24512 after the .pth payload crashes a development machine. |
| 24 Mar, 12:44 UTC | The attacker, still holding maintainer access, closes the issue and floods the thread with bot comments. |
| 24 Mar, ~13:38 UTC | PyPI quarantines the package. LiteLLM halts releases, rotates credentials and engages incident responders. |
| 25 Mar | Microsoft links the LiteLLM wave to the same TeamPCP campaign as Trivy and Checkmarx KICS. |
| 16 Apr | VECT ransomware operators announce a partnership with TeamPCP on BreachForums. |
| 2 Jul | FBI FLASH advisory warns that affiliated actors will use the harvested credentials long after the intrusion. |
| 11-13 Aug | Researchers publish analysis of a 153 GB archive of stolen files spanning 2,488 corporate domains. |
The bot flood deserves a line of its own. Analysts counted 88 comments from 73 accounts inside a 102-second window, posted from previously compromised developer accounts rather than throwaway profiles, with roughly three quarters of those accounts also used during the Trivy disclosure. Suppressing the disclosure was part of the operation, not an afterthought.
How did a poisoned security scanner reach PyPI?
No exploit was needed at any stage. Every step used credentials or behaviour the ecosystem already trusts.
- A leaked token was rotated but not fully revoked. Access to the Trivy project persisted for about 20 days.
- Published tags were overwritten. Malicious code was force-pushed over existing version tags, so pipelines pinned to a tag still pulled poison.
- The scanner entered a build it did not own. LiteLLM's CI installed Trivy unpinned from the system package manager and inherited the compromise automatically.
- Publishing keys were harvested. The
PYPI_PUBLISHtoken was read out of the GitHub Actions runner environment. - Two releases were pushed straight to PyPI, bypassing the project's normal GitHub release flow entirely.
Security tooling was the ideal first domino. Scanners, linters and SAST tools run inside build pipelines with broad privileges, and almost nobody pins them, precisely because they are security software. That trust is the attack surface. Expect this pattern again against other tooling.
What did the malware steal, and how did it get out?

On each compromised runner the stealer escalated to root and swept the host for anything that could authenticate. Google tracks the malware as SANDCLOCK.
| Collected | Read from | What it gives an attacker |
|---|---|---|
| Cloud credentials | Instance metadata service, AWS, GCP and Azure profile directories, environment variables | Account takeover, data access, compute abuse, persistence |
| Kubernetes tokens | Mounted service account paths inside the pod | Workload takeover and lateral movement inside the cluster |
| CI/CD secrets | Job environment and /proc/<pid>/mem | Repository access, publishing rights, downstream compromise |
| SSH and Git credentials | Key directories, credential helpers, shell history, config files | Repository theft, malicious commits, deploy key abuse |
| AI provider keys | Gateway config, .env files, environment variables | Prompt and data exposure, model abuse, cost fraud |
| Application secrets | .env files, database URLs, SaaS keys, webhook tokens, TLS private keys | Data theft, impersonation, operational compromise |
Two details change how you should investigate.
Masking is not a control. GitHub Actions redacts secret values from log output. It does not remove them from process memory. Reading
/proc/<pid>/mem returns the value regardless of what the log shows. If your team treated clean, masked build output as evidence that a secret was never exposed, that assumption needs revisiting today.Blocked egress does not mean the data stayed in. The primary channel sealed collected data with AES-256 under a hard-coded RSA-4096 public key, staged it as
tpcp.tar.gz and shipped it to models.litellm[.]cloud. Where that failed, the malware created a public repository inside the victim's own GitHub account and uploaded the archive there as a release asset. Observed names include tpcp-docs and docs-tpcp. Some organisations published their own secrets to the open internet, under their own name, without knowing it.Reported persistence on developer machines included a script at
~/.config/sysmon/sysmon.py with a matching systemd user service, plus pods named node-setup-* in kube-system on affected clusters.Why the .pth file broke everyone's assumptions

Most teams defend against malicious packages with two beliefs: install scripts can be disabled, and a package you never import cannot run.
Version 1.82.7 behaved conventionally. Its payload lived in
proxy_server.py and ran when something imported the proxy module.Version 1.82.8 did not. A
.pth file placed under site-packages is processed every time the Python interpreter starts. No import, no application code, no reference to LiteLLM anywhere in your project. Installing --ignore-scripts does not stop it. A machine that pulled the package as a transitive dependency and never touched it still executed the stealer on the next python invocation.That is why the presence of
litellm_init.pth on a host is a strong compromise indicator on its own, and why cache purging matters. A yanked release can still be served from a local mirror or a wheel cache months later.Who is TeamPCP and what happens to stolen credentials?
TeamPCP emerged in late 2025 and industrialised one idea: contaminate widely used developer components, then choose victims afterwards from whatever access falls out.
The FBI's July 2026 FLASH alert attributes four compromises to the group between February and March 2026: the Trivy container scanner, Checkmarx KICS, LiteLLM, and Telnyx Python SDK versions 4.87.1 and 4.87.2. Reported totals run past 500,000 credentials from more than 10,000 pipelines.
Two follow-on developments matter for exposure modelling:
- VECT ransomware. Operators announced a partnership with TeamPCP on BreachForums on 16 April 2026 and select targets from the credential archive rather than breaking in. Responders have confirmed at least one deployment using TeamPCP-sourced credentials. Target selection happens after access, which inverts the usual "are we a target" question.
- PCPJack. A second group that appeared in mid-2026 to work the same credential base, using already-stolen access to pull further cloud secrets from environments TeamPCP had already entered. SentinelOne published analysis in May 2026.
Confidence assessment: high on the attack chain and artefacts, which both affected projects and multiple independent vendors documented consistently. Medium on organisation-level exposure counts, which are reconstructed from campaign data and not validated per victim. The practical implication is that stolen access from March is a live commodity being traded and reused, not a closed incident.
The LiteLLM CVEs that followed the compromise

Moving past March is not enough. The legitimate releases that followed carried a run of serious application flaws.
| Advisory | Class | Affected | Fixed in | Severity |
|---|---|---|---|---|
| CVE-2026-49468 | Host header authentication bypass | < 1.84.0 | 1.84.0 | Critical, CVSS 9.5 |
| CVE-2026-42208 | SQL injection in proxy API key verification | 1.81.16 to 1.83.6 | 1.83.7 | Critical, CVSS 9.3 |
| CVE-2026-59822 | MCP Streamable HTTP authentication bypass | < 1.84.0 | 1.84.0 | High |
| GHSA-xqmj-j6mv-4862 | Server-side template injection at /prompts/test | 1.80.5 to 1.83.6 | 1.83.7 | High |
| GHSA-5jmr-gcrj-2c9q | Arbitrary file write via Skills ZIP path traversal | < 1.83.7-stable | 1.83.7-stable | Moderate |
CVE-2026-49468 is the one to patch first if your proxy is reachable. The auth layer derived the request route from a value Starlette rebuilds using the client-supplied Host header, so a crafted Host made the auth gate evaluate a public health route while FastAPI dispatched the protected management handler. Public proof-of-concept code reaches
/key/generate unauthenticated, which mints a valid virtual key that then works without any bypass header. That is a persistent foothold plus provider cost abuse. Deployments behind a CDN, WAF or reverse proxy that validates the Host header are mitigated, and LiteLLM Cloud customers were not affected.The SQL injection is worse than its class suggests. The vulnerable query sits in API key verification, reachable by sending a crafted Authorization header to an ordinary route with no valid key. The proxy database holds the keys the gateway manages, so the target is the credential store itself.
Upgrade to 1.84.0 or later, then verify the current release and open advisory list before you deploy. Confirm provenance rather than trusting the number: correlate the package, the GitHub release, the source commit, build provenance and signature. On 31 March a 1.83.0 package appeared on PyPI without an obvious matching GitHub release. A higher version number proves nothing on its own, which is the most transferable lesson from this incident.
How to check whether you were exposed

Indicators
Domains are defanged. Block and hunt, do not resolve.
| Type | Value | Use |
|---|---|---|
| Package | litellm==1.82.7 | Search manifests, lockfiles, image layers, package caches |
| Package | litellm==1.82.8 | As above, higher priority: executes at interpreter start |
| File | litellm_init.pth | Search under site-packages. Presence triggers full host rotation |
| File | litellm/proxy/proxy_server.py | Payload location in 1.82.7. Compare hash against a clean build |
| File | tpcp.tar.gz | Encrypted staging archive on build hosts and runner images |
| Persistence | ~/.config/sysmon/sysmon.py, sysmon.service | Reported developer-machine persistence |
| C2 domain | models.litellm[.]cloud | Block egress, search DNS, proxy and firewall logs from 23 Mar 2026 |
| Repos | tpcp-docs, docs-tpcp | Fallback exfil repos created inside victim GitHub accounts |
| Upstream | trivy v0.69.4, v0.69.5, v0.69.6 | Malicious release and Docker Hub images in CI images and base layers |
| Actor | TeamPCP, SANDCLOCK stealer | Campaign attribution across Trivy, KICS, LiteLLM, Telnyx SDK |
Hunt queries
Find the package. Enumerate installed packages in every runner image, container and virtual environment and record the LiteLLM version. Grep every
requirements.txt, poetry.lock, uv.lock, Pipfile.lock and Dockerfile for the two versions. Search the filesystem for litellm_init.pth and tpcp.tar.gz. Check internal PyPI mirrors and proxy caches, because a yanked release can still be served locally.Find the activity. Query DNS, proxy and firewall logs for the C2 domain from 23 March onward. Review cloud audit logs for new IAM users, new access keys, unfamiliar regions and new roles from 24 March. In source control, look for new deploy keys, personal access tokens, unexpected workflows, new Actions secrets and repositories nobody recognises, especially public ones with a single release asset. In Kubernetes, look for new service accounts and unexpected API calls from build identities. On build hosts, look for Python spawning
curl, tar or a shell, and any read of /proc/<pid>/mem.Retention is the constraint. Default audit log retention on most cloud and source control platforms is shorter than the gap since March 2026. Export or extend those logs before you start.
MITRE ATT&CK mapping
T1195.002 Supply Chain Compromise: Software Supply Chain · T1552.001 Credentials in Files · T1552.005 Cloud Instance Metadata API · T1528 Steal Application Access Token · T1078.004 Valid Accounts: Cloud Accounts · T1613 Container and Resource Discovery · T1543.002 Create or Modify System Process: Systemd Service · T1567 Exfiltration Over Web Service
What to rotate, in what order

Rotating only the LiteLLM or model provider key is not enough. Treat every credential the affected process could read as exposed.
First 24 hours. Find every host, image and cache that resolved the package. Isolate and preserve them before rebuilding. Rotate cloud access and service account keys across AWS, GCP and Azure; repository tokens, deploy keys, CI secrets and registry credentials; Kubernetes tokens and cluster service accounts; SSH keys, database passwords and TLS private keys; and every AI provider key the host could read.
24 to 72 hours. Rebuild affected environments from known-clean sources rather than cleaning in place. Review cloud, source control, registry and cluster audit logs from 24 March onward. Hunt unrecognised repositories in your own GitHub organisations, plus new tokens, service accounts and deploy keys.
Ongoing. Pin dependencies, and pin GitHub Actions to commit SHAs rather than tags, starting with your security tooling. Shorten credential lifetime and scope, and prefer workload identity over static keys. Require provenance (SBOM, signature, attestation) before an artifact is trusted. Give each build job only the credentials it needs.
One caveat on containers: LiteLLM states that users on the official Proxy Docker deployment path were not impacted, because dependencies were pinned in the image build. That finding is specific to that image. A container still inherits the compromise if it installs packages at build or run time, resolves unpinned dependencies, receives injected secrets, or came out of a pipeline that was itself exposed.
Why AI infrastructure is the next supply chain target
AI gateways, agent runtimes, MCP servers and vector stores sit where credentials, data, compute and autonomous action meet. Five properties make that layer attractive:
- Privileged by design. A gateway holds credentials for models, databases, plugins, cloud services and internal tools because that is its function. Compromise the control point and you get everything it is wired to.
- Wide, shallow dependency graphs. A modern AI stack pulls packages, SDKs, connectors, extensions and vector databases from dozens of maintainers, most unpinned, few audited.
- Autonomous execution raises the ceiling. Agentic workflows read, write, call tools and trigger business processes. Stolen access there buys action, not only data.
- Adoption outruns inventory. Teams stand up AI services faster than security registers them, leaving unowned MCP servers and unmanaged keys.
- Existing visibility does not reach it. Attack surface tooling sees hosts, ports and certificates. It does not see which agent holds which key, or which model gateway sits in front of a production database.
This is also a third-party risk problem, not only an internal one. Your suppliers inherited the same 40 minutes. If a vendor's build pipeline was exposed and their tokens reached your systems, your rotation plan is incomplete without theirs.
Key takeaways
- Two malicious LiteLLM releases were live on PyPI for about 40 minutes on 24 March 2026, and that was long enough to reach 2,488 corporate domains.
- Version 1.82.8 executed at Python interpreter start via a
.pthfile, so hosts that never imported the package still ran the stealer. - The 153 GB archive published in August confirms the material left victim environments and is circulating. Some keys were still valid five months later.
- Removing the package, upgrading the version and clean masked logs prove nothing. Rotation, log review and provenance verification do.
- Upgrade LiteLLM to 1.84.0 or later to close CVE-2026-49468 and the related advisories, and put admin routes behind a proxy that validates the Host header.
Scrutex tracks where campaign proceeds surface: stolen and malware-harvested credentials, leaked code and exposed repositories, dark web and Telegram forum activity, and public cloud buckets. Attack surface discovery finds internet-facing gateways still running affected versions, and vendor scoring covers the suppliers whose pipelines you inherit. Start a free exposure check, no credit card required.
FAQ
Q: Which LiteLLM versions were compromised?
A: Versions 1.82.7 and 1.82.8, both published to PyPI on 24 March 2026 and removed within roughly 40 minutes. Version 1.82.6 is the last release known to be clean. Upgrade to 1.84.0 or later, which also fixes CVE-2026-49468.
Q: Am I affected if I never imported LiteLLM?
A: Possibly. Version 1.82.8 installed a
.pth file that Python runs at interpreter startup, so the payload executed wherever the package was merely present, including transitive installs through agent frameworks and MCP servers. Search for litellm_init.pth under site-packages.Q: What credentials did the LiteLLM malware steal?
A: Cloud access keys from instance metadata and profile directories, Kubernetes service account tokens, CI/CD secrets read from process memory, SSH and Git credentials, AI provider API keys, database URLs, SaaS tokens and TLS private keys.
Q: Is my organisation breached if it appears in the 153 GB dataset?
A: No. A match means exposure evidence is strong, not that credentials were captured, that they still work, or that an attacker used them. Validate against your own build history for the 24 March window and your cloud, source control and cluster audit logs before treating it as an incident.
Q: We rotated in March. Is that enough?
A: Only if the rotation covered every credential class the affected job could reach and you verified it. Researchers found keys still valid at organisations that reported rotating, usually because the rotation covered the obvious AI keys and missed cloud, repository and cluster credentials.
Q: Does using the LiteLLM Docker image mean we were safe?
A: The official Proxy Docker image pinned its dependencies and was not impacted by the malicious SDK releases. That does not extend to custom images that install packages at build or run time, resolve unpinned dependencies, or come out of a pipeline that was itself exposed.