CriticalSupply chainPatch available

LiteLLM 1.107.0: poisoned PyPI and Docker release built from a lookalike repository

Attackers with stolen gh CLI tokens shipped malware as LiteLLM 1.107.0 on PyPI, Docker Hub and GHCR. Check before running Python, then rebuild and rotate.

Published
Updated
Fixed in
Pin litellm 1.106.2, or a later release signed with the new cosign key (1.106.3 onward)
Exploitation
Yes. Malicious artifacts were live for about two hours on PyPI and about two and a half on Docker Hub on 21 October 2026

What happened

On 21 October 2026, LiteLLM’s own release pipeline built, signed and published a malicious litellm 1.107.0 (LiteLLM). The GitHub source tag for 1.107.0 is clean. The artifacts were built from Berri-AI/litellm, a lookalike of the real BerriAI/litellm repository. This is LiteLLM’s second compromise of 2026, after the TeamPCP campaign in March.

Affected artifacts and windows, in UTC (LiteLLM):

ArtifactWindow (UTC)
PyPI litellm==1.107.0 (all seven wheels)11:39 to 13:23
Docker Hub tags v1.107.0, latest, main-latest, main-stable11:46 to 14:20
GHCR ghcr.io/berriai/litellm, litellm-database, litellm-non_root (same tags)11:46 to about 13:51

The bad Docker Hub tags pointed at sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e. LiteLLM says the release “contained compiled malware which steals credentials and loads remote-code execution tools”. Enterprise customers pulling from LiteLLM’s private registry were not affected (LiteLLM).

How the attacker got in

LiteLLM’s account changed as its investigation went on, so the order matters.

First account, 21 October. LiteLLM said it had “observed specific situations in which Claude Code will embed a user’s GitHub CLI OAuth token into a response body”. Where response bodies are logged, those tokens can end up in LiteLLM’s logs. It suspected the attacker took developers’ tokens from the logs of an internet-exposed internal LiteLLM deployment (LiteLLM). That post now carries a note that some of its assumptions “were later discovered to be inaccurate or incomplete”.

Root-cause analysis, 23 October. The request logs showed “the first token came from a different service” (LiteLLM attack chain):

  1. On 27 May 2026, an administrative key for an internal, experimental LiteLLM Agent Platform was committed to a public repository. The file was deleted about three hours later, but the commit stayed in public history and the key was never revoked.
  2. On 10 October, the attacker used that key against the platform’s API. One response returned the configuration of every managed agent, including the GitHub CLI OAuth token of one of LiteLLM’s two GitHub organisation owners.
  3. The same night, workflows pushed to throwaway branches sent repository secrets to webhook.site, including two more maintainers’ GitHub CLI tokens.
  4. On 21 October, the owner’s token overrode branch protection and pointed the release workflow at the lookalike fork. A second stolen token approved the production gate. A third deleted the workflow run records.

LiteLLM’s follow-up does not tell engineers to stop using Claude Code or similar agents. It says “Everything on a developer’s machine should be assumed readable by the agents running on it”, bars agent users from owner or admin roles, and limits GitHub CLI OAuth tokens to their intended scope (LiteLLM changes).

Am I affected?

You are affected if an unpinned pip install resolved 1.107.0 between 11:39 and 13:23 UTC, or you pulled or started one of the tags above between 11:46 and 14:20 UTC (LiteLLM). Look for files before you start Python, because cook.pth runs at every interpreter start:

bash
# Payload and package metadata, found without starting Python
find / -path '*site-packages*' \( -name 'cook.pth' -o -name 'cook.py' \
  -o -name 'litellm-1.107.0.dist-info' -o -name 'sitecustomize.py' \) 2>/dev/null

# The lock held by the detached loader process
ls -la /tmp/remote_code_loader.lock 2>/dev/null && lsof /tmp/remote_code_loader.lock

# Pinned versions in requirements and lockfiles
grep -rnE '^litellm(\[[^]]*\])?==1\.107\.0' --include='requirements*.txt' .
grep -nE -A1 'name = "litellm"' poetry.lock uv.lock 2>/dev/null | grep '"1.107.0"'

# Docker images on this host, with the digest you actually pulled
docker image ls --digests | grep -i litellm

A sitecustomize.py inside site-packages next to cook.pth is the payload. Some Linux distributions ship a legitimate sitecustomize.py elsewhere, so judge by location. Any LiteLLM image with the digest above, or any GHCR or Docker Hub image pulled by one of those tags during the window, counts as compromised.

If 1.107.0 ran on a host with outbound internet access, LiteLLM says to assume the data left. On AWS, look for GetCallerIdentity from the instance role around install time, and for that role’s credentials used from outside your infrastructure:

bash
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=GetCallerIdentity \
  --start-time 2026-10-21T11:30:00Z --end-time 2026-10-22T00:00:00Z \
  --query 'Events[].[EventTime,Username]' --output text

What to do now

  1. Remove the payload before anything else. Delete cook.pth, cook.py, sitecustomize.py and any __pycache__/cook*.pyc and sitecustomize*.pyc from every affected site-packages. Then kill the detached Python process holding /tmp/remote_code_loader.lock (LiteLLM).
  2. Rebuild, do not repair. Recreate virtual environments and images from scratch, pinned to litellm==1.106.2 or a later explicit version. LiteLLM rotated its cosign signing key on 23 October; 1.103.8, 1.104.6, 1.105.4 and 1.106.3, published on 24 October, were the first releases signed with the new key (LiteLLM).
  3. Verify digests, not only signatures. The malicious 1.107.0 images carry a valid signature from the old cosign key. Pin images by @sha256: digest and check the new cosign.pub fingerprint LiteLLM published.
  4. Rotate everything the host could reach. The instance or task IAM role, every provider API key stored in the proxy database, LiteLLM master and virtual keys, the DATABASE_URL password, and any secret in the process environment (LiteLLM). A LiteLLM proxy usually holds keys for several model providers, so expect a long list.
  5. Treat agent output and logs as secret-bearing. If you log LLM request or response bodies, including through a gateway like LiteLLM, assume tokens and keys will appear there. Redact, restrict who can read those logs, and keep internal AI deployments off the public internet.
  6. Keep agents away from owner credentials. Do not run coding agents in a shell logged in to gh as an organisation owner or admin. Use a separate, least-privileged account for day-to-day work, and fine-grained, expiring tokens for automation.

Why this is part of the Vulnpocalypse

This attack did not need a vulnerability. It needed one committed key that nobody revoked, a service that handed out a GitHub owner’s token, and a release pipeline that trusted whoever held that token. The AI tooling made it worse: LiteLLM sits in front of every model a team uses, so a poisoned release reaches all of their provider keys at once. And developers now run agents with the same credentials they use to own the organisation. LiteLLM’s own conclusion is the one to copy: anything on a developer’s machine is readable by the agents running on it.

Sources