Skip to content

Compromised LiteLLM 1.107.0: Cleanup Steps That Work

Compromised LiteLLM 1.107.0 on PyPI installs a credential stealer and backdoor. Detect infected envs, clean without re-triggering, pin 1.106.2, rotate keys.

7 min readBeginner

Compromised LiteLLM 1.107.0 on PyPI drops a credential stealer and backdoor that runs on every Python start – pip, IDE language servers, CI workers – even if your code never imports litellm. PyPI window: roughly 11:39-13:23 UTC on 2026-10-21 (Docker tags lingered longer). Community reports put malicious downloads in the tens of thousands; stolen AWS credentials saw follow-on abuse.

Shell-first cleanup, in order: detect without starting the infected interpreter, delete the hooks, kill the detached loader, pin a clean build, rotate what the stealer targets. Full maintainer write-up: LiteLLM incident post (Oct 21, 2026).

Quick check: are you in the blast radius?

Exposed if anything below landed inside the compromise windows:

  • Unpinned pip install litellm / pip install -U litellm that resolved to 1.107.0
  • Explicit litellm==1.107.0
  • Containers from v1.107.0, latest, main-latest, or main-stable while those tags pointed at digest sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e (maintainers’ figure; Docker Hub roughly 11:46-14:20 UTC)

Enterprise private-registry customers were not impacted. As of the incident guidance, safe pins are litellm==1.106.2 (also clean: 1.107.0rc1, 1.107.0rc2). Do not open Python or pip on a suspect host yet.

# Prefer find/ls from a clean shell - avoid python -c on the suspect env
find / -name 'litellm-1.107.0.dist-info' 2>/dev/null
find / -name 'cook.pth' -o -name 'cook.py' 2>/dev/null | head -50
ls -la /tmp/.pc_once_72129118c59f5636c0bb /tmp/remote_code_loader.lock 2>/dev/null

# Docker / k8s quick sweep
docker ps -a --filter ancestor=litellm/litellm:1.107.0
docker images --digests | grep -E '1bbcb1443bc9|litellm'
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"t"}{.spec.containers[*].image}{"n"}{end}' | grep -i litellm

cook.pth / cook.py / sitecustomize.py beside site-packages (or the 1.107.0 dist-info tree) means the bad wheel landed. The /tmp marker is a one-way signal only – present ⇒ stealer already uploaded once. Missing ⇒ proves nothing; containers and tmp cleaners wipe it constantly.

Clean compromised LiteLLM 1.107.0 without re-triggering

Order is the whole game. LiteLLM’s remediation (steps credited on BerriAI/litellm#47826) is blunt: any interpreter start reloads the hooks. That includes pip.

  1. Delete the three files from every site-packages with the shell only – not Python helpers on that env.
  2. Kill the detached loader holding /tmp/remote_code_loader.lock.
  3. Rebuild or reinstall pinned to 1.106.2 (fresh venv/image preferred).
  4. If the host had outbound internet, assume exfil – then rotate.
# 1) Locate site-packages roots without starting the infected interpreter if you can
# (known-good machine mounting the disk, or paths find already printed)
SITE_CANDIDATES=$(find /home /opt /usr /var /root -type d -name 'site-packages' 2>/dev/null)

for sp in $SITE_CANDIDATES; do
 rm -f "$sp/cook.pth" "$sp/cook.py" "$sp/sitecustomize.py"
 rm -f "$sp/__pycache__/cook"*.pyc "$sp/__pycache__/sitecustomize"*.pyc 2>/dev/null
 rm -rf "$sp/litellm-1.107.0.dist-info" 2>/dev/null
done

# 2) Stop the hourly backdoor daemon
if [ -f /tmp/remote_code_loader.lock ]; then
 fuser -k /tmp/remote_code_loader.lock 2>/dev/null || true
 rm -f /tmp/remote_code_loader.lock
fi
# orphan python with cwd / and PPID 1 is worth a look
ps auxww | grep -E '[p]ython'

# 3) Only now: fresh env + pin
python3 -m venv /tmp/clean-llm && source /tmp/clean-llm/bin/activate
pip install --upgrade pip
pip install 'litellm==1.106.2'
pip show litellm | grep -E 'Version|Location'

Docker: don’t “patch” a running bad container. Stop it. Rebuild from a pre-incident base – or pull a clean tag and pin by digest – then redeploy. Floating latest / main-stable were the trap during the window.

Pro tip: Every venv, CI runner cache, conda env, and notebook kernel is its own site-packages tree. One missed cook.pth and the next job re-exfils.

A disposable laptop venv feels almost boring once the three files are gone. A long-lived LiteLLM proxy VM with a fat credentials table is a different animal – same malware, totally different blast radius for key churn.

Rotation list matched to what actually left the box

Assume the haul already left if 1.107.0 ever ran with outbound access. Encrypted drops went to the lookalike www.litellm.dev/healthcheck path – breakdown in SafeDep’s analysis. Official rotation priority (incident report): cloud IAM and long-lived keys on the box, provider keys and master/virtual keys stored by the proxy, DATABASE_URL / proxy DB creds, env secrets, then SCM/registry tokens and SSH material that could push code.

  • Instance/task IAM roles and any long-lived cloud keys that lived on the host
  • Upstream provider keys the proxy stored, plus master/virtual keys
  • DATABASE_URL password and proxy DB credentials
  • GitHub/GitLab/registry tokens; SSH keys with push rights
  • Other env secrets and local tool auth that sat in the same environment

Then hunt reuse: CloudTrail GetCallerIdentity (and sibling calls) from the role around install time, plus later use of those credentials outside your normal ranges. Abuse of stolen AWS credentials was already observed after this incident.

Pitfalls that leave you half-clean

People burn hours here.

Mistake What actually happens
Run pip uninstall / pip install -U first Interpreter starts → hooks run again → another exfil chance
Only fix the project venv Global, user, conda, poetry, CI cache, IDE envs still hold cook.*
Downgrade package, skip daemon kill Hourly loader keeps polling; uninstall does not stop the forked process
Treat missing /tmp marker as clean False negative on containers and hosts that clear tmp
Pin only a floating Docker tag How latest burned people in-window; pin digests

Exit checks

No cook/sitecustomize under any site-packages. No lock file. No litellm 1.107.0 dist-info. pip show litellm reads 1.106.2 (or another pre-incident pin). Images rebuilt and pinned by digest. Written rotation log for each secret class above. Egress logs quiet on the lookalike healthcheck path.

When deleting the three files is not enough

File delete + pin is the floor for a disposable laptop that never held production roles. Jump to full rebuild (or a new host) when the box had cloud admin roles, ran a LiteLLM proxy with a populated credentials table, shared mounts with secrets managers, or held the lock file for hours.

How long was “hours” on your fleet before anyone noticed the lock? That’s the uncomfortable second-stage question – public dead-drops can go empty while a machine you still trust keeps looking reachable.

In those cases treat the machine as untrusted media: image it if you need evidence, then replace.

FAQ

Which LiteLLM versions are safe after the 1.107.0 compromise?

Pin litellm==1.106.2 (or 1.107.0rc1 / rc2) as of the official incident guidance. Private enterprise registry pulls were not impacted.

I never import litellm in my app – am I still affected?

Yes, if 1.107.0 sat in that environment’s site-packages. The wheels drop cook.pth and sitecustomize.py at the root; the interpreter runs them on startup for every process on that install. A CI job that only used the venv for tests still fired the stealer when the worker booted.

Is deleting cook.* enough, or do I have to rotate keys?

Deleting stops future runs. It does not claw data back. If 1.107.0 ran with outbound internet, official guidance is to assume exfil succeeded – marker presence confirms; marker absence does not clear you. Rotate IAM roles, provider keys the proxy stored, DB passwords, and env secrets; then watch cloud audit logs for outsider use. “We removed the files in five minutes” is not a substitute for rotation when production roles or a proxy DB were on the box.

Next: one production host or CI runner. Clean shell. Run the finds. If anything lights up – delete hooks → kill lock → rebuild on 1.106.2 → rotation list before the next deploy.