By falco365 · Published May 7, 2026

litellm on PyPI: the first .pth injection in the TeamPCP chain and why the operator named it

The litellm PyPI compromise on March 24, 2026 was the first documented use of .pth file injection in the TeamPCP campaign. The operator later named the technique after this compromise in CanisterWorm's own payload string — Layer 1 evidence that litellm is where this class of persistence began in the cluster.

litellm on PyPI: the first .pth injection in the TeamPCP chain and why the operator named it
Analysis

On March 24, 2026, a community member reported that litellm — a Python package used as a unified LLM proxy across over 100 provider APIs — had been compromised on PyPI. Malicious versions 1.82.7 and 1.82.8 were removed from the registry after the report. The compromise was the first documented use of .pth file injection in the TeamPCP campaign cluster.

The operator subsequently named the technique after this compromise. Six weeks later, CanisterWorm's payload contained the self-attribution string Technique: .pth file injection (TeamPCP/LiteLLM method). That string — embedded in the operator's own malware — is Layer 1 evidence: the operator considered the litellm compromise to be the technique's origin point in their toolchain. This finding documents that origin.

What we know
  • March 24, 2026 — Community report filed at BerriAI/litellm GitHub issue #24512. Malicious code confirmed in litellm==1.82.7 and litellm==1.82.8. Both versions subsequently removed from PyPI.
  • Vector — The compromise entered litellm's CI/CD pipeline via a Trivy GitHub Action that had been compromised by TeamPCP on March 20. The Action stole PyPI publishing credentials from litellm's pipeline. Those credentials were used to publish the malicious versions.
  • Two delivery variants across the two versions:
    • Version 1.82.8: malicious code delivered as litellm_init.pth in the package distribution. Python's site.py evaluates .pth files at interpreter startup before any import. The payload executed on every python invocation in environments where the package was installed — no explicit import required.
    • Version 1.82.7: malicious payload embedded directly in proxy/proxy_server.py. Requires the compromised module to be imported, a narrower trigger condition than the .pth variant.
The .pth variant in 1.82.8 is the operationally significant one. A .pth file in site-packages is not an import — it is a startup hook. It runs before the Python application has loaded. It survives package reinstallation if the .pth file itself is not removed. It runs in every subprocess that inherits the Python environment. The operator recognized this persistence advantage: by CanisterWorm's release, .pth injection had become the preferred PyPI persistence mechanism in the toolchain, named after this compromise.
Why litellm was targeted

litellm's attack surface profile explains why it appeared in the TeamPCP propagation chain. It is used primarily in AI engineering workflows — the same environments that hold the credentials TeamPCP systematically harvests:

  • Cloud provider credentials at scale. litellm is a proxy that routes requests to AWS Bedrock, GCP Vertex AI, Azure OpenAI, and 100+ other providers. Environments running litellm typically have cloud credentials with broad scope — the exact credential class TeamPCP's sweeper targets.
  • CI/CD pipeline deployment. Production litellm deployments frequently run in CI/CD environments for evaluation pipelines and automated testing. Those environments hold the combination of cloud credentials and package-publish tokens that enable worm propagation.
  • High download volume. A widely-installed package maximises the blast radius per compromised publish token. litellm's reach into the AI/ML development community made it a high-value propagation target.
Payload mechanics

The following analysis is from the community disclosure at BerriAI/litellm#24512, verified against the primary source.

The litellm_init.pth file (34,628 bytes, SHA-256: ceNa7wMJnNHy1kRnNCcwJaFjWX3pORLfMh7xGL8TUjg) contained a single Python statement that spawned a subprocess running a double-base64-decoded payload. The trigger reads:

import os, subprocess, sys; subprocess.Popen([sys.executable, "-c",
"import base64; exec(base64.b64decode('...'))"])

The payload collected a specific credential surface from the victim environment, including: all environment variables; SSH private keys; AWS credentials and IMDS token; Kubernetes config files and service account tokens; GCP application default credentials; Azure credentials; Docker authentication configs; npm tokens (.npmrc); Vault tokens; shell histories; cryptocurrency wallet directories; SSL/TLS private keys; CI/CD configuration files (.gitlab-ci.yml, Jenkinsfile, .travis.yml); and database credential files.

Collected data was written to a temporary file, encrypted with AES-256-CBC using PBKDF2 key derivation via openssl enc, and the AES session key was separately encrypted with a hardcoded 4096-bit RSA public key using OAEP padding. Both files were packed into an archive named tpcp.tar.gz — a direct self-attribution: the archive carries the campaign name. The archive was exfiltrated via HTTP POST to https://models.litellm[.]cloud/ with Content-Type: application/octet-stream.

The typosquat domain litellm[.]cloud (versus the legitimate litellm.ai) evades casual egress log review by appearing plausible, and the campaign name embedded in the archive name is the second self-attribution marker in the TeamPCP cluster — the first being Technique: .pth file injection (TeamPCP/LiteLLM method) in CanisterWorm's payload six weeks later.

The .pth technique origin: four-step analysis

The claim that litellm is the first use of .pth injection in the TeamPCP chain rests on four steps:

Observation: The CanisterWorm payload contains the string Technique: .pth file injection (TeamPCP/LiteLLM method). The March 24 litellm compromise is the earliest documented use of .pth injection in the TeamPCP cluster timeline.

Mechanism: Self-attribution strings in malware are deliberate operator choices, not build artifacts. An operator who names a technique after a specific compromise is recording its origin for internal reference. The string references "LiteLLM" specifically — not "PyPI injection" or a generic description — indicating the operator associates the technique with this particular victim, not the technique class broadly.

Inference: litellm was the originating application of .pth injection in the TeamPCP toolchain. The technique was subsequently standardized and reused: in elementary-data (April 30, elementary.pth), in CanisterWorm's PyPI propagation logic, and in TeamPCP's broader toolkit.

Confidence bound: This inference would be weakened by evidence of .pth injection in TeamPCP activity before March 24, 2026. No such evidence is known. The three-week gap between the litellm compromise (March 24) and the next confirmed .pth use in the cluster (Xinference, April 22) is consistent with a technique being refined between deployments.

Indicators of compromise

Compromised versions:

  • litellm==1.82.7 — payload in proxy/proxy_server.py
  • litellm==1.82.8 — payload as litellm_init.pth in site-packages

Filesystem artifacts:

  • litellm_init.pth in any Python site-packages directory — SHA-256: ceNa7wMJnNHy1kRnNCcwJaFjWX3pORLfMh7xGL8TUjg
  • tpcp.tar.gz in temp directories — the exfil archive; presence confirms active data collection

Network indicator (defanged):

  • https://models.litellm[.]cloud/ — exfiltration endpoint (typosquatting litellm.ai); POST with Content-Type: application/octet-stream

Campaign self-attribution markers (Layer 1):

  • tpcp.tar.gz — archive name embedding the campaign identifier
  • Technique: .pth file injection (TeamPCP/LiteLLM method) — in CanisterWorm payload six weeks later, naming this compromise as the technique origin
Detection and mitigation
  • Hunt for litellm_init.pth immediately. Run find $(python3 -c "import site; print(' '.join(site.getsitepackages()))") -name "litellm_init.pth" 2>/dev/null on all Python environments. If present, the 1.82.8 payload executed and is persisting across all Python invocations in that environment.
  • Audit for any unexpected .pth files in site-packages. Any .pth file containing executable code rather than plain path entries is malicious. This detection covers the entire TeamPCP .pth injection class, not just litellm.
  • Rotate all credentials reachable from affected environments. The collection scope was broad: cloud credentials, SSH keys, CI/CD secrets, database passwords, API keys. Treat any environment with 1.82.7 or 1.82.8 installed as fully compromised.
  • Check for worm propagation from affected environments. If the environment held PyPI or npm publish tokens, check both registries for unexpected patch releases on packages those tokens could access. The TeamPCP worm uses stolen publish credentials to extend its reach downstream.
  • Block models.litellm[.]cloud egress. This domain has no legitimate use. Any outbound connection to it is evidence of active exfiltration.
  • Pin your Trivy GitHub Actions by SHA. The confirmed entry vector for this compromise was a Trivy Action pinned by version tag rather than commit SHA. SHA pinning means the tag can be moved without affecting your workflow. This is the structural fix for the entire GitHub Actions tag-hijack vector.
Attribution and cluster position

The litellm compromise is the third confirmed event in the TeamPCP campaign timeline, following the Trivy Action compromise (March 20) and the Aqua Security GitHub org defacement (March 23). It is the first confirmed PyPI victim and the first confirmed use of .pth injection in the cluster.

The progression matters for defenders: TeamPCP began with a GitHub Actions compromise (npm-focused, affecting the Trivy ecosystem), then used stolen credentials to pivot into PyPI distribution (litellm), establishing a cross-ecosystem worm that expanded over the following six weeks to Checkmarx, Bitwarden, Xinference, and the Shai-Hulud npm cluster. The litellm compromise is where TeamPCP's PyPI branch began.

See the npm supply-chain worm cluster analysis for the full six-week timeline and cross-campaign technical analysis.

Criminal-market signal

Dark-web sweeps for TeamPCP across multiple runs have returned clean negatives. The litellm compromise is part of the same operator-run toolchain. No criminal-market presence for this technique or this campaign is expected or has been observed. The operator uses the credentials; they do not sell the attack. Detection must occur at the registry and CI/CD layer — specifically, hunting for unexpected .pth files in Python environments and monitoring for Trivy Action SHA drift.