Skip to main content

Best-effort scanner to detect IOCs from the compromised LiteLLM PyPI packages v1.82.7/v1.82.8 (TeamPCP, March 2026)

Project description

LiteLLM Supply Chain Attack Scanner

A best-effort scanner that attempts to detect indicators of compromise (IOCs) left by the malicious LiteLLM PyPI packages v1.82.7 and v1.82.8, published on March 24, 2026. The compromise was identified by security researchers at Endor Labs, Datadog Security Labs, Snyk, and Sonatype, with the initial public disclosure filed as GitHub Issue #24512.

No guarantees. This tool attempts to find known artifacts associated with the compromise. It does not guarantee detection of all malicious activity, nor does it guarantee that a clean scan means your system was not affected. A determined attacker may have removed traces, and the tool cannot detect secrets that have already been exfiltrated. Use this scanner as one input in your incident response — not as a definitive verdict.

Quick Start

Option A: Install from PyPI

Linux / macOS:

# Recommended — installs in an isolated environment
pipx install scan-litellm-compromise
scan-litellm

If you don't have pipx: sudo apt install pipx (Debian/Ubuntu) or brew install pipx (macOS).

Alternatively, using pip inside a virtual environment:

python3 -m venv /tmp/scanner && /tmp/scanner/bin/pip install scan-litellm-compromise
/tmp/scanner/bin/scan-litellm

Note: pip install directly will fail on modern Linux distributions (Debian 12+, Ubuntu 23.04+, Fedora 38+) due to PEP 668. Use pipx or a virtual environment instead.

Windows:

pip install scan-litellm-compromise
scan-litellm

Or using a virtual environment:

py -m venv %TEMP%\scanner && %TEMP%\scanner\Scripts\pip install scan-litellm-compromise
%TEMP%\scanner\Scripts\scan-litellm

Option B: Run from source (no install needed)

Linux / macOS:

git clone https://github.com/CognitiveSand/scan_litellm_compromise.git
cd scan_litellm_compromise
python3 run_scan.py

Windows — double-click run_scan.bat, or from a terminal:

git clone https://github.com/CognitiveSand/scan_litellm_compromise.git
cd scan_litellm_compromise
py run_scan.py

No dependencies required — uses only the Python standard library.

Command-Line Options

Flag Description
--scan-path DIR Restrict Phase 1 (discovery) and Phase 4 (source scan) to a specific directory instead of scanning the entire system. IOC artifact checks (Phase 3) still run system-wide.
--resolve-c2 Enable live DNS queries to C2 domains in addition to using hardcoded known IPs. See warning below.
--help Show usage information.

Warning about --resolve-c2: This flag causes the scanner to make live DNS queries to models.litellm.cloud and checkmarx.zone — both attacker-controlled domains. This carries real risks:

  • Operational security: Your DNS query is visible to the attacker's infrastructure, revealing that you are actively investigating the compromise. This could prompt them to rotate infrastructure, wipe logs, or accelerate exploitation of already-stolen credentials.
  • Network monitoring: The queries may trigger alerts in your own SIEM, IDS, or SOC dashboards, creating noise during an active incident response.
  • DNS logging: Corporate DNS resolvers and upstream providers log queries. The domains may already be on threat intel blocklists, which could flag your machine as compromised.

In most cases you do not need this flag. The scanner ships hardcoded known IPs (46.151.182.203, 83.142.209.11) confirmed by multiple threat intelligence sources. Live DNS is only useful if you suspect the attacker has rotated to new infrastructure since this tool was last updated.

Examples:

# Scan only a specific project directory
python3 -m scan_litellm_compromise --scan-path /home/user/myproject

# Enable live DNS resolution for C2 connection checks
python3 -m scan_litellm_compromise --resolve-c2

# Combine both
python3 -m scan_litellm_compromise --scan-path ./myproject --resolve-c2

Video Overview

This scanner was inspired by Fahd Mirza's video highlighting the incident — thanks to him for bringing it to attention.

What Happened

On March 24, 2026, two backdoored versions of the litellm Python package were published to PyPI:

  • v1.82.7 (uploaded ~10:39 UTC) — malicious code injected into litellm/proxy/proxy_server.py (base64-encoded)
  • v1.82.8 (uploaded ~10:52 UTC) — added a litellm_init.pth file that executes on every Python interpreter startup, regardless of whether litellm is imported

The packages were available for approximately 3 hours before PyPI quarantined them. LiteLLM has roughly 95 million monthly downloads.

How the Attack Worked

The attacker compromised BerriAI's PYPI_PUBLISH token by poisoning the aquasecurity/trivy-action GitHub Action used in LiteLLM's CI/CD pipeline. Malicious commits were force-pushed onto 75 of 76 existing version tags in the Trivy action repository. When LiteLLM's CI ran, it fetched the attacker-controlled action, which exfiltrated the PyPI publishing token. This was identified and documented by researchers at Endor Labs, Snyk, and StepSecurity.

Three-Stage Payload

Stage 1 — Credential Harvester: Sweeps the local filesystem and live process environments for secrets including:

  • SSH private keys (~/.ssh/)
  • AWS, GCP, and Azure credentials
  • Kubernetes kubeconfig and service account tokens
  • Database credentials (PostgreSQL, MySQL, Redis)
  • CI/CD tokens (PyPI, npm, Docker registry, GitHub PATs)
  • All environment variables (capturing API keys for OpenAI, Anthropic, etc.)
  • .env files (recursively discovered)
  • Shell histories, git credentials, TLS private keys
  • Cryptocurrency wallets

Stage 2 — Encrypted Exfiltration: Harvested data is encrypted with AES-256-CBC (random session key) and the session key is encrypted with a hardcoded RSA-4096 public key. The bundle is archived as /tmp/tpcp.tar.gz and sent via HTTPS POST to models.litellm.cloud.

Stage 3 — Persistence Backdoor:

  • Drops ~/.config/sysmon/sysmon.py
  • Creates ~/.config/systemd/user/sysmon.service ("System Telemetry Service")
  • Every 50 minutes, polls checkmarx.zone/raw for a secondary payload, downloads it to /tmp/pglog, and executes it
  • Tracks state in /tmp/.pg_state

Kubernetes Lateral Movement

If a Kubernetes service account token is found, the payload deploys privileged pods named node-setup-{node_name} into the kube-system namespace on every node. These pods mount the host root filesystem and install the sysmon backdoor at the node level.

What This Scanner Attempts to Find

This scanner makes a best-effort attempt to detect known IOCs. It automatically detects the platform (Linux, macOS, or Windows) and adjusts scan paths accordingly.

Phase What It Looks For
1 litellm metadata directories (dist-info / egg-info) across filesystem (Linux: /home, /opt, /usr, /srv, /var; macOS: /Users, /opt/homebrew, /Library; Windows: %USERPROFILE%, %APPDATA%, Program Files). Can be restricted with --scan-path.
2 litellm version from metadata files — no Python interpreter execution needed
3 IOC artifacts: litellm_init.pth, sysmon persistence, temp staging files, C2 network connections (matched against hardcoded known IPs by default; --resolve-c2 enables live DNS), suspicious Kubernetes pods. On Windows: also checks Registry Run keys and Scheduled Tasks
4 Source files and dependency configs (pyproject.toml, requirements.txt, etc.) that reference litellm, flagging any pinned to compromised versions

Limitations

  • This scanner cannot detect exfiltrated secrets. If your credentials were sent to the attacker's C2 server, no local scan can undo that. Credential rotation is required regardless of scan results.
  • Artifacts may have been cleaned up. The absence of IOC files does not prove the system was never compromised.
  • The scanner does not inspect Docker image layers, CI/CD runner caches, or remote systems. Each environment must be checked independently.
  • Root-owned paths (/root on Linux) are excluded. If you need to scan those, run a separate check with appropriate privileges.
  • The scanner cannot decrypt exfiltrated data. Only the attacker holds the RSA-4096 private key.
  • Windows Registry scanning is best-effort. The scanner checks common Run key locations but cannot cover all possible persistence mechanisms (WMI subscriptions, COM hijacking, etc.).

Usage

Linux / macOS:

python3 run_scan.py
# or
python3 -m scan_litellm_compromise
# or, if installed via pip:
scan-litellm

Windows — double-click run_scan.bat, or from a terminal:

py run_scan.py

The scanner auto-detects the platform (Linux, macOS, or Windows) and adjusts scan paths, network commands, and persistence checks accordingly.

Exit code is 1 if compromise indicators are found, 0 otherwise.

See Command-Line Options for --scan-path and --resolve-c2.

Platform Support

Feature Linux macOS Windows 10/11
litellm detection /home, /opt, /usr, /srv, /var /Users, /opt/homebrew, /usr/local, /Library %USERPROFILE%, %APPDATA%, Program Files
Conda/pipx detection /opt/conda, ~/.local/share/pipx Homebrew Caskroom, ~/.local/share/pipx %LOCALAPPDATA%\Miniconda3, %LOCALAPPDATA%\pipx
Persistence check systemd user services sysmon files (inert without systemd) Registry Run keys, Scheduled Tasks, Startup folder
Temp artifacts /tmp/ /tmp/ %TEMP%
Network connections ss -tnp lsof -i -P -n netstat -ano
C2 IP matching Hardcoded known IPs (opt-in live DNS via --resolve-c2) Same Same
ANSI terminal colors Native Native Auto-enabled via Virtual Terminal Processing

macOS Note

macOS support was added based on threat intelligence analysis of the TeamPCP malware's behavior on Darwin systems. It has not been tested on actual macOS hardware as the maintainer does not currently have access to a Mac. The malware's credential theft and .pth backdoor are confirmed to work on macOS, but the systemd persistence mechanism is inert (macOS uses launchd, not systemd). If you run this scanner on macOS and encounter issues, please open an issue.

If Compromise Is Detected

Assume ALL secrets on the affected machine are compromised. The following steps are recommended — this list is not exhaustive:

  1. Rotate credentials immediately — SSH keys, cloud provider credentials (AWS/GCP/Azure), API keys, database passwords, CI/CD tokens, .env file contents
  2. Remove malicious artifacts — delete litellm_init.pth, sysmon persistence files, and temp staging files (see platform-specific paths above)
  3. Downgrade litellmpip install litellm==1.82.6 (last known clean version) or upgrade past the compromised range once verified safe
  4. Update pinned versions — check all requirements.txt, pyproject.toml, and lock files for references to 1.82.7 or 1.82.8
  5. Inspect Kubernetes clusters — look for node-setup-* pods in kube-system, audit ClusterRoleBindings
  6. Block C2 domainsmodels.litellm.cloud and checkmarx.zone at DNS/firewall level
  7. Audit cloud provider logs — check AWS CloudTrail, GCP Audit Logs, Azure Activity Logs for unauthorized API calls using potentially stolen credentials

Advisories and References

ID Source
PYSEC-2026-2 Python Packaging Authority (PyPA)
SNYK-PYTHON-LITELLM-15762713 Snyk
GitHub Issue #24512 Initial disclosure
GitHub Issue #24518 Official timeline

Security Research and Technical Analyses

Known IOC Hashes (SHA-256)

File SHA-256
litellm_init.pth (v1.82.8) 71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238
proxy_server.py (v1.82.7) a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b
sysmon.py (dropped backdoor) 6cf223aea68b0e8031ff68251e30b6017a0513fe152e235c26f248ba1e15c92a

Project Structure

scan_litellm_compromise/
  __main__.py            Entry point for python -m
  scanner.py             Orchestrator (argparse CLI)
  config.py              Cross-platform constants, patterns, and known C2 IPs
  models.py              Typed data structures
  formatting.py          Terminal output (ANSI with Windows support)
  platform_policy.py     Platform abstraction (Strategy pattern)
  platform_linux.py      Linux-specific paths and commands
  platform_darwin.py     macOS-specific paths and commands
  platform_windows.py    Windows-specific paths and commands
  ioc_windows.py         Windows-only IOC checks (Registry, Tasks)
  discovery.py           Phase 1 — find litellm metadata directories
  version_checker.py     Phase 2 — read litellm version from metadata
  ioc_scanner.py         Phase 3 — IOC artifact detection
  source_scanner.py      Phase 4 — source/config file scanning
  report.py              Phase 5 — summary and remediation
tests/                   pytest test suite (299 tests)
run_scan.py              Direct entry point
run_scan.bat             Double-click launcher for Windows

Disclaimer

This tool is provided as-is, with no warranty of any kind. It attempts to find known indicators of the LiteLLM v1.82.7/v1.82.8 compromise but cannot guarantee complete detection. A clean scan does not mean your system was unaffected. Always perform credential rotation if there is any possibility that a compromised version was installed in your environment, even briefly.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

scan_litellm_compromise-0.3.1.tar.gz (38.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

scan_litellm_compromise-0.3.1-py3-none-any.whl (28.6 kB view details)

Uploaded Python 3

File details

Details for the file scan_litellm_compromise-0.3.1.tar.gz.

File metadata

  • Download URL: scan_litellm_compromise-0.3.1.tar.gz
  • Upload date:
  • Size: 38.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for scan_litellm_compromise-0.3.1.tar.gz
Algorithm Hash digest
SHA256 b0edc470541cf2f607df75ef9158621f15ee753748de1763a7b65d10ca309515
MD5 176f7242816d5ddbe287706039f4216c
BLAKE2b-256 c4b98f575e89cf8aae70fe29856265d7956346bd8a772305e3cafa5b4de1d9d0

See more details on using hashes here.

Provenance

The following attestation bundles were made for scan_litellm_compromise-0.3.1.tar.gz:

Publisher: pypi_publish.yml on CognitiveSand/scan_litellm_compromise

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scan_litellm_compromise-0.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for scan_litellm_compromise-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 49e21338ea903993e38df97539605e801a0ac0a6c04c43c1f49218a13a1f6f5f
MD5 64ca5c9db077515c80e098e9778aa36d
BLAKE2b-256 207eb9a8a6d153a7db7bbe89070fde75e5e5fcb681b653d5fc7161b297d17836

See more details on using hashes here.

Provenance

The following attestation bundles were made for scan_litellm_compromise-0.3.1-py3-none-any.whl:

Publisher: pypi_publish.yml on CognitiveSand/scan_litellm_compromise

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page