— notes

Anatomy of a Supply Chain Attack: LiteLLM on PyPI

2 min read#security
On this page
  1. What Is LiteLLM?
  2. The Attack Chain
  3. What the Malware Does
  4. Check Your Environment

On March 24, 2026, Callum McMahon at FutureSearch was testing a Cursor MCP plugin that pulled in litellm as a transitive dependency. Shortly after, his machine became unresponsive. He traced it to a newly installed litellm package.

What Is LiteLLM?

LiteLLM is a Python library that works like a universal remote for AI APIs. 95 million downloads per month on PyPI. ~40,000 GitHub stars.

The Attack Chain

sequenceDiagram
    participant A as Attacker (TeamPCP)
    participant T as Trivy GitHub Repo
    participant CI as LiteLLM CI (GitHub Actions)
    participant P as PyPI Registry
Note over A,T: March 1 - Initial Compromise
A->>T: Submit malicious PR (Pwn Request)
T-->>A: CI leaks personal access token
Note over CI,P: March 24 - Package Takeover
CI->>T: Pull trivy-action@latest (not pinned to SHA)
T-->>CI: Serve compromised action
CI-->>A: Leak PyPI publisher token
A->>P: Publish litellm v1.82.7 (inline payload)
A-&gt;&gt;P: Publish litellm v1.82.8 (.pth persistence)</code></pre></figure>
graph LR
    A["Trivy Repo\n(compromised)"] --> B["GitHub Bot Army"]
    B --> C["PyPI Account Takeover"]
    C --> D["LiteLLM v1.82.7 / v1.82.8"]
    D --> E[".pth Backdoor"]
    E --> F["Credential Harvest"]
    F --> G["Exfil Server (ICP Canister)"]

What the Malware Does

The payload was triple-nested: a base64 blob decodes to an orchestrator script, which decodes a second base64 blob containing the harvester.

graph TD
    ROOT[".pth Backdoor"]
    ROOT --> ENV["ENV Variables"]
    ROOT --> AWS["~/.aws/credentials"]
    ROOT --> DOTENV[".env Files"]
    ROOT --> PROC["/proc/self/environ"]
    ROOT --> EXFIL["Outbound HTTP Exfil to ICP canister"]
    ENV --> EXFIL
    AWS --> EXFIL
    DOTENV --> EXFIL
    PROC --> EXFIL

Two versions were published:

  • v1.82.7: injected payload at line 128 of litellm/proxy/proxy_server.py
  • v1.82.8: added a .pth file that executes every time Python starts

.pth files in site-packages execute code before your application begins, on every Python invocation: pytest, IDE language server, even pip install.

Check Your Environment

ls ~/.config/sysmon/sysmon.py 2>/dev/null && echo "BACKDOOR FOUND"
find $(python3 -c "import site; print(' '.join(site.getsitepackages()))") \
  -name "litellm_init.pth" 2>/dev/null

If anything shows up, upgrading the package is not enough. Rotate all LLM provider API keys, cloud credentials, GitHub and PyPI tokens, CI/CD secrets, and SSH keys. Rebuild from known-good images. The last known-clean version is 1.82.6.