On 24 March 2026, two versions of the litellm Python package on PyPI were found to contain malicious code. The packages (versions 1.82.7 and 1.82.8) were published by a threat actor known as TeamPCP after they obtained the maintainer's PyPI credentials through a prior compromise of Trivy, an open source security scanner used in litellm's CI/CD pipeline.
The malicious versions were available for approximately three hours before PyPI quarantined the package. litellm is downloaded roughly 3.4 million times per day.
Snyk has been tracking this incident. The vulnerability record is SNYK-PYTHON-LITELLM-15762713, and status updates are on the Snyk Trust Center.[1]
|
TL |
DR |
|
Affected package |
litellm (PyPI) |
|
Affected versions |
1.82.7, 1.82.8 |
|
Safe versions |
≤ 1.82.6 |
|
Snyk ID |
|
|
First detected |
10:39 UTC, March 24, 2026 (1.82.7 upload) |
|
PyPI quarantine |
\~13:38 UTC, March 24, 2026 |
|
Attacker |
TeamPCP (also: PCPcat, Persy_PCP, ShellForce, DeadCatx3) |
|
Attack vector |
Supply chain: compromised PyPI publisher credentials via poisoned Trivy GitHub Action in LiteLLM CI/CD |
|
Payload type |
Three-stage: credential harvester + encrypted exfiltration + persistent backdoor + Kubernetes worm |
|
Exfiltration domain |
models.litellm.cloud (registered March 23, 2026) |
|
MITRE ATT\&CK |
T1546.018 (Python Startup Hooks), T1003 (Credential Dumping), T1610 (Deploy Container) |
Leading Events
|
Time (UTC) |
Evidence |
Event |
|
Late Feb 2026 |
MegaGame10418 Pwn Request against Trivy's CI exploits a pull_request_target workflow to exfiltrate the aqua-bot credentials |
|
|
Mar 19, 17:43 UTC |
Trivy v0.69.4 GitHub Action tags rewritten to point to a malicious release |
|
|
Mar 23, 12:58 UTC |
Endor Labs (captured pre-deletion PyPI metadata) |
Checkmarx KICS GitHub Action compromised; checkmarx.zone C2 domain and models.litellm.cloud registered |
|
Mar 24, 10:39 UTC |
Endor Labs (captured pre-deletion PyPI metadata) |
Malicious litellm 1.82.7 published to PyPI |
|
Mar 24, 10:52 UTC |
Malicious litellm 1.82.8 published to PyPI (13 minutes after 1.82.7, with escalated .pth delivery mechanism) |
|
|
Mar 24, 11:48 UTC |
FutureSearch (Callum McMahon) opens disclosure issue |
|
|
Mar 24, 12:36 UTC |
HN thread posted; reaches 324 points |
|
|
Mar 24, \~12:44 UTC |
GitHub issue #24512 (visible in comment timestamps) |
Bot comments flood issue #24512; issue closed using the compromised maintainer account |
|
Mar 24, 13:03 UTC |
FutureSearch (timestamped update) |
FutureSearch confirms issue closure and bot spam |
|
Mar 24, 13:48 UTC |
Clean tracking issue opened |
|
|
Mar 24, 15:09 UTC |
LiteLLM maintainer confirms all GitHub, Docker, and PyPI keys rotated; maintainer accounts moved to new identities |
|
|
Mar 24, 15:27 UTC |
Compromised versions deleted; package unquarantined on PyPI |
How it was discovered
Callum McMahon at FutureSearch was testing a Cursor MCP plugin that pulled in litellm as a transitive dependency. Shortly after Python started, his machine became unresponsive due to RAM exhaustion. He traced it to the newly installed litellm package and found litellm_init.pth, a 34,628-byte file in site-packages/, double base64-encoded.
The RAM exhaustion was a side effect of the payload, not an intentional feature. The .pth mechanism fires on every Python interpreter startup. Because the payload spawns a new Python subprocess, which also triggers .pth execution, the result was an unintended fork bomb. McMahon published his findings on futuresearch.ai, and the disclosure spread to r/LocalLLaMA, r/Python, and Hacker News within the hour.
The attack chain - The attack on LiteLLM started five days earlier with Trivy.
19 March: The attackers rewrote Git tags in the trivy-action GitHub Action repository to point to a malicious release (v0.69.4) carrying the same credential-harvesting payload and exfiltration infrastructure used in later operations. (For full details on the Trivy compromise, see Snyk's Trivy GitHub Actions supply chain compromise coverage.)
23 March: The same infrastructure was used in a separate attack on Checkmarx KICS (Keep Infrastructure as Code Secure). The C2 domain checkmarx.zone, which impersonates the Checkmarx security company, was registered and activated in this operation.
24 March: LiteLLM's CI/CD pipeline ran Trivy as part of its build process, pulling it from apt without a pinned version. The compromised action exfiltrated the PYPI_PUBLISH token from the GitHub Actions runner environment. With that credential, the attackers published litellm 1.82.7 at 10:39 UTC and 1.82.8 at 10:52 UTC, each containing malicious payloads.
Initial LiteLLM maintainer response on Hacker News.
Two Delivery Mechanisms - The two malicious versions used different injection techniques:
1.82.7 (source injection): The payload was base64-encoded and embedded directly inside litellm/proxy/proxy_server.py It executes when anything imports litellm.proxy, the standard import path for LiteLLM's proxy server mode.
1.82.8 (.pth file): This version added litellm_init.pth to site-packages/. The .pth mechanism fires on every Python interpreter startup with no import required, including when pip, python -c, or an IDE's language server starts Python.
The .pth file is correctly declared in the wheel's RECORD file, so it passes pip's hash verification. The package passes all standard integrity checks because the malicious content was published using legitimate credentials, not injected after the fact. Snyk Learn has a detailed walkthrough of how this class of attack works in Compromise of a Legitimate Package.
CPython maintainers have acknowledged the security risk of .pth startup hooks in issue #113659, but no patch has been applied. This technique maps to MITRE ATT\&CK T1546.018 (Python Startup Hooks).
The three-stage payload
Stage 1: Information collection
The script collects:
System data: hostname, environment variables, running processes, network routing, recent auth logs from /var/log/auth.log
Credentials: SSH private keys, .env files, Git credentials, API keys from shell history, Slack and Discord webhook tokens, CI/CD configs (Jenkins, Travis CI, Terraform), password hashes from /etc/shadow
Cloud credentials: AWS credentials and config, GCP and Azure service account files, cloud metadata via direct IMDS queries, AWS Secrets Manager, and SSM Parameter Store (with full IMDSv2 signing implemented in the script)
Container and orchestration: Docker config.json (registry credentials), Kubernetes kubeconfig files, service account tokens, secrets, and certificates
Cryptocurrency: Bitcoin, Ethereum, Solana, Cardano, Monero wallet files and seed phrases, Ledger hardware wallet configurations
Portion of the decoded malicious code, showing structured iteration and collection of secrets in various directories
Stage 2: Encryption and exfiltration
Collected data is encrypted before transmission:
A 32-byte AES-256 session key is generated via openssl rand
Data is encrypted with AES-256-CBC (PBKDF2 key derivation)
The session key is encrypted with a hardcoded 4096-bit RSA public key (OAEP padding)
Everything is bundled into tpcp.tar.gz
The bundle is POST'd to https://models.litellm.cloud/ via curl
models.litellm.cloud is distinct from the legitimate litellm.ai domain and was registered on March 23, one day before the malicious packages were published.
The RSA public key in this payload is identical to that in the Trivy and KICS payloads. Wiz researchers confirmed this match across all three operations, making it the strongest single technical attribution link.
Temporary files created during exfiltration: session.key, payload.enc, session.key.enc, tpcp.tar.gz in the system temp directory.
Defanged view of the malicious code. The tpcp.tar.gz archive name is a consistent marker across the Trivy, KICS, and LiteLLM operations.
Stage 3: Persistence and lateral movement
Local persistence:
Writes a backdoor Python script to ~/.config/sysmon/sysmon.py (or /root/.config/sysmon/sysmon.py)
Creates a systemd user service at ~/.config/systemd/user/sysmon.service with the description "System Telemetry Service."
Registers and enables it: systemctl --user daemon-reload && systemctl --user enable --now sysmon.service
The persistence script polls https://checkmarx.zone/raw every 5 minutes for a URL, downloads the file to /tmp/pglog, makes it executable, and runs it in the background. State is tracked in /tmp/.pg_state. At the time of analysis, the endpoint returns YouTube URLs; the operator can switch to delivering live payloads at will.
Kubernetes lateral movement: If the script finds a Kubernetes service account token at the standard mount path, it reads all secrets across every namespace. It then attempts to deploy a privileged pod to every node in the kube-system namespace using alpine:latest. These pods mount the host filesystem and install the sysmon backdoor on the underlying node.
Malicious pods are named node-setup-{node_name} (node name truncated to 35 characters), with a container named setup.
About TeamPCP
TeamPCP (also identified as PCPcat, Persy_PCP, ShellForce, and DeadCatx3 per Wiz Threat Center) has been active since at least December 2025. The actor maintains Telegram channels at @Persy_PCP and @teampcp and embeds the string "TeamPCP Cloud stealer" in payloads. Wiz has tracked the full campaign (Wiz Threat Center; Wiz blog; ramimac.me).
The LiteLLM compromise is Phase 09 of an ongoing campaign. Consistent infrastructure across all operations: same RSA key pair, same tpcp.tar.gz bundle naming, and tpcp-docs-prefixed GitHub repositories used as dead-drop C2 staging. All three domains in this operation share the same registrar (Spaceship, Inc.) and hosting provider (DEMENIN B.V.).
The actor has also deployed CanisterWorm, which uses the Internet Computer Protocol (ICP) as a C2 channel. ICP canisters cannot be taken down by domain registrars or hosting providers. Aikido security researchers document this as the first observed use of ICP as a C2 mechanism in a supply chain campaign.
A component called hackerbot-claw uses an AI agent (openclaw) for automated attack targeting. Aikido researchers documented this as one of the first cases of an AI agent used operationally in a supply chain attack.
Issue suppression
When community members began reporting the compromise in GitHub issue #24512, the attackers posted 88 bot comments from 73 unique accounts in a 102-second window (12:44-12:46 UTC). The accounts used were previously compromised developer accounts, not purpose-created profiles. Rami McCarthy's analysis found 76% account overlap with the botnet used during the Trivy disclosure.
Using the compromised krrishdholakia maintainer account, the attackers closed issue #24512 as "not planned" and made commits to unrelated repositories with the message "teampcp update."
The community opened a parallel tracking issue (#24518) and continued discussion on Hacker News, where the thread reached 324 points.
Confirmed impact
The affected versions were on PyPI for approximately 3 hours. The following projects filed security PRs or issues on March 24 to pin away from 1.82.7 and 1.82.8:
|
Project |
Evidence |
|
DSPy |
PR #9498 merged; CI failure report |
|
MLflow |
PR #21971 merged |
|
OpenHands |
|
|
CrewAI |
|
|
langwatch |
|
|
strands-agents/sdk-python |
|
|
Arize Phoenix |
|
|
nanobot |
|
|
dreadnode/rigging |
|
|
CoPaw |
|
|
Aider |
Confirmed safe (pins litellm==1.82.3) |
The .pth mechanism fires when any Python process starts, including pip itself. In CI/CD environments, this means the payload can execute during build steps, not just at application runtime.
Detection: Are you affected?
Step 1: Check your installed version
pip show litellm | grep Version
If the output shows 1.82.7 or 1.82.8, treat the system as compromised and follow the remediation below. Do not just upgrade; the payload may have already run.
Step 2: Check for persistence artifacts
# Check for the sysmon backdoor
ls -la ~/.config/sysmon/sysmon.py 2>/dev/null && echo "BACKDOOR FOUND"
ls -la /root/.config/sysmon/sysmon.py 2>/dev/null && echo "ROOT BACKDOOR FOUND"
# Check for the systemd persistence service
systemctl --user status sysmon.service 2>/dev/null
ls -la ~/.config/systemd/user/sysmon.service 2>/dev/null && echo "PERSISTENCE SERVICE FOUND"
# Check for exfiltration archive remnants
ls /tmp/tpcp.tar.gz /tmp/session.key /tmp/payload.enc /tmp/session.key.enc 2>/dev/null && echo "EXFIL ARTIFACTS FOUND"
Step 3: Check for malicious .pth files
# Find .pth files in site-packages with suspicious patterns
find $(python3 -c "import site; print(' '.join(site.getsitepackages()))") \
-name "*.pth" -exec grep -l "base64\|subprocess\|exec" {} \;
Step 4: Verify file hashes
# Check proxy_server.py (1.82.7)
find / -path "*/litellm/proxy/proxy_server.py" 2>/dev/null -exec shasum -a 256 {} \;
# Malicious: a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b
# Check litellm_init.pth (1.82.8)
find / -name "litellm_init.pth" 2>/dev/null -exec shasum -a 256 {} \;
# Malicious: 71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238
Step 5: Check for network indicators
grep "litellm.cloud\|checkmarx.zone" /etc/hosts
grep "models.litellm.cloud\|checkmarx.zone" /var/log/syslog 2>/dev/null
Step 6: Check Kubernetes
kubectl get pods -A | grep "node-setup-"
Step 7: Scan with Snyk
snyk test --package-manager=pip
The Snyk in-app banner alert, with affected repositories surfaced in the asset inventory. The Trust Center link navigates to real-time incident status.
Snyk customers can also check the litellm package advisor and the full vulnerability record at SNYK-PYTHON-LITELLM-15762713.
Remediation
If you have NOT installed 1.82.7 or 1.82.8:
Pin to <=1.82.6 until a clean release is available:
pip install "litellm<=1.82.6"
# requirements.txt:
litellm<=1.82.6
If you HAVE installed 1.82.7 or 1.82.8:
The payload executes when Python starts, including during pip install itself. Treat the system as potentially compromised regardless of whether you ran any application code.
Remove persistence artifacts:
rm -f ~/.config/sysmon/sysmon.py
rm -f ~/.config/systemd/user/sysmon.service
systemctl --user disable sysmon.service 2>/dev/null
systemctl --user daemon-reload
rm -f /tmp/tpcp.tar.gz /tmp/session.key /tmp/payload.enc /tmp/session.key.enc /tmp/.pg_state /tmp/pglog
Rotate credentials on the affected system:
SSH private keys: generate new keys, revoke old keys from authorized_keys, GitHub, GitLab
Cloud credentials: AWS access keys, GCP service account keys, Azure service principals
API keys: .env files, shell environment variables, CI/CD secrets
Docker registry credentials: ~/.docker/config.json
Kubernetes: ~/.kube/config, in-cluster service account tokens
Database passwords from any config files on the system
Git credentials from ~/.gitconfig or the system credential store
Cryptocurrency wallet seed phrases
Audit AWS Secrets Manager and SSM Parameter Store, as the payload queries these directly if instance metadata is accessible.
Audit Kubernetes cluster secrets. If a service account token was present, all secrets across all namespaces may have been read. Check for node-setup-* pods in kube-system.
Install a clean version on a fresh environment rather than upgrading in-place:
pip install "litellm<=1.82.6"
Why didn't pip hash verification catch this
Hash verification confirms a file matches what PyPI advertised, but does not indicate whether the advertised content is malicious.
The litellm_init.pth file in 1.82.8 is correctly declared in the wheel's RECORD file with a matching hash. pip install --require-hashes would have passed. The package passes all standard integrity checks because the malicious content was published using legitimate credentials; there is no hash mismatch, no suspicious domains, and no misspelled package name.
The only install-time detection path is inspecting whether a package installs .pth files and whether those files contain patterns like subprocess, base64, or exec. No widely deployed pip plugin currently does this automatically.
The broader pattern - The target selection across this campaign focuses on tools with elevated access to automated pipelines: a container scanner (Trivy), an infrastructure scanning tool (KICS), and an AI model routing library (LiteLLM). Each of these tools requires broad read access to the systems it operates on (credentials, configs, environment variables) by design.
LiteLLM is increasingly being deployed as a centralized LLM gateway that stores API credentials for multiple model providers. In that configuration, the credential set accessible from a single compromised host is broader than for a typical application.
The initial disclosure spread through AI developer communities (r/LocalLLaMA, r/Python, Hacker News) rather than through traditional security channels such as r/netsec or CVE feeds.
For further reading on supply chain risks specific to LLM tooling, Snyk Learn covers the topic in Supply Chain Vulnerabilities in LLMs. The 2024 Ultralytics AI Pwn Request supply chain attack is also a useful comparison case: another widely used AI Python library compromised via a CI/CD exploit, with a similar attack chain.
Indicators of compromise:
File hashes:
|
File |
SHA-256 |
|
litellm_init.pth (1.82.8) |
71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238 |
|
proxy_server.py (1.82.7) |
a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b |
|
sysmon.py |
6cf223aea68b0e8031ff68251e30b6017a0513fe152e235c26f248ba1e15c92a |
Network:
Exfiltration: https://models.litellm.cloud/ (POST)
C2 polling: https://checkmarx.zone/raw (GET)
Filesystem:
~/.config/sysmon/sysmon.py or /root/.config/sysmon/sysmon.py
~/.config/systemd/user/sysmon.service (description: "System Telemetry Service")
/tmp/tpcp.tar.gz, /tmp/session.key, /tmp/payload.enc, /tmp/session.key.enc
/tmp/.pg_state, /tmp/pglog
Kubernetes:
Pods: node-setup-{node_name} in kube-system
This article is shared at no charge for educational and informational purposes only.
Red Sky Alliance is a Cyber Threat Analysis and Intelligence Service organization. We provide indicators of compromise information (CTI) via a notification service (RedXray) or an analysis service (CTAC). For questions, comments, or assistance, please contact the office directly at 1-844-492-7225 or feedback@redskyalliance.com
- Reporting: https://www.redskyalliance.org/
- Website: https://www.redskyalliance.com/
- LinkedIn: https://www.linkedin.com/company/64265941
Weekly Cyber Intelligence Briefings:
REDSHORTS - Weekly Cyber Intelligence Briefings
https://register.gotowebinar.com/register/5207428251321676122
[1] https://snyk.io/articles/poisoned-security-scanner-backdooring-litellm/
Comments