How It Was Discovered

Security researcher Callum McMahon at FutureSearch found the compromise almost by accident. While testing a Cursor MCP plugin, his machine suddenly experienced severe RAM exhaustion shortly after Python started. Investigating the anomaly, he found a suspicious 34,628-byte file named litellm_init.pth in Python’s site-packages directory, encoded in double base64.

The reason it was noticeable at all was an unintended bug in the malware itself — the .pth payload spawned Python subprocesses that recursively triggered the .pth startup mechanism, creating a fork bomb that consumed all memory. Without that bug, the backdoor would have operated silently.

The Attack Chain

This was a multi-stage software supply chain compromise executed by a threat actor known as TeamPCP (aliases: PCPcat, Persy_PCP, ShellForce, DeadCatx3). Here’s how it unfolded.

Phase 1 — Compromise of Trivy (the Security Scanner)

In late February 2026, attackers exploited a pull_request_target workflow vulnerability in Aqua Trivy’s CI/CD pipeline to exfiltrate the aqua-bot credentials. On March 19, they rewrote Git tags in trivy-action to redirect users to a malicious release v0.69.4.

Yes — the security scanner itself was the entry point.

Phase 2 — Poisoning LiteLLM via Its Own CI/CD

On March 24, LiteLLM’s build pipeline used Trivy from apt without version pinning. When it ran the compromised Trivy GitHub Action, the action exfiltrated LiteLLM’s PYPI_PUBLISH token.

Using the stolen token, attackers published two malicious PyPI versions:

  • v1.82.7 (10:39 UTC) — Embedded malicious base64 payload directly in litellm/proxy/proxy_server.py, executing on import.
  • v1.82.8 (10:52 UTC) — Deployed litellm_init.pth to site-packages, which executes on every Python interpreter startup — no import needed. This includes pip, python -c, and even IDE language servers.

PyPI quarantined both versions roughly 3 hours later, but LiteLLM receives ~3.4 million downloads/day.

Phase 3 — Three-Stage Malware Payload

Once installed, the malware executed a devastating three-stage payload:

StageAction
Stage 1: Credential HarvestingCollected SSH keys, .env files, Git configs, shell history, cloud credentials (AWS/GCP/Azure including IMDSv2 + Secrets Manager + SSM), Docker registry creds, Kubernetes kubeconfig + service account tokens, and even cryptocurrency wallets (BTC, ETH, SOL, ADA, XMR)
Stage 2: Encrypted ExfiltrationUsed hybrid encryption (AES-256-CBC + 4096-bit RSA OAEP), bundled everything into tpcp.tar.gz, and POSTed to https://models.litellm.cloud/
Stage 3: Persistence & Lateral MovementInstalled a systemd service (sysmon.service) polling https://checkmarx.zone/raw every 5 minutes for new payloads. In Kubernetes environments, it read all secrets across namespaces and deployed privileged pods (node-setup-{node}) to every node, mounting host filesystems to backdoor underlying infrastructure

Mitigation Plan — Step by Step

If you used LiteLLM in late March 2026, here’s exactly what to do.

Step 1: Determine If You’re Affected

# Check installed LiteLLM version
pip show litellm | grep Version

# If version is 1.82.7 or 1.82.8, you are affected

Step 2: Check for Malware Artifacts

# Check for the malicious .pth file in site-packages
find $(python3 -c "import site; print(' '.join(site.getsitepackages()))") \
  -name "*.pth" -exec grep -l "base64\|subprocess\|exec" {} \;

# Verify file hashes (if files exist)
# litellm_init.pth (v1.82.8):
sha256sum $(python3 -c "import site; print(site.getsitepackages()[0])")/litellm_init.pth
# Expected malicious hash: 71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238

# Check for persistence backdoor
ls -la ~/.config/sysmon/sysmon.py
ls -la /root/.config/sysmon/sysmon.py

# Check for the malicious systemd service
systemctl --user status sysmon.service

# Check for network indicators
# Look for connections to attacker infrastructure
ss -tunap | grep -E "models\.litellm\.cloud|checkmarx\.zone"

# Check DNS resolution logs if available
grep -r "models.litellm.cloud\|checkmarx.zone" /var/log/

# Kubernetes: Check for malicious pods
kubectl get pods -A | grep "node-setup-"

# Check for temporary malware files
ls -la /tmp/pglog /tmp/.pg_state 2>/dev/null

Step 3: Remove Malware (If Compromised)

# Uninstall compromised LiteLLM
pip uninstall litellm -y

# Remove the malicious .pth file
find $(python3 -c "import site; print(' '.join(site.getsitepackages()))") \
  -name "litellm_init.pth" -delete

# Remove persistence mechanism
rm -rf ~/.config/sysmon/
systemctl --user stop sysmon.service
systemctl --user disable sysmon.service
rm -f ~/.config/systemd/user/sysmon.service
systemctl --user daemon-reload

# Remove temp files
rm -f /tmp/pglog /tmp/.pg_state

# Kubernetes: Remove malicious pods
kubectl delete pods -n kube-system -l app=node-setup --all-namespaces

# Install a safe version
pip install "litellm<=1.82.6"

Step 4: Rotate ALL Credentials (Critical)

This is the most important step. The malware exfiltrated everything it could find — assume any credential on the affected machine is compromised.

# SSH Keys — regenerate
ssh-keygen -t ed25519 -C "[email protected]"
# Update authorized_keys on all servers

# AWS credentials
aws iam create-access-key --user-name <username>
aws iam delete-access-key --user-name <username> --access-key-id <OLD_KEY>

# Audit AWS Secrets Manager for unauthorized access
aws secretsmanager list-secrets
aws cloudtrail lookup-events --lookup-attributes \
  AttributeKey=EventName,AttributeValue=GetSecretValue \
  --start-time "2026-03-24T00:00:00Z"

# Audit AWS SSM Parameter Store
aws ssm describe-parameters
aws cloudtrail lookup-events --lookup-attributes \
  AttributeKey=EventName,AttributeValue=GetParameter \
  --start-time "2026-03-24T00:00:00Z"

# GCP — rotate service account keys
gcloud iam service-accounts keys list --iam-account=<SA_EMAIL>
gcloud iam service-accounts keys create new-key.json --iam-account=<SA_EMAIL>
gcloud iam service-accounts keys delete <OLD_KEY_ID> --iam-account=<SA_EMAIL>

# Docker registry credentials
docker logout
docker login  # with new credentials

# Kubernetes — rotate service account tokens
kubectl delete secret <sa-token-secret> -n <namespace>

# Git tokens / API keys
# Rotate all tokens found in: .env, .git/config, shell history
# Check what was exposed:
grep -r "API_KEY\|SECRET\|TOKEN\|PASSWORD" ~/.bashrc ~/.zshrc ~/.bash_history ~/.env

Step 5: Pin Dependencies Going Forward

# Pin litellm in requirements.txt
echo "litellm<=1.82.6" >> requirements.txt

# Use hash-pinning for maximum security
pip install --require-hashes -r requirements.txt

# Generate hashes for your requirements
pip-compile --generate-hashes requirements.in

Lessons Learned

Supply Chain Attacks Are Transitive

A security scanner (Trivy) — the very tool meant to protect you — became the attack vector. LiteLLM didn’t have a direct vulnerability; it was compromised through its CI/CD dependency on a compromised security tool. Trust is transitive and fragile.

.pth Files Are a Dangerous Python Mechanism

The .pth startup hook technique (MITRE ATT&CK T1546.018) executes on every Python interpreter startup without any import. Most developers don’t know this mechanism exists, yet it’s been present in Python for decades. It bypasses all standard application-level security controls.

Hash Verification Is Insufficient for Supply Chain Attacks

Standard integrity checks (pip’s hash verification, wheel RECORD files) all passed because the malicious content was published using legitimately stolen credentials. The package was “authentic” from PyPI’s perspective.

Unpinned Dependencies in CI/CD Are Critical Risks

LiteLLM pulled Trivy without version pinning, meaning any upstream compromise automatically propagated. Every dependency in a build pipeline should be pinned to specific versions and verified.

Accidental Detection Is Not a Strategy

The malware was only discovered because of a bug in the attacker’s code (the fork bomb). A properly functioning backdoor could have operated undetected for weeks or months across millions of installs.

Community Response Can Be Suppressed

Attackers used 88 bot comments from 73 compromised accounts in 102 seconds and hijacked maintainer accounts to close disclosure issues. Having multiple communication channels and an incident response plan is essential.

What This Means for the Future

AI-Assisted Attack Automation

TeamPCP already operates hackerbot-claw, an AI agent system for automated targeting. Future attackers will use AI to automatically discover CI/CD misconfigurations at scale, generate more sophisticated payloads, and adapt malware in real-time based on the target environment.

Decentralized C2 Infrastructure

The attackers used Internet Computer Protocol (ICP) canisters as C2 infrastructure (CanisterWorm), making it resilient to traditional domain takedowns. Expect future attacks to increasingly use blockchain/decentralized infrastructure that cannot be seized.

Weaponization of Security Tools

This attack specifically targeted security scanners (Trivy, Checkmarx KICS) — tools that inherently run with elevated privileges in CI/CD pipelines. This “trust inversion” pattern will likely be replicated against SAST/DAST scanners, container image scanners, dependency audit tools, and code signing infrastructure.

Kubernetes-Native Worm Propagation

The lateral movement capability — automatically deploying privileged pods across every node — demonstrates that cloud-native infrastructure creates new worm propagation paths. Future variants could target service meshes (Istio/Linkerd sidecars), GitOps controllers (ArgoCD/Flux), and admission webhooks.

The .pth Class of Pre-Import Hooks

Every language ecosystem has analogous mechanisms: Node.js --require, Ruby’s $LOAD_PATH manipulation, Java agents. Expect similar techniques to emerge across ecosystems where startup hooks can execute code before the application is even aware.

Key Takeaways

This incident is a wake-up call that modern software supply chains have deep, transitive trust relationships. The attacker compromised a security tool to compromise a build pipeline to compromise a package to compromise end-user systems — a four-hop chain that traditional security models are not designed to detect.

What you can do today:

  1. Pin every dependency in your CI/CD pipeline — including security scanners
  2. Use hash-pinned requirements (pip-compile --generate-hashes) for Python projects
  3. Audit .pth files in your site-packages regularly
  4. Monitor for anomalous process behavior — fork bombs, unexpected network connections, new systemd services
  5. Treat credential rotation as mandatory after any suspected supply chain compromise — not optional

References