Security Advisory · Supply Chain

The LiteLLM PyPI attack: a supply-chain postmortem

How a hijacked maintainer account and a malicious .pth file turned the booming MCP plugin ecosystem into one of the most targeted credential-theft operations against AI developers yet.

Anuj Sadani | Tech / March 2026 / 11 min read
95MMonthly downloads
1.82.7–8Compromised versions
~13 minBetween bad releases
TeamPCPThreat actor

A maintainer account gets hijacked

On March 24, 2026 at 10:39 UTC, litellm==1.82.7 was uploaded to PyPI. Thirteen minutes later, 1.82.8 followed. Neither version had a corresponding Git tag or GitHub release. Both were uploaded directly by the threat actor after compromising the PyPI account of krrishdholakia, the litellm maintainer, bypassing the project's entire CI/CD pipeline.

The attack was only discovered by accident. A developer at FutureSearch installed an MCP plugin in Cursor IDE, and that plugin pulled in litellm as a transitive dependency. Within seconds the machine ran out of RAM and crashed, caused by an exponential fork bomb that was a bug in the malware itself. Without that bug, the attack would have been completely silent.

Mar 23, 12:58 UTC
TeamPCP compromises KICS GitHub Actions, a previous operation in this campaign.
Mar 24, 10:39 UTC
litellm 1.82.7 uploaded to PyPI, with the malicious payload in proxy/proxy_server.py.
Mar 24, 10:52 UTC
litellm 1.82.8 uploaded, escalating the attack via litellm_init.pth so no import is needed.
Mar 24, afternoon
FutureSearch discovers the attack via the fork-bomb crash in Cursor. Reported as GitHub issue #24512.
Mar 24, evening
PyPI quarantines the entire litellm package. MLflow emergency-pins to <=1.82.6.
Attacker calling card

A commit pushed to a forked repository by the attacker reads "teampcp owns BerriAI". The same group was behind the Trivy scanner attack on March 19 and the KICS compromise, and the exfiltrated archive filename, tpcp.tar.gz, is a direct reference to the group's name.

Anatomy of the payload

The attack behaves differently across the two versions, with 1.82.8 being the escalated, more aggressive iteration. Here is what 1.82.8 does from the moment litellm is installed.

The .pth trick: why no import is needed

Python's site module processes every .pth file in site-packages/ at interpreter startup. The malicious file litellm_init.pth (34,628 bytes, listed in the package's own RECORD) contains a one-line trigger.

litellm_init.pth · obfuscated trigger
import os, subprocess, sys; subprocess.Popen([
  sys.executable, "-c",
  "import base64; exec(base64.b64decode('...34KB of double-encoded payload...'))"
])

This spawns a child Python process carrying the full payload. Because .pth files trigger on every interpreter startup, the child re-triggers the same file, creating an exponential fork bomb. That fork bomb was not intentional. It is a bug, and without it this attack would still be undetected in countless environments.

Version 1.82.7 was slightly less aggressive. Its payload was embedded directly in proxy/proxy_server.py and needed an explicit import litellm to fire. Version 1.82.8 removed that requirement entirely.

What the 1.82.8 payload does on install
  • 1
    Credential harvesting. Sweeps the filesystem for SSH private keys, .env files, cloud credentials across AWS, GCP and Azure, Kubernetes configs, database passwords, shell history, Docker configs, npm tokens and crypto wallet files. It then runs printenv to capture every live environment variable, so any API key or secret in the process environment goes too.
    SSH keysCloud credsAPI keysK8s secretsCrypto wallets
  • 2
    Encrypted exfiltration. Collected data is encrypted with AES-256-CBC using a random 32-byte session key, which is itself encrypted with a hardcoded 4096-bit RSA public key. Both are bundled into tpcp.tar.gz and POSTed to https://models.litellm.cloud/, a lookalike domain registered specifically for this attack. The legitimate domain is litellm.ai.
    AES-256-CBCRSA-4096models.litellm.cloud
  • 3
    Lateral movement and persistence. If a Kubernetes service account token is present, the payload reads all secrets across all namespaces and tries to create a privileged alpine:latest pod on every node in kube-system. Each pod mounts the host filesystem and drops a backdoor at ~/.config/sysmon/sysmon.py with a systemd service that polls a C2 server for updated binaries.
    K8s priv-escsystemd persistenceC2 polling
Why LiteLLM specifically

LiteLLM is an LLM API gateway. Its entire job is to manage API keys for OpenAI, Anthropic, Google and 100-plus other providers, so any machine running it is by definition a treasure chest of high-value credentials. The attacker knew exactly what they would find.

What's safe, what's not

VersionStatusTriggerNotes
≤ 1.82.6 Clean Last known-good release. Pin here.
1.82.7 Compromised Requires import litellm Payload in proxy/proxy_server.py.
1.82.8 Critical Fires on Python startup, no import needed Malicious .pth file. The fork-bomb bug is what caused discovery.
Transitive dependency risk

You may have never run pip install litellm yourself. LiteLLM is a dependency of many AI agent frameworks, MCP servers and orchestration tools, so check all your Python environments, not just the primary one. CI/CD containers, project-local virtual envs, and tool-managed environments like Cursor and Claude Code are all in scope.

How to check if you're affected

Run these on any machine where you use Python-based AI tooling, especially Cursor, Claude Code, or anything with MCP plugins installed.

Step 1 · check the installed version
pip show litellm
# Version: 1.82.7 or 1.82.8 means you are affected
Step 2 · search for the malicious .pth file
find / -name "litellm_init.pth" 2>/dev/null
find ~/.cache/uv -name "litellm_init.pth" 2>/dev/null
find ~/.local -name "litellm_init.pth" 2>/dev/null
Step 3 · check for the persistence backdoor
ls ~/.config/sysmon/sysmon.py 2>/dev/null
ls ~/.config/systemd/user/sysmon.service 2>/dev/null
Step 4 · check Kubernetes (if applicable)
kubectl get pods -n kube-system | grep node-setup
kubectl get secrets --all-namespaces | grep -i suspicious
If you find any of the above

Treat the machine as fully compromised. The window between install and exfiltration is seconds, so assume everything reachable from that machine was stolen and rotate it immediately.

SSH keys, AWS/GCP/Azure credentials, every .env API key, Kubernetes configs, GitHub tokens, database passwords, Docker credentials, and shell history (which often contains secrets passed as arguments).

Step 5 · clean up
# Remove the package
pip uninstall litellm -y

# Purge caches so it can't reinstall from cache
rm -rf ~/.cache/uv
pip cache purge

# Remove persistence if found
rm -rf ~/.config/sysmon/
systemctl --user disable sysmon.service 2>/dev/null
rm ~/.config/systemd/user/sysmon.service 2>/dev/null

The MCP ecosystem is an emerging attack surface

This attack did not just exploit a Python package. It exploited a structural property of how modern AI tooling gets assembled, and it landed on machines running Cursor and Claude Code because of it.

"The person who discovered this only found it because their machine crashed. Without that bug, it would still be running silently on thousands of developer machines right now."

MCP plugins are a young, largely unvetted ecosystem. There is no curated review process, plugins can pull in arbitrary Python packages, and the machines running these tools (developer workstations and CI runners) are loaded with the exact credentials attackers want.

Claude Code users face compounded risk. It is an agentic tool with broad filesystem access and code execution by design, and it has documented support for the LiteLLM proxy as an MCP integration. Beyond the litellm vector, Check Point Research separately disclosed vulnerabilities in Claude Code where a malicious repository configuration file could trigger arbitrary shell commands and API-key exfiltration simply by opening a project. Those issues are now patched by Anthropic.

The pattern is the same across all of these attacks: the configuration and tooling layers have become part of the execution surface. A .pth file is not code you wrote. An MCP plugin dependency is not a package you explicitly chose. Both run with full privileges on your machine.

How to reduce your exposure

No single control fully prevents supply-chain attacks, but layered defenses shrink the blast radius considerably.

Pin

Pin and lock dependencies

Use lockfiles (uv.lock, poetry.lock, a pinned requirements.txt) and never upgrade blindly. litellm==1.82.6 would have been safe. Treat upgrades as decisions, not automatic steps.

Audit

Audit transitive deps

Use pip-audit, uv tree, or pipdeptree to know what you actually installed and why. If you did not explicitly install a package, you should know what pulled it in.

Detect

Flag PyPI/GitHub mismatches

The biggest tell here: 1.82.7 and 1.82.8 existed on PyPI with no GitHub tag. Tools like Socket.dev, Endor Labs, or Sigstore detect packages published outside the normal CI/CD pipeline.

Rotate

Use short-lived credentials

Prefer AWS OIDC tokens in CI over long-lived access keys, rotate API keys, and use a secrets manager instead of .env files on disk. Static secrets on developer machines are the jackpot.

Isolate

Isolate MCP plugins

Run MCP servers in Docker containers rather than your host Python environment. The Docker MCP Toolkit gives isolated execution compatible with Claude Desktop, Cursor and VS Code, keeping installed packages off your host filesystem.

Alert

Alert on .pth files

A .pth file in a third-party package has essentially no legitimate use. A CI or SAST rule that flags any .pth appearing in installed dependencies is low noise and high signal.

Immediate action checklist

The uncomfortable baseline

Assume that at some point a dependency in your stack will be compromised, and design your credential hygiene around that assumption: minimal secrets on any given machine, short-lived tokens, isolated execution, and regular rotation. The goal is not to make compromise impossible. It is to make it survivable.

This campaign isn't over

TeamPCP has now crossed five supply-chain ecosystems in under a month: GitHub Actions, Docker Hub, npm, OpenVSX and PyPI. Each compromised environment yields credentials that unlock the next target. The 13-minute gap between litellm 1.82.7 and 1.82.8 shows an attacker actively iterating during the attack window, escalating the trigger mechanism in real time.

The AI developer tooling ecosystem is a particularly attractive target. High-privilege machines, dense credential environments, and a young plugin ecosystem with minimal security review combine with the implicit trust developers place in the tools they use every day. That combination will keep getting exploited.

The best defense is to treat every layer of your tooling stack, not just your own code, as part of your attack surface.

References

FutureSearch · litellm-pypi-supply-chain-attack

GitHub issue #24512 · BerriAI/litellm

Endor Labs · teampcp-isnt-done

Check Point Research · critical Claude Code flaws