LiteLLM 1.107.0 on PyPI and Docker carried a credential stealer

An attacker drove LiteLLM's own release pipeline with stolen GitHub tokens; pin 1.106.2, check for three dropped files and rotate keys.

Share
LiteLLM logo on a white card above the words '1.107.0 COMPROMISED' and a red badge reading 'Pin 1.106.2 and rotate keys'
Logos: LiteLLM. Illustration: EngineerPal

An attacker used LiteLLM's own release pipeline to publish a credential-stealing version 1.107.0 of the popular AI gateway library to PyPI and its Docker images on Wednesday (Oct. 21), LiteLLM said.

The bad package was on PyPI from 7:39 to about 9:23 a.m. ET. The Docker Hub tags v1.107.0, latest, main-latest and main-stable pointed to the poisoned image from 7:46 to 10:20 a.m. ET, and the GitHub Container Registry copies until about 9:51 a.m., according to LiteLLM.

"If 1.107.0 was ever installed on a host with outbound internet, assume the exfil succeeded," LiteLLM said in its incident post, which lists its investigation as still in progress.

No one has published how many hosts installed 1.107.0 or pulled the bad images. LiteLLM also hasn't confirmed how the attacker got into an internal deployment, saying only that it "currently suspect[s]" a "recently patched vulnerability."

What the malware does

Each wheel drops cook.pth, cook.py and sitecustomize.py into site-packages. "Both run when any Python process starts on the host, even one that never imports litellm," security firm SafeDep said in a writeup discussed on Hacker News. That includes running pip.

The stealer takes SSH keys, cloud credentials, CI secrets, browser passwords, AI coding-tool tokens and the LLM provider keys stored in a LiteLLM proxy's database, according to Wiz and SafeDep. sitecustomize.py is an hourly backdoor that can download and run remote code, though Wiz and Datadog said no second-stage payload was observed.

Wiz said it "has observed abuse of stolen credentials, starting approximately 4.5 hours post-exfiltration," including AWS credentials probed for Bedrock access. That hasn't been independently confirmed.

The attacker didn't bypass LiteLLM's hardened pipeline but drove it, using three developers' stolen GitHub CLI tokens to override branch protection, approve the release and delete workflow records. It pointed the build at a lookalike repo, Berri-AI/litellm, LiteLLM said.

LiteLLM believes the tokens came from logs of an internet-exposed internal deployment. It said it has seen "specific situations in which Claude Code will embed a user's GitHub CLI OAuth token into a response body." Anthropic hasn't publicly commented, and no one has published a reproduction.

The release shipped "with seemingly legitimate build provenance that faithfully recorded the malicious fork as the source," Datadog Security Labs said.

Am I affected, and what should I do?

You're affected "if you used an unpinned version of LiteLLM between 11:39 and 13:23 UTC via pip or if you used the docker image v1.107.0, latest, main-latest, or main-stable between 11:46 and 14:20 UTC," LiteLLM said. That's 7:39 to 9:23 a.m. ET for pip and 7:46 to 10:20 a.m. ET for Docker.

LiteLLM says "all previous versions are safe" and recommends pinning litellm==1.106.2, which is now the latest version on PyPI. The Upwind researchers who reported the attack in issue #47826 said every 1.106.2 wheel checked clean.

LiteLLM suggests pip show litellm to check, but on a suspect host that command starts Python and reruns the stealer, so check the filesystem from the shell first:

find / \( -name 'litellm-1.107.0.dist-info' -o -name 'cook.pth' -o -name 'cook.py' -o -name 'sitecustomize.py' \) -path '*site-packages*' 2>/dev/null
ls -la /tmp/.pc_once_72129118c59f5636c0bb /tmp/remote_code_loader.lock 2>/dev/null
docker images --digests | grep -i litellm
  1. Check Docker digests. Treat any container that ran image index sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e as compromised, per LiteLLM. On GHCR, the litellm-database and litellm-non_root indexes begin sha256:c00f9d92c427 and sha256:7b00e91cd2fe.
  2. Check lockfiles and Oct. 21 CI logs for litellm 1.107.0. Any unpinned dependency that resolved during the window could have pulled it.
  3. Clean up from the shell, before running Python. Delete the three files and their .pyc files from every affected site-packages (a sitecustomize.py with no cook.pth beside it may be legitimate), and kill the detached Python process holding /tmp/remote_code_loader.lock.
  4. Rebuild venvs and images from scratch pinned to litellm==1.106.2, and pin Docker images by digest.
  5. Rotate from a clean machine: the instance or task IAM role, every provider key in the LiteLLM proxy database plus master and virtual keys, the DATABASE_URL password, and any secret in a process environment on the host.
  6. Check CloudTrail for GetCallerIdentity calls from the instance role around install time, then for use of its credentials from outside your infrastructure.

If the /tmp/.pc_once_… marker exists, the exfiltration is confirmed, LiteLLM said, but its absence "proves nothing" because cleanup can remove it.

This is LiteLLM's second PyPI compromise this year. Versions 1.82.7 and 1.82.8 carried a credential stealer on March 24, LiteLLM said, and no connection between the two attacks has been established, according to Datadog. LiteLLM has paused regular releases while it investigates, it said in issue #47826.