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.7andlitellm==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.pthin the package distribution. Python'ssite.pyevaluates.pthfiles at interpreter startup before any import. The payload executed on everypythoninvocation 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.pthvariant.
- Version 1.82.8: malicious code delivered as
The.pthvariant in 1.82.8 is the operationally significant one. A.pthfile insite-packagesis not an import — it is a startup hook. It runs before the Python application has loaded. It survives package reinstallation if the.pthfile itself is not removed. It runs in every subprocess that inherits the Python environment. The operator recognized this persistence advantage: by CanisterWorm's release,.pthinjection 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 inproxy/proxy_server.pylitellm==1.82.8— payload aslitellm_init.pthinsite-packages
Filesystem artifacts:
litellm_init.pthin any Pythonsite-packagesdirectory — SHA-256:ceNa7wMJnNHy1kRnNCcwJaFjWX3pORLfMh7xGL8TUjgtpcp.tar.gzin temp directories — the exfil archive; presence confirms active data collection
Network indicator (defanged):
https://models.litellm[.]cloud/— exfiltration endpoint (typosquattinglitellm.ai); POST withContent-Type: application/octet-stream
Campaign self-attribution markers (Layer 1):
tpcp.tar.gz— archive name embedding the campaign identifierTechnique: .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.pthimmediately. Runfind $(python3 -c "import site; print(' '.join(site.getsitepackages()))") -name "litellm_init.pth" 2>/dev/nullon 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
.pthfiles insite-packages. Any.pthfile containing executable code rather than plain path entries is malicious. This detection covers the entire TeamPCP.pthinjection 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[.]cloudegress. 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.