← All Posts

The Supply Chain Is the Attack Surface

On March 19, 2026, a threat group called TeamPCP force-pushed 76 of 77 version tags in Aqua Security’s trivy-action GitHub repository. Trivy is the most widely deployed open-source vulnerability scanner in the industry. The new tags pointed to malicious commits containing a credential stealer. Because most CI/CD pipelines reference GitHub Actions by mutable version tags instead of pinned commit SHAs, every organization running Trivy in their build pipeline began executing the compromised code immediately. The scans still appeared to succeed. The scanner still reported vulnerabilities. It also quietly harvested every secret in the runner environment.1

Five days later, on March 24, TeamPCP used a PyPI publishing token stolen from one of those CI environments to publish two backdoored versions of LiteLLM, a popular AI proxy library present in 36% of cloud environments.2 Version 1.82.8 included a .pth file that executes automatically on any Python startup, before any import, before any application-level sandbox. The payload swept SSH keys, cloud credentials, database passwords, cryptocurrency wallets, and CI/CD secrets, encrypted them with a hardcoded RSA public key, and exfiltrated the archive to an attacker-controlled domain registered the day before.3

PyPI quarantined both versions after 46 minutes. In that window, 46,996 installations occurred.4 The LiteLLM maintainer deleted his GitHub account, rotated all keys, and engaged Mandiant.5

The security scanner scanned. The build pipeline built. The package manager installed. Every component did exactly what it was trusted to do.

TL;DR

  • The chain: Trivy (security scanner) compromised March 19 via GitHub Actions tag hijacking. LiteLLM (AI proxy) compromised March 24 via PyPI token stolen from a Trivy-infected CI environment. 46,996 downloads in 46 minutes.4
  • The technique: Version 1.82.8 abuses Python’s .pth mechanism to execute a credential stealer on any python3 invocation on the infected system, without importing LiteLLM.3
  • The blast radius: 2,337 PyPI packages depend on LiteLLM. 88% had version specs that allowed the compromised versions. Confirmed downstream impact on Google ADK, Stanford DSPy, MLflow, Guardrails AI, and Cisco AI Defense SDK.4
  • The campaign: TeamPCP hit five ecosystems in one week: GitHub Actions, Docker Hub, npm (CanisterWorm, 28+ packages), Open VSX (Checkmarx KICS), and PyPI (LiteLLM).6
  • The structural lesson: Each link in the chain operated within its authorized scope. The composition of authorized operations produced unauthorized behavior. This is the same composition gap that enables silent egress at the agent level and Clinejection at the CI/CD level. Supply chain attacks are composition attacks.
  • What you can do: Pin dependencies to immutable references. Isolate publishing credentials from CI runners. Monitor outbound network requests at the OS level. Treat pip install as code execution, because it is.

A Security Scanner Walks Into Your Build Pipeline

The attack started with a misconfiguration in Trivy’s GitHub Actions environment in late February 2026. An attacker exploited a vulnerable pull_request_target workflow to exfiltrate Aqua Security’s aqua-bot personal access token. Aqua rotated some credentials on March 1, but the rotation was incomplete. TeamPCP retained valid tokens.1

On March 19, they used those tokens to force-push 76 of 77 version tags in the trivy-action repository and all 7 tags in setup-trivy, redirecting every mutable tag reference to malicious commits. They also triggered release automation to publish a malicious Trivy binary as v0.69.4.1

The payload was a credential stealer that dumped Runner.Worker process memory, harvested SSH keys, cloud credentials, Kubernetes secrets, Docker configs, Git credentials, and .env files. Data was encrypted with AES-256 and a 4096-bit RSA key, then exfiltrated to attacker-controlled servers. The malicious code ran alongside legitimate Trivy functionality. Scans succeeded. Vulnerabilities were reported. The stealer ran in the background.7

Between March 19 and March 23, Docker Hub images with the trivy:0.69.4, trivy:0.69.5, trivy:0.69.6, and trivy:latest tags were compromised. CrowdStrike, Microsoft, Wiz, and Palo Alto all published technical analysis within days.789

The irony is structural: the most security-conscious organizations were the most exposed. If you ran Trivy in CI because you care about vulnerability scanning, your build pipeline was the one harvesting credentials.

From Scanner to Stealer to AI Proxy

LiteLLM’s CI pipeline ran Trivy. Specifically, ci_cd/security_scans.sh installed Trivy via apt from Aqua’s repository without version pinning.10 During the compromise window, a CI run installed the poisoned Trivy binary, which harvested the PYPI_PUBLISH_PASSWORD from the GitHub Actions environment.

On March 23, the attacker registered litellm.cloud. On March 24 at 10:39 UTC, they published litellm==1.82.7 to PyPI using the stolen credentials. Version 1.82.8 followed at 10:52 UTC.4

The two versions used different attack techniques. Version 1.82.7 embedded the payload in proxy_server.py, which triggers on import litellm.proxy. Version 1.82.8 added a .pth file to site-packages/, which triggers on any Python interpreter startup. The .pth mechanism is a legitimate Python feature for path configuration. Any file ending in .pth placed in site-packages/ is executed automatically by Python’s site.py during interpreter initialization. Most Python developers do not know this feature exists.3

The payload in both versions collected environment variables, SSH keys, AWS/GCP/Azure credentials, Kubernetes configs, database passwords, cryptocurrency wallets, and CI/CD secrets. It encrypted the collection with AES-256-CBC using a random session key, wrapped the session key with a hardcoded 4096-bit RSA public key, and exfiltrated the archive via HTTPS POST. Only the attacker can decrypt the stolen data.3

A bug in the 1.82.8 malware made it visible. The .pth file spawns a child Python process, which re-triggers the .pth file, creating an exponential fork bomb. Andrej Karpathy noted the bug on X. Without the fork bomb, the stealer would have run silently on every Python invocation on every infected system, potentially for weeks before detection.4

PyPI quarantined both versions at 11:25 UTC. Total exposure: 46 minutes. Total downloads: 46,996.4

The Blast Radius

LiteLLM is downloaded approximately 3.4 million times per day. Wiz reports it is present in 36% of cloud environments.2 The 46-minute exposure window was enough.

FutureSearch analyzed PyPI’s BigQuery download logs and found that during the attack window, version 1.82.8 was downloaded 6x more than any safe version. Downloads skewed heavily toward pip (which resolves to latest) versus uv (which uses lock files). Of the 46,996 downloads, 23,142 were pip installs of v1.82.8, each of which executed the payload during installation itself because the .pth file fires during pip’s own Python process.4

2,337 packages on PyPI depend on LiteLLM. 88% (2,054 packages) had version specifications that allowed the compromised versions to install. The downstream exposure includes:4

Project Monthly Downloads Impact
CrewAI 5.9M At risk
Browser-Use 4.2M At risk
Opik (Comet) 3.5M Confirmed 2 CI workflows ran compromised versions
Mem0 2.7M At risk
DSPy (Stanford NLP) 1.6M Pinned to <=1.82.6
Agno 1.6M At risk
Google ADK Confirmed: litellm breaks all installs
MLflow Pinned to <=1.82.6
Guardrails AI Emergency advisory issued
Cisco AI Defense SDK Emergency advisory issued

LiteLLM Cloud and Docker proxy users were unaffected because their dependencies were pinned in requirements.txt. The distinction between “pinned” and “unpinned” was the difference between compromised and safe.5

Five Ecosystems in One Week

TeamPCP did not stop at PyPI. The campaign hit five package ecosystems in rapid succession:6

GitHub Actions (March 19): 76 trivy-action tags and all 7 setup-trivy tags hijacked. Malicious Trivy binaries v0.69.4 through v0.69.6 published.

Docker Hub (March 19-22): Compromised Trivy images published as aquasec/trivy:latest and version-specific tags. Propagated to mirror.gcr.io.

Open VSX (March 23): Checkmarx KICS GitHub Action compromised. 35 tags hijacked between 12:58 and 16:50 UTC.2

npm (March 23-24): CanisterWorm, a self-propagating worm, deployed across 28+ npm packages.

PyPI (March 24): LiteLLM versions 1.82.7 and 1.82.8 published with credential-stealing payloads.

Each ecosystem compromise used credentials harvested from the previous one. The campaign was a directed graph of trust exploitation, where each compromised node yielded the keys to the next.

Supply Chain Attacks Are Composition Attacks

The structural pattern in the TeamPCP campaign is the same pattern I described in the context of agent security and silent egress: individually authorized components composing into unauthorized behavior.

Trivy was authorized to run in CI. CI was authorized to hold publishing credentials. The package manager was authorized to install packages. Each .pth file was authorized to configure Python paths. Every link in the chain operated within its defined scope. The composition of those authorized operations produced credential exfiltration at scale.

The Clinejection attack demonstrated the same pattern at the CI/CD level: an issue title injected adversarial text into Cline’s automated pipeline, which executed an npm preinstall script, which poisoned the build cache, which contaminated cross-workflow artifacts. Each step was individually authorized.11

The MCPTox benchmark demonstrated it at the agent level: malicious instructions embedded in tool descriptions redirect agents to exfiltrate credentials using legitimate tools already on the server. The agent never executes the poisoned tool itself. It reads the poisoned description and uses its own authorized tools to carry out the attack.12

Silent egress demonstrates it at the runtime level: adversarial instructions in web page metadata induce an agent to exfiltrate runtime context through an outbound HTTP request that the agent is authorized to make.13

The attack surface is not a single vulnerable component. The attack surface is the composition of trusted components. Securing individual links does not secure the chain. Trivy was not the vulnerability. LiteLLM was not the vulnerability. The vulnerability was the trust relationship between them, mediated by mutable version tags and unpinned dependencies.

What Actually Helps

The defenses that would have prevented each stage of this attack are boring. They are also the ones most organizations skip.

Pin to immutable references. LiteLLM installed Trivy via apt without version pinning. If security_scans.sh had pinned to a specific version or commit SHA, the poisoned v0.69.4 binary would never have executed. GitHub Actions should reference commit SHAs, not mutable tags. The difference between uses: aquasecurity/[email protected] (compromised) and uses: aquasecurity/trivy-action@57a97c7 (safe) is the difference between losing your publishing credentials and not.1

Isolate publishing credentials from CI runners. The PYPI_PUBLISH_PASSWORD was accessible as an environment variable in the GitHub Actions runner where Trivy ran. If publishing credentials were isolated in a separate workflow with no third-party action dependencies, the Trivy compromise would not have yielded PyPI access. PyPI’s Trusted Publishers (OIDC) eliminates stored tokens entirely.5

Monitor outbound network requests. The credential stealer exfiltrated data via HTTPS POST to models.litellm.cloud, a domain registered 24 hours before the attack. Egress monitoring that flags requests to newly registered domains would have caught this. My own hook system blocks outbound requests to domains not on an allowlist, the same pattern that would have intercepted this exfiltration.14

I learned the same lesson in production the hard way. A trusted Cloudflare cache purge path fired an authorized API call and still produced the wrong outcome because the surrounding guardrails were too loose. That failure pushed me to add destructive-action approvals and preflight hooks around high-impact operations, not just domain allowlists.

Treat pip install as code execution. A .pth file in site-packages/ executes on every Python startup. There is no opt-out. There is no sandbox. If you run pip install in a CI environment with access to credentials, you are granting arbitrary code execution to every transitive dependency in the package. The mitigation is to run pip install in an environment with no credential access, then copy the installed packages to the environment that needs them.

Use lock files. FutureSearch’s analysis found that downloads via uv (which uses lock files) were far less likely to install the compromised version than downloads via pip (which resolves to latest). Lock files are not a complete defense, but they prevent the most common exposure: an unpinned >= version constraint silently upgrading to a compromised release.4

The Uncomfortable Implication

The TeamPCP campaign demonstrates that supply chain security is not a secondary concern that sits behind application security, network security, and endpoint protection. For organizations running AI agents with tool access, the supply chain is the primary attack surface.

An AI agent that calls pip install in a CI environment, fetches URLs, or installs MCP servers is composing trust relationships at runtime. Each operation is authorized. The composition may not be. The deploy-and-defend paradox accelerates this: organizations deploy agents faster than they audit the trust chains those agents compose.

The fork bomb bug in LiteLLM 1.82.8 was the only reason this attack was detected quickly. Without that implementation error, the credential stealer would have run silently on every Python invocation on every infected system. 46,996 installations in 46 minutes. 36% of cloud environments. 2,337 downstream packages. The attacker made a mistake. Next time, they might not.


FAQ

How do I check if I was affected?

Search your CI logs and local environments for LiteLLM versions 1.82.7 or 1.82.8 installed between 10:39 and 11:25 UTC on March 24, 2026. If either version was installed, rotate all credentials that were accessible as environment variables, SSH keys, or config files on that system. LiteLLM’s official guidance recommends treating any system that ran the compromised versions as fully compromised.5

Which advisory identifier should I track?

Track PYSEC-2026-2 in OSV and the PyPA advisory database. OSV also aliases the incident as MAL-2026-2144. As of March 25, 2026, the public OSV advisory page does not list a CVE alias for the malicious LiteLLM releases, so incident response teams should key off the affected versions, the install window, and PYSEC-2026-2 first.15

Were LiteLLM Cloud or Docker proxy users affected?

No. LiteLLM Cloud and the official Docker proxy image pin dependencies in requirements.txt, preventing automatic resolution to the compromised versions. Only users who installed LiteLLM via pip install litellm (unpinned) during the 46-minute window were affected.5

What is a .pth file and why is it dangerous?

A .pth file is a Python feature for configuring module search paths. Any file ending in .pth placed in the site-packages/ directory is read and executed by Python’s site.py module during interpreter initialization. This happens before any import statement, before any application code, and before any Python-level sandbox. The mechanism is documented in the Python standard library but is not widely known among application developers. It is a legitimate feature being used as an attack vector.3

Yes. The LiteLLM compromise was a direct consequence of the Trivy compromise. TeamPCP compromised Trivy’s GitHub Actions on March 19, which harvested CI/CD credentials from organizations running Trivy in their build pipelines. One of those credentials was LiteLLM’s PyPI publishing token. The attacker used that token to publish the malicious LiteLLM versions five days later.10

What is TeamPCP?

TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce, CipherForce) is a threat group that conducted a multi-ecosystem supply chain campaign in March 2026, compromising packages across GitHub Actions, Docker Hub, npm, Open VSX, and PyPI. The group used credential harvesting, tag hijacking, and self-propagating worm techniques across five package ecosystems in approximately one week.6

How does this relate to AI agent security?

AI agents that install packages, fetch URLs, or connect to MCP servers compose trust relationships at runtime. Each operation is individually authorized. The composition of those operations can produce unauthorized behavior, as demonstrated by silent egress (agent-level), Clinejection (CI/CD-level), and this attack (supply-chain-level). Runtime behavioral monitoring, domain allowlisting, and credential isolation address the composition gap at different layers of the stack.14


Sources


  1. CrowdStrike, “From Scanner to Stealer: Inside the trivy-action Supply Chain Compromise,” March 2026. Technical analysis of tag hijacking, credential harvesting, and attack timeline. 

  2. Wiz, “Three’s a Crowd: TeamPCP Trojanizes LiteLLM in Continuation of Campaign,” March 2026. LiteLLM present in 36% of cloud environments. Full campaign progression from Trivy to KICS to LiteLLM. 

  3. Sonatype, “Compromised litellm PyPI Package Delivers Multi-Stage Credential Stealer,” March 2026. Technical analysis of the .pth file technique, payload encryption, and exfiltration mechanism. 

  4. FutureSearch (Daniel Hnyk), “LiteLLM Hack: Were You One of the 47,000?” March 2026. PyPI BigQuery analysis: 46,996 downloads in 46 minutes. 2,337 dependent packages, 88% allowing compromised versions. Downstream exposure by monthly download volume. 

  5. LiteLLM, “Security Update: Suspected Supply Chain Incident,” March 2026. Official postmortem. Confirmed Trivy as the vector. Docker/Cloud users unaffected. Mandiant engaged. 

  6. Kaspersky, “Trojanization of Trivy, Checkmarx, and LiteLLM Solutions,” March 2026. Full campaign analysis across five ecosystems. 

  7. Microsoft, “Guidance for Detecting, Investigating, and Defending Against the Trivy Supply Chain Compromise,” March 2026. Detection guidance, affected version matrix, compromise indicators. 

  8. Aqua Security, “Trivy Supply Chain Attack: What You Need to Know,” March 2026. Official incident response. Incomplete credential rotation acknowledged. 

  9. Palo Alto Networks, “When Security Scanners Become the Weapon,” March 2026. Technical breakdown of the attack. 

  10. Snyk, “How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM,” March 2026. Trivy-to-LiteLLM attack chain. Unpinned Trivy dependency in ci_cd/security_scans.sh

  11. Khan, Adnan, via Simon Willison, “Clinejection: Compromising Cline’s Production Releases,” simonwillison.net, March 2026. 

  12. Zhiqiang Wang et al., “MCPTox: A Benchmark for Tool Poisoning Attack on Real-World MCP Servers,” arXiv:2508.14925, AAAI 2026. 

  13. Lan, Qianlong et al., “Silent Egress: When Implicit Prompt Injection Makes LLM Agents Leak Without a Trace,” arXiv:2602.22450, February 2026. 

  14. Blake Crosley, “What I Told NIST About AI Agent Security,” blakecrosley.com, February 2026. 

  15. OSV / PyPA Advisory Database, “PYSEC-2026-2” published March 24, 2026. Public advisory for the malicious LiteLLM releases. Alias listed: MAL-2026-2144

Related Posts

Deploy and Defend: The Agent Trust Paradox

Every major institution is racing to deploy AI agents and building walls against them simultaneously. 1 in 8 enterprise …

17 min read

Your Agent Sandbox Is a Suggestion

An attacker opened a GitHub issue and shipped malware in Cline's next release. Agent sandboxes fail at three levels. Her…

18 min read

The Ralph Loop: How I Run Autonomous AI Agents Overnight

I built an autonomous agent system with stop hooks, spawn budgets, and filesystem memory. Here are the failures and what…

8 min read