CriticalSupply chainPatch available

Malicious litellm 1.107.0 shipped a credential stealer and RAT through PyPI and Docker

Malicious litellm 1.107.0 on PyPI and Docker ran a credential stealer and RAT at Python startup. Last safe pre-incident release is 1.106.2.

Published
Updated
Fixed in
Clean releases v1.106.3 and v1.106.4 resumed 24 October 2026 (StepSecurity); 1.106.2 was the last safe release before the incident (Datadog, Wiz). BerriAI will skip 1.107.0 permanently.
Exploitation
Yes. The payload ran at every Python interpreter start and exfiltrated credentials; Wiz observed the attacker checking stolen AWS instance-role credentials with sts:GetCallerIdentity and bedrock:ListFoundationModels, consistent with LLMjacking.

What happened

  • 21 October 2026, 11:39 UTC: malicious litellm==1.107.0 was published to PyPI; PyPI quarantined it around 13:20 to 13:23 UTC, and affected Docker images were removed or repointed by about 13:53 to 14:20 UTC. The package was available for roughly two hours (Datadog; StepSecurity). Wiz counted about 49,000 downloads in that window (Wiz).
  • The compromised release carried both an infostealer and a remote access trojan (RAT). Datadog described it as adding files that run “even if LiteLLM is never imported” (Datadog; Aikido).
  • Root cause (per BerriAI’s postmortem, reported by sources). A configuration file containing a master key for an internal LiteLLM tool was committed to a public GitHub repository on 27 May 2026; the commit was removed about three hours later but the key was never revoked and stayed valid for over four months (The Register; GitGuardian).
  • 10 October 2026: the attacker used that key to obtain the GitHub CLI OAuth token of a LiteLLM GitHub organization owner, a token not scoped to specific repositories, then stole tokens from two more maintainers (The Register; GitGuardian).
  • 21 October 2026: the attacker built the release from a lookalike repository, Berri-AI/litellm (one hyphen off BerriAI/litellm), and used a second maintainer’s token to satisfy the production approval step. The application source was byte-identical to the legitimate code; the payload was injected via the build, using a pyproject.toml maturin directive data = "cook" that bundled a cook/ directory into the wheels (Datadog; Aikido; Microsoft).
  • GitGuardian notes this follows an earlier, separate LiteLLM compromise; Hudson Rock’s analysis of that earlier exposure describes a 153GB archive of 433,909 files attributed to 2,488 organizations, figures that relate to the earlier incident, not to 1.107.0 (GitGuardian).

Affected versions

  • PyPI: litellm==1.107.0, all seven platform wheels (macOS x86_64/arm64, manylinux aarch64/x86_64, musllinux aarch64/x86_64, win_amd64) carry the payload. The source distribution has a tampered pyproject.toml but no payload files (Datadog; StepSecurity).
  • Docker / GHCR: v1.107.0 on ghcr.io/berriai/litellm, ghcr.io/berriai/litellm-database, ghcr.io/berriai/litellm-non_root and docker.io/litellm/litellm. The floating tags latest, main-latest and main-stable pointed to the malicious image on 21 October from about 11:46 to 14:20 UTC (Datadog; Aikido).
  • Not affected (per StepSecurity): 1.107.0rc1, 1.107.0rc2, and the 1.106.x, 1.105.x, 1.104.x and 1.103.x lines; the 1.107.0.dev1, dev3 pre-releases were not inspected (StepSecurity).
  • Safe / fixed: Datadog and Wiz name 1.106.2 as the last safe release (Datadog; Wiz). StepSecurity says clean releases resumed on 24 October as v1.106.3 and v1.106.4, recommends pinning 1.106.4 or the backports 1.105.5, 1.104.7 or 1.103.9, and notes 1.107.0 will be skipped permanently (StepSecurity).

How the malware works

  • Startup hooks. The wheel drops cook.pth, cook.py and sitecustomize.py into site-packages, outside the litellm/ package. cook.pth contains import cook, which Python runs at every interpreter start; cook.py deobfuscates and runs the stealer in a daemon thread; sitecustomize.py is imported automatically by Python and carries both a collector and the remote loader. The payload fires on any Python or pip launch, LiteLLM import or not (Datadog; Microsoft). Microsoft calls the combined .pth plus sitecustomize.py approach “the first documented case in the wild” (Microsoft).
  • Stealer (“privcollect”). Collects browser passwords and cookies (Chrome, Firefox, Edge, Safari); AWS, Azure and GCP credentials including instance metadata (IMDS); AI provider keys (OpenAI, Anthropic, Cohere and others) and AI coding tool configs (Claude, Codex, Gemini, Cursor, Copilot and others); SSH, GPG and TLS keys; Git, npm and PyPI tokens; .env files and shell history; Docker, Kubernetes, Vault and CI/CD secrets; and, when DATABASE_URL is set, upstream provider keys from the LiteLLM proxy’s LiteLLM_CredentialsTable and LiteLLM_ProxyModelTable. On Linux it also reads other processes’ environments via procfs (Datadog; Wiz; Endor Labs).
  • Exfiltration. Harvested data is encrypted with AES-256-CBC, the AES key wrapped with a 4,096-bit RSA key (OAEP), then sent by HTTP POST to www.litellm[.]dev/healthcheck with a hardcoded X-Token header (Datadog; Microsoft).
  • RAT / remote code loader. A daemon (guarded by /tmp/remote_code_loader.lock) reads a hostname hourly from a GitHub dead-drop file, then downloads and executes Python from /codepy on that host with no signature check. The dead drop held only test/empty values when observed and the GitHub repo was removed, so no second stage was confirmed; the loader does not run on Windows, but the stealer does (Datadog; Aikido; StepSecurity).
  • Post-exploitation. Wiz observed the attacker, about 4.5 hours after exfiltration (16:20 to 17:01 UTC), checking stolen AWS instance-role credentials from CI runners and Kubernetes nodes with sts:GetCallerIdentity, then bedrock:ListFoundationModels, consistent with LLMjacking; some environments also saw IAM enumeration (Wiz).

Indicators of compromise

Files / host. cook.pth, cook.py, sitecustomize.py in site-packages; /tmp/.pc_once_72129118c59f5636c0bb (stealer upload marker); /tmp/remote_code_loader.lock (loader running); .pc_e_* / .pc_sq_* temp staging; environment variable PRIVCOLLECT_ACTIVE=1; build config data = "cook" under [tool.maturin] (Datadog; Endor Labs; StepSecurity).

Network. www.litellm[.]dev/healthcheck (exfiltration); header X-Token: XWiA8gZNa7NcX2XRoAEb; raw.githubusercontent[.]com/letters9813/pythonclient-test/main/domain.txt (C2 dead drop, since removed); any /codepy request (Datadog; Microsoft).

Attacker artifacts. Lookalike repo github[.]com/Berri-AI/litellm; GitHub account letters9813; commit-author email ckdi82fkoos@proton.me; malicious head commit 20535d8fa11f786a6c24df7488a0e449ed5f09ad; attacker IPs 168.100.8.57, 165.227.201.8 (Wiz), plus 69.48.229.143 and 199.91.221.39 (StepSecurity) (Wiz; StepSecurity).

Hashes (SHA-256), Linux/macOS variants. cook.pth 7f191632e7320705d154b85204f1ae546b543acbd7e8861229b76f5042b92bdb; cook.py 9dd0a3926f96a37eb86cf1ee2310d9be93139cc4ebef3ef9e2efe9531390be25; sitecustomize.py 34bef04fe88ac9d0aae0475630b1738681d15b94f233ac8aa46eacf5968305fa. Windows variants: cook.pth 84dc8c82c0a4a4a62552ef826b49fcc86d1a5c4a5d0f2c06665723f93d129d7d; cook.py 03ac2a8b752605d1d7710979c17ff18b03dd978203ba23ba97fd8ce3d6f897ea; sitecustomize.py b6122440d0d9cb1eb1ade9933f49214da8c31288de7710c076c2680dfa528465 (Microsoft; Endor Labs). Container image index digest: sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e (Datadog).

What to do

  1. Scope exposure. Search lockfiles, requirements and CI logs for 1.107.0; check environments for cook.pth and the dist-info; check local and Kubernetes images by digest, not mutable tags (Datadog).
  2. Do not run Python or pip in a suspect environment, each launch re-triggers the payload. Inspect with trusted tools from a clean system (Microsoft).
  3. Contain and rebuild. Kill any process holding /tmp/remote_code_loader.lock, remove cook.pth, cook.py, sitecustomize.py and their __pycache__, and rebuild from clean artifacts; removing the litellm package alone is not sufficient (StepSecurity; Microsoft).
  4. Rotate all reachable credentials from a clean machine, cloud keys, AI provider and AI-tool tokens, SSH/GPG/TLS keys, Git/npm/PyPI tokens, Kubernetes/Vault/registry secrets, and on proxy hosts the stored provider keys, master key, salt key and DATABASE_URL password. Revoke active sessions, since rotation may not invalidate every token (Datadog; StepSecurity).
  5. Hunt in logs. Look for POSTs to www.litellm.dev/healthcheck, requests to the GitHub dead-drop path or any /codepy, and in CloudTrail for GetCallerIdentity and Bedrock calls from instance roles or from the listed IPs (Microsoft; StepSecurity).
  6. Pin to a safe version, 1.106.2, or clean 1.106.3/1.106.4, pin container images by digest, and block 1.107.0 in your package proxy. From v1.106.3, images are signed with a new key (StepSecurity).

Sources