TeamPCP Supply Chain Campaign: When the Security Scanner Became a Weapon Against AI Infrastructure

Executive Summary
On March 24, 2026, threat group TeamPCP successfully pushed two backdoored versions of the Python library LiteLLM (v1.82.7 and v1.82.8) to PyPI — a package registry serving over 95 million downloads per month. This was not an isolated incident. It was the final link in a systematic supply chain campaign running nearly a month, originating with the compromise of Trivy (a widely-used vulnerability scanner), pivoting through Checkmarx KICS, and culminating in LiteLLM — the LLM proxy library embedded in the majority of modern AI pipelines.
The payload executes immediately upon package import, or on every Python process startup via a .pth file mechanism. Targeted data includes: SSH keys, cloud credentials (AWS/GCP/Azure), Kubernetes secrets, database passwords, .env files, and cryptocurrency wallets. All harvested data is encrypted using RSA-4096/AES-256-CBC before exfiltration to attacker-controlled infrastructure.
Immediate priority action: Audit every environment for LiteLLM installations between 10:39–16:00 UTC on March 24, 2026. If found — assume compromise and rotate all credentials.
1. Campaign Timeline
| Date / Time | Event |
|---|---|
| Feb 28, 2026 | An automated bot exploits a pull_request_target misconfiguration in the Trivy repository, stealing a Personal Access Token (PAT). Aqua Security addresses the visible surface but leaves residual access intact. |
| Mar 19, 2026 | TeamPCP uses the residual PAT to force-push malicious code into 76 of 77 version tags of aquasecurity/trivy-action and all tags of setup-trivy. Payload: a cloud credential infostealer executing inside CI/CD runners. Assigned CVE-2026-33634 (CVSS 9.4). |
| Mar 20, 2026 | Aqua Security detects the attack and publishes a public disclosure. Malicious artifacts are removed from distribution channels. |
| Mar 21–22, 2026 | TeamPCP extends to Docker Hub with malicious Trivy images. Worm-like propagation observed through exposed SSH keys and Kubernetes APIs. 44 Aqua Security internal repositories are defaced. |
| Mar 23, 2026 | Using credentials harvested from Trivy, TeamPCP compromises Checkmarx GitHub Actions: kics-github-action and ast-github-action. The domain checkmarx[.]zone is activated as C2. |
| Mar 24 – 10:39 UTC | LiteLLM v1.82.7 is uploaded to PyPI using the compromised maintainer account. A 12-line obfuscated payload is injected into proxy_server.py. |
| Mar 24 – 10:52 UTC | LiteLLM v1.82.8 is pushed, adding litellm_init.pth to trigger payload execution on every Python interpreter startup. |
| Mar 24 – 11:48 UTC | Researcher Callum McMahon (FutureSearch) opens GitHub Issue #24512 after his machine crashes from a fork bomb triggered by the .pth file. |
| Mar 24 – 12:44 UTC | Attackers use the hijacked maintainer account to close Issue #24512 and flood the thread with 88 bot comments from 73 accounts in 102 seconds. |
| Mar 24 – 13:38 UTC | PyPI admins quarantine the entire litellm package. Both malicious versions are removed. Total distribution window: ~59 minutes. |
2. Attack Flow
Phase 1: Initial Access — Trivy CI/CD Compromise
TeamPCP exploited the pull_request_target GitHub Actions workflow — a common configuration that allows PRs from fork repositories to run in the context of the base repository, with access to its secrets. After stealing the PAT, the group executed an imposter commit attack: force-pushing malicious code into all existing version tags.
The consequence: any pipeline using uses: aquasecurity/trivy-action@v0.x.x — including previously pinned versions — began executing attacker code starting March 19. Pipelines referencing version tags had no indication anything had changed; release metadata appeared untouched.
Phase 2: Credential Harvesting & Lateral Movement
kamikaze.sh — the primary payload deployed in the Trivy wave — evolved through three versions:
- Version 1: A 150-line bash script performing environment fingerprinting and harvesting AWS/GCP/Azure credentials via the Instance Metadata Service (IMDS)
- Version 2: Added SSH key extraction,
.envfile scanning, and Kubernetes secret enumeration - Version 3: A multi-stage dropper with persistence mechanisms
The credential chain: Trivy compromise → stolen credentials unlock Checkmarx KICS → the PYPI_PUBLISH token for LiteLLM is exfiltrated from a GitHub Actions runner running a Trivy scan as part of its own CI/CD workflow.
Phase 3: PyPI Package Poisoning — LiteLLM
Attackers injected 12 lines of code into litellm/proxy/proxy_server.py at line 128, placed between two unrelated legitimate code blocks — a steganography-style technique designed to evade manual code review:
# Malicious injection at proxy_server.py:128
# Base64-encoded payload decoded and executed via subprocess
# Avoids exec() to bypass static analysis tools
import base64, subprocess, tempfile, os
_d = base64.b64decode("<REDACTED_PAYLOAD>")
_f = tempfile.NamedTemporaryFile(delete=False, suffix='.py')
_f.write(_d); _f.close()
subprocess.Popen(['python3', _f.name], ...)
Critically, the GitHub source at the corresponding commit does not contain these lines — the injection occurred during or after the wheel build process, after the source was published to the repository. Source-level code review would not have caught it.
v1.82.8 adds litellm_init.pth:
# litellm_init.pth — placed in site-packages
# Python's site.py processes .pth files automatically on every interpreter startup
import subprocess; subprocess.Popen(['python3', '-c', '<payload>'], ...)
The .pth mechanism is particularly aggressive: no import litellm required — as long as litellm is installed in the environment, the payload runs on every Python process startup. This is why McMahon's machine crashed: the child process triggers the .pth → spawns another child → exponential fork bomb. The fork bomb was an unintentional bug in the malware. Without it, the campaign would likely have gone undetected far longer.
Phase 4: Multi-Stage Payload Execution
Stage 1 — Credential Collection:
The payload harvests and packages the following into tpcp.tar.gz:
~/.ssh/id_rsa, id_ed25519, config
~/.aws/credentials, ~/.aws/config
~/.config/gcloud/*, ~/.azure/*
~/.kube/config
**/.env files
~/.gitconfig, ~/.docker/config.json
Database passwords (PostgreSQL, MySQL)
Cryptocurrency wallet files
The archive is encrypted: AES-256-CBC with a random session key, the session key encrypted with a hardcoded RSA-4096 public key, then POST-ed to https://models[.]litellm[.]cloud/ — a domain deliberately constructed to resemble LiteLLM's legitimate infrastructure.
Stage 2 — Kubernetes Lateral Movement:
If a Kubernetes service account token is present on the compromised host:
# Payload enumerates all nodes in the cluster
kubectl get nodes --all-namespaces
# Deploys a privileged pod to EVERY node in kube-system
# Pod mounts the host filesystem (/)
# Installs persistence dropper across all nodes
Stage 3 — Persistent Backdoor:
~/.config/sysmon/sysmon.py # Python backdoor script
~/.config/systemd/user/sysmon.service # Systemd service
Service display name: "System Telemetry Service"
The backdoor polls C2 every 50 minutes: GET checkmarx[.]zone/raw → receives a URL → downloads and executes the next-stage payload. Kill switch: if the response contains the string youtube.com, execution halts — an anti-sandbox evasion technique.
In Kubernetes environments, this persistence mechanism is deployed across every node in the cluster via privileged pods mounting the host filesystem.
3. Indicators of Compromise (IOC)
Network IOCs
# C2 Domains
models[.]litellm[.]cloud # Exfiltration endpoint (impersonates LiteLLM)
checkmarx[.]zone # C2 backdoor polling (impersonates Checkmarx)
scan[.]aquasecurtiy[.]org # Trivy wave C2 (typosquat of aquasecurity)
# Registrar: Spaceship, Inc. | Hosting: DEMENIN B.V.
Host-Based IOCs
# Files
~/.config/sysmon/sysmon.py
~/.config/systemd/user/sysmon.service
/tmp/pglog # Fake PostgreSQL log
/tmp/.pg_state # State file
tpcp.tar.gz # Credential archive in tmp directories
# Python environment
litellm_init.pth (in site-packages)
proxy_server.py (modified, if running litellm 1.82.7 or 1.82.8)
Package Indicators
# Malicious versions
litellm==1.82.7 (uploaded 2026-03-24 10:39:24 UTC)
litellm==1.82.8 (uploaded 2026-03-24 10:52:19 UTC)
# Last known-clean version
litellm==1.82.6 (published 2026-03-22, verified clean by Endor Labs)
GitHub IOCs
# Repository patterns created by attacker in victim org
docs-tpcp-*
# Commit message indicator
"teampcp update"
# Attacker Telegram channels
@Persy_PCP
@teampcp
Detection — Check for Exposure
# Check installed LiteLLM version
pip show litellm 2>/dev/null | grep -E "^Version|^Location"
pip list --format=freeze | grep litellm
# Search for the malicious .pth file
find ~/.cache/uv -name "litellm_init.pth" 2>/dev/null
find /usr -name "litellm_init.pth" 2>/dev/null
# Check for persistence artifacts
[ -f ~/.config/sysmon/sysmon.py ] && echo "COMPROMISED: sysmon.py found"
[ -f ~/.config/systemd/user/sysmon.service ] && echo "COMPROMISED: sysmon.service found"
# Search for credential archive
find /tmp -name "tpcp.tar.gz" 2>/dev/null
SIEM / Network Detection
# Splunk / Elastic
dest_domain IN ("models.litellm.cloud", "checkmarx.zone", "scan.aquasecurtiy.org")
AND (http_method="POST" OR http_method="GET")
# Suricata — flag outbound POST to exfiltration domain
alert http any any -> any any (
msg:"TeamPCP LiteLLM Exfiltration Attempt";
content:"models.litellm.cloud"; http_header;
content:"POST"; http_method;
sid:2026001; rev:1;
)
4. MITRE ATT&CK Mapping
| Tactic | Technique | Detail |
|---|---|---|
| Initial Access | T1195.002 — Compromise Software Supply Chain | Trojanized LiteLLM PyPI package |
| Initial Access | T1078.004 — Valid Accounts: Cloud Accounts | Hijacked PyPI maintainer account |
| Execution | T1059.006 — Python | Python payload executed via subprocess |
| Execution | T1072 — Software Deployment Tools | Triggered via pip install / Python startup |
| Persistence | T1546.004 — .pth hijack | litellm_init.pth executes on every Python startup |
| Persistence | T1543.002 — Systemd Service | sysmon.service persistent backdoor |
| Defense Evasion | T1027 — Obfuscated Files/Information | Base64-encoded payload, subprocess instead of exec() |
| Defense Evasion | T1036.004 — Masquerade Task/Service | "System Telemetry Service", fake PostgreSQL process |
| Credential Access | T1552.001 — Credentials in Files | Harvests .env, .aws, .ssh, .kube/config |
| Credential Access | T1552.004 — Private Keys | SSH private keys |
| Discovery | T1082 — System Information Discovery | Environment fingerprinting |
| Lateral Movement | T1610 — Deploy Container | Privileged pods deployed across all Kubernetes nodes |
| Collection | T1560.001 — Archive via Utility | Credentials packaged into tpcp.tar.gz |
| Exfiltration | T1048.003 — Exfiltration Over HTTPS | Encrypted POST to models.litellm.cloud |
| C2 | T1071.001 — Web Protocols | Backdoor HTTP polling every 50 minutes |
| C2 | T1568 — Dynamic Resolution | ICP canister as dead-drop C2 (CanisterWorm variant) |
5. Threat Actor Profile: TeamPCP
TeamPCP (also tracked as PCPcat, Persy_PCP, ShellForce, DeadCatx3 per Wiz Threat Center) has been active since at least December 2025. The group embeds the string "TeamPCP Cloud stealer" directly inside their payloads — making no attempt to hide attribution. Whether this reflects operational confidence or deliberate reputation-building is unclear.
Defining characteristic: Each target is not an end goal — it's a pivot point to the next one. Trivy yielded credentials to reach Checkmarx KICS; KICS yielded the PYPI_PUBLISH token for LiteLLM. This is deliberate credential chaining, not opportunistic targeting.
Target selection logic: TeamPCP exclusively compromises security-adjacent tools — vulnerability scanners (Trivy), IaC analyzers (KICS), LLM proxies (LiteLLM). The rationale is straightforward: these tools run with elevated privileges by design and hold broad access to credentials and infrastructure. Compromising a security tool means inheriting everything that tool was authorized to see.
Notable capabilities:
- Uses an AI agent (openclaw) for automated attack targeting — documented by Aikido as one of the first observed uses of an AI agent operationally in a supply chain campaign
- CanisterWorm leverages Internet Computer Protocol (ICP) as C2 — a decentralized layer that cannot be taken down by domain registrars or hosting providers
- The botnet for comment flooding used previously compromised developer accounts rather than purpose-created profiles — 76% account overlap with the botnet deployed during the Trivy disclosure (analysis by Rami McCarthy)
- The 13-minute gap between v1.82.7 and v1.82.8 shows the attacker was actively iterating during the attack window, not executing a pre-planned script
The LiteLLM compromise is Phase 09 of an ongoing campaign. Endor Labs assesses with high confidence that this campaign is not over.
6. Analysis
Technically, the LiteLLM attack is not especially sophisticated — the payload is straightforward, the .pth mechanism is well-documented, the encryption scheme is standard. What makes this incident significant is the strategic position LiteLLM occupies in modern AI infrastructure.
LiteLLM is not an ordinary library. It is an LLM gateway — its entire function is to hold API keys for dozens of providers simultaneously: OpenAI, Anthropic, Google Vertex, AWS Bedrock, Azure OpenAI. A single compromised LiteLLM instance doesn't lose one credential set; it loses the entire AI stack's credential surface. That is exactly why TeamPCP targeted it. The ROI per compromise is unusually high.
The most uncomfortable observation from a SOC perspective: this incident was not detected by EDR, SIEM, or any security tooling. It was caught by a developer who noticed his machine had frozen due to a fork bomb — an unintentional bug in the malware. Without that bug, this campaign would likely still be running. Security teams should resist the comfort of "we have EDR coverage." Does your EDR monitor developer laptops when they work remotely? Can it detect an outbound HTTPS POST to a domain that looks entirely legitimate — like models.litellm.cloud?
According to Wiz Research, LiteLLM is present in 36% of monitored cloud environments. If your organization has deployed AI agents, MCP servers, or any LLM orchestration tooling in the past six months, LiteLLM is likely somewhere in your transitive dependency tree — regardless of whether anyone explicitly installed it.
The broader pattern to recognize: this campaign represents an escalation from build-time attacks (CI/CD pipelines) to runtime attacks (developer machines and production environments). Traditional supply chain defense concentrates on the build pipeline. TeamPCP has pivoted directly to where credentials actually live — developer workstations. This shift demands a corresponding adjustment in threat modeling.
7. Recommendations
Immediate (0–24h)
Assess exposure:
# Check installed LiteLLM version
pip show litellm 2>/dev/null | grep -E "^Version|^Location"
pip list --format=freeze | grep litellm
# Search all virtual environments
find / -name "litellm_init.pth" 2>/dev/null
find / -path "*/litellm/proxy/proxy_server.py" -exec grep -l "tpcp\|base64" {} \; 2>/dev/null
# Check for persistence
[ -f ~/.config/sysmon/sysmon.py ] && echo "COMPROMISED: sysmon.py found"
[ -f ~/.config/systemd/user/sysmon.service ] && echo "COMPROMISED: sysmon.service found"
If v1.82.7 or v1.82.8 was ever installed:
# Remove package and purge cache
pip uninstall litellm -y
pip cache purge
rm -rf ~/.cache/uv
# Reinstall from last clean version
pip install litellm==1.82.6
Rotate credentials in priority order:
- PyPI tokens (if you are a package maintainer)
- Cloud provider credentials (AWS IAM keys, GCP service accounts, Azure service principals)
- Kubernetes kubeconfig and service account tokens
- SSH keys
- API keys in
.envfiles - Database credentials
- GitHub Personal Access Tokens
Review network logs for traffic to:
models.litellm.cloud
checkmarx.zone
scan.aquasecurtiy.org
Audit Kubernetes cluster (if applicable):
# Look for unauthorized pods in kube-system
kubectl get pods -n kube-system | grep -E "node-setup|alpine"
# Check for unexpected secret access events
kubectl get events --all-namespaces | grep -i "secret"
# Search for attacker-created repositories
gh api /orgs/<YOUR_ORG>/repos | jq '.[].name' | grep docs-tpcp
Short-term (1–7 days)
Full dependency audit:
# Find all LiteLLM references across the codebase
grep -r "litellm" . --include="*.txt" --include="*.toml" --include="*.cfg"
# Audit transitive dependencies
pip-audit --requirement requirements.txt
# or
safety check -r requirements.txt
Pin versions explicitly in all dependency files:
# requirements.txt
litellm==1.82.6 # verified clean, explicitly pinned
Audit CI/CD pipelines for Trivy and Checkmarx usage:
# Replace floating version tags with commit SHAs
# BEFORE (unsafe)
uses: aquasecurity/trivy-action@v0.20.0
# AFTER (safe — pinned to commit SHA)
uses: aquasecurity/trivy-action@9b2b452e66... # verify SHA from official channel
Add network egress monitoring: Restrict outbound connections from build environments to explicitly whitelisted domains. On developer endpoints, flag unusual outbound HTTPS POST requests — particularly to recently registered domains.
Long-term
Adopt a dependency pinning strategy across the board:
- Use lock files (
poetry.lock,uv.lock, hashedrequirements.txt) for all production deployments - Implement a private package mirror (Artifactory, Nexus) with malware scanning before packages enter internal environments
- Automate SBOM generation in CI/CD to maintain a complete transitive dependency inventory
Harden CI/CD pipelines:
- Never expose
GITHUB_TOKENor long-lived secrets in workflows triggered bypull_request_targetfrom forks - Pin all GitHub Actions to commit SHAs — version tags can be force-pushed without warning
- Replace long-lived PyPI publishing tokens with OIDC-based short-lived tokens
Build detection capability:
- Deploy EDR with visibility on developer endpoints, not only production servers
- Integrate PyPI malware feeds (Sonatype OSS Index, Checkmarx SCA, Snyk) as a CI/CD quality gate
- Alert on
.pthfile creation in Pythonsite-packagesdirectories - Monitor for newly created systemd user services on Linux endpoints
Establish AI tooling governance:
- Inventory all AI-related packages in active use across the organization
- Assign human ownership to every AI agent or tool in operation
- Apply least-privilege to AI agent credentials — use ephemeral, sender-constrained tokens rather than long-lived API keys
- Do not connect AI agents to production systems during the experimentation phase
8. References
- Orca Security — LiteLLM Supply Chain Attack: Malware & Mitigation (2026-03-24)
- Endor Labs — TeamPCP Isn't Done: Threat Actor Behind Trivy and KICS Compromises Now Hits LiteLLM's 95 Million Monthly Downloads on PyPI (2026-03-24)
- Snyk — How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM (2026-03-26)
- FutureSearch / Callum McMahon — litellm 1.82.8 Supply Chain Attack on PyPI (March 2026) (2026-03-24)
- Palo Alto Networks Unit 42 — Weaponizing the Protectors: TeamPCP's Multi-Stage Supply Chain Attack on Security Infrastructure (2026-04-01)
- Arctic Wolf — TeamPCP Supply Chain Attack Campaign Targets Trivy, Checkmarx (KICS), and LiteLLM (2026-03-24)
- Kaspersky — Trojanization of Trivy, Checkmarx, and LiteLLM solutions (2026-04-03)
- SANS Institute — When the Security Scanner Became the Weapon: Inside the TeamPCP Supply Chain Campaign (2026-04-03)
- LiteLLM Official — Security Update: Suspected Supply Chain Incident (2026-03-24)
- GitGuardian — How GitGuardian Enables Rapid Response to the LiteLLM Supply Chain Attack (2026-03-30)
- Sonatype — Compromised litellm PyPI Package Delivers Multi-Stage Credential Stealer (2026-04-03)
- InfoQ — PyPI Supply Chain Attack Compromises LiteLLM, Enabling the Exfiltration of Sensitive Information (2026-04-01)





