psf-litellm-oct2026-check(8)
LiteLLM 1.107.0 supply-chain compromise: detect, hunt and contain (v1)
No CVE (malicious package release, PyPI and Docker)
If this software is helpful, consider a donation to our 501c3 nonprofit:
https://privacysafe.foundation
On 21 October 2026 a malicious release of litellm, version 1.107.0, was
published to PyPI (all seven wheels, plus an sdist with a tampered
pyproject.toml) and to the project's Docker images (tags v1.107.0, latest,
main-latest, main-stable), after attacker-controlled GitHub accounts
triggered a release. Each wheel drops three files into the ROOT of
site-packages, beside litellm/ rather than inside it:
cook.pth one line, "import cook"
cook.py a zlib+base64 blob; a credential stealer ("privcollect")
sitecustomize.py an hourly backdoor that fetches and runs attacker code
Python runs .pth files and sitecustomize.py at interpreter startup, so the
malware runs on every Python start on the host, even if litellm is never
imported. The stealer targets cloud tokens, developer and private keys, AI
tooling credentials, browser data, environment variables, and a LiteLLM
proxy's saved credentials, and exfiltrates to litellm[.]dev; the backdoor
reads a domain from a GitHub dead drop (letters9813) and runs code from it.
The last clean release before it is 1.106.2.
The remedy is to rebuild, not to clean in place: if 1.107.0 was installed from
a wheel, or a LiteLLM container pulled on 21 October 2026 ran, treat the host
as FULLY COMPROMISED. The stealer already ran. Rebuild the host, and from a
clean machine revoke and rotate every credential it could reach (cloud keys,
API tokens, SSH and signing keys, browser sessions, the LiteLLM proxy's
keys), then reinstall litellm pinned to 1.106.2 by digest. This script finds
the compromise, hunts the indicators, and can contain the auto-executing
backdoor (quarantine) so it stops running while you rebuild; containment is
not remediation.
Usage:
./litellm-oct2026-check.sh [command]
check (default) find litellm in every Python environment (and running
containers) and flag 1.107.0, and report the dropped
malware files in site-packages. Read-only. Exit 0 = nothing
found, 1 = compromise indicators present, 2 = couldn't read.
hunt read-only, deeper: the malware files across site-packages,
the /tmp markers it leaves, and logs for its network
indicators (litellm.dev, the GitHub dead drop). Exit 0 =
clean, 1 = findings.
quarantine CONTAINMENT: back up (for forensics) and remove
cook.pth and cook.py, and sitecustomize.py when it carries
the malware markers, so they stop running at Python start.
Needs write access. The host is still compromised: rebuild
and rotate credentials.
version print the script version.
Env overrides:
SCAN_PATHS="/usr /opt /srv /home /root /var/www"
where to look for site-packages
SCAN_DEPTH=9 how deep the search goes
SAFE_PIN=1.106.2 the last clean release to pin to
BACKUP_ROOT=/var/backups/litellm-oct2026
HUNT_LOGS="/srv/app/log/*.log" extra log globs for hunt's network check
SCAN_CONTAINERS=1 1 = also look inside running containers;
0 = skip them
PSF_LOCK_WAIT seconds a run that changes the host waits for another one that is already
changing it (default 30), then stops with exit 2; the lock is STATE_DIR/.lockThe Security Patches software and this documentation are dedicated to the public domain under CC0 1.0 Universal. Other pages on privacysafe.app/docs are CC BY-SA 4.0 unless the page says otherwise.