LiteLLM Compromised Twice: Why a Valid Signature Did Not Help, and How to Protect Your AI Gateway

On October 21, a malicious LiteLLM 1.107.0 reached PyPI, Docker Hub and GHCR. Nobody uploaded it with stolen registry credentials. LiteLLM's own release pipeline built it from a lookalike fork, Berri-AI/litellm, signed it and published it. The wheels were live on PyPI for about 1.7 hours, and the Docker Hub images for about 2.6.

It was the second time in seven months. In March, versions 1.82.7 and 1.82.8 shipped a credential stealer to PyPI. LiteLLM is the open-source gateway many teams put in front of every model provider they use, so it holds the keys for every provider behind it. That makes this more than another package incident.

I read LiteLLM's incident posts, its March townhall and the independent analyses from Socket and Datadog. Facts below come from LiteLLM unless I say otherwise, and I mark my own arithmetic. I have not analysed the malware myself.

Two LiteLLM compromises, one target

March 24October 21
Versions1.82.7 and 1.82.81.107.0
ChannelsPyPI onlyPyPI, Docker Hub and GHCR
How it ranIn 1.82.8, a .pth file on any Python startcook.pth and sitecustomize.py on any Python start
Way inA compromised Trivy scanner in CI, with static release credentials in reachStolen GitHub tokens running the real release pipeline
Live forAbout 40 minutes1.7 hours on PyPI, 2.1 on GHCR, 2.6 on Docker Hub
Release signingNot in place until v1.83.0Signed by LiteLLM's own pipeline

Sources: the March GitHub issue, LiteLLM's townhall and its October incident post. The October window lengths are my arithmetic from LiteLLM's timestamps.

Why the AI gateway is the target

A gateway routes requests to model providers, so it needs each provider's key, and it ends up holding all of them, plus its own master and virtual keys, a database password and usually a cloud role. LiteLLM's advice to affected users reads like that inventory: rotate the instance or task IAM role, every provider key stored in the proxy database, the master and virtual keys, the database password, and any secret in the environment of any process on the host.

The payload was written for that inventory. Datadog found that the October stealer collects cloud credentials, including from instance metadata, along with SSH keys, Git and PyPI tokens, LLM provider keys and sessions for AI coding tools. If a database is configured, it queries the tables where the proxy stores upstream provider credentials. LiteLLM says the attacker's activity in its own systems focused on upstream inference API tokens. Datadog and Socket both put LiteLLM at about 23 million weekly downloads.

I have written before that a secret that never enters the model's context cannot be repeated by the model; it can still be misused through a tool that holds it. A gateway is the tool that holds all of them. The same logic made OpenClaw's gateway token worth stealing earlier this year.

How the October release was built

LiteLLM's root-cause analysis, published on October 23, sets out the chain.

  • May 27. A configuration file containing the master key of the LiteLLM Agent Platform, a tool LiteLLM uses to orchestrate agents, was committed to a public repository. The commit was force-pushed over. The key was never revoked.
  • October 10. The attacker used that key on the platform's API. One response returned every managed agent's configuration, including a GitHub CLI token belonging to one of LiteLLM's two organisation owners. It was a personal token rather than a service account, and it was not scoped down.
  • The same evening. Throwaway branches ran workflows that posted repository secrets to an attacker-controlled endpoint, and the runs were deleted. Two of those secrets were the GitHub CLI tokens of two more maintainers.
  • October 21. Branch protection rejected the attacker's change to the release workflow, and the owner's token overrode it. The workflow built from the fork, a second maintainer's token approved the production gate, and a third deleted the run records.

That gate normally needed a special account locked to a device in LiteLLM's office. Earlier that week it had been relaxed so that any two engineers could ship daily backports, and LiteLLM notes that the owner's token could have removed the restriction anyway.

One correction matters. LiteLLM's first post suspected the tokens came from the logs of an internal LiteLLM deployment. The root-cause analysis traced the first token to the Agent Platform instead, and LiteLLM marked its first post as inaccurate or incomplete in places. A first incident report is a draft.

Why the signature did not help

After March, LiteLLM did what the usual guidance says. Its CI/CD v2, announced on March 30, isolated security scans and tests, moved releases into a separate repository, published to PyPI through Trusted Publishing so that no long-lived token was involved, made Docker release tags immutable, and signed images with cosign. The March townhall named the cases signing was meant to cover: stolen registry credentials, tampered artifacts and mutated tags.

October avoided all three. The attacker needed no registry credential, because the real pipeline published. Nothing was tampered with after the build, because the build itself took the wrong source. A cosign check confirms that an image came from LiteLLM's pipeline, and this one did. A signature tells you who built an artifact. It does not tell you what it was built from.

That second question had an answer on day one. Socket examined the provenance attached to the images on October 21 and found the release workflow's input source_owner set to Berri-AI, with the source recorded as github.com/Berri-AI/litellm. The provenance for 1.106.2 records BerriAI/litellm. Datadog described the provenance as having faithfully recorded the malicious fork. The evidence was one hyphen long, and no check looked at it.

Floating tags did the rest. Socket reported that latest, main-latest and main-stable on GHCR resolved to the same image index as v1.107.0, so a deployment that pulled latest received the stealer without asking for it. The researcher who reported the release advised pinning 1.106.2 by digest.

Other release failures this year point the same way. In March, a Claude Code release shipped its source map to npm through a genuine pipeline. In May, an attacker published 84 malicious versions of 42 TanStack packages through a pull_request_target workflow, a poisoned Actions cache and an OIDC token read from runner memory, and TanStack says no npm token was stolen. A short-lived token shrinks what can be stolen. It does not stop a hijacked pipeline from using it.

What I would take from my own release work

On Auto-RAG and MerlinX, the platforms I work on at DXWand, releases ship in gated waves. UAT runs the identical image artifacts, verified by digest rather than rebuilt look-alikes, and nothing promotes until post-deploy assertions pass and a soak holds against the baseline error rate. I also brought 35 Azure DevOps projects and about 224 repositories under one standard: an estate-wide branch-policy audit, then mandatory review gates on tier-one repositories, protected tags, a hardened deploy-render gate, break-glass access, and a scheduled drift audit.

LiteLLM had several of the same controls, including review gates, protected tags and a gated release environment, and its branch protection did reject the first push. What failed were the exceptions: an owner token that could override protection, and a release gate relaxed for a week of backports. Break-glass access and drift audits exist for those two cases. The digest rule carries over to an image you did not build: a digest you pinned before October 21 never pointed at 1.107.0.

An AI gateway supply-chain checklist

ControlWould it have kept 1.107.0 out?
Pin images by digest and packages by hashYes, for any pin set before October 21
Adopt no release younger than a dayYes, every channel was clean within 2.6 hours
Check the provenance's source repositoryYes for the images, whose provenance named Berri-AI/litellm
Verify the cosign signatureNo, LiteLLM's pipeline signed it
Egress allowlist for the gatewayNot the install, but likely the theft
Short-lived, scoped credentialsNot the install, but less to steal

Source: my assessment, from the LiteLLM, Socket and Datadog accounts above.

If you run a gateway:

  • Pin and wait. Deploy images by sha256 digest, never latest, install Python packages with hash checking, and give new releases a cooling-off period. LiteLLM's own March plan added a cooldown before upgrading its dependencies.
  • Allowlist egress. A gateway needs your providers, its database and your telemetry. The stealers posted to lookalike domains, models.litellm[.]cloud in March and www.litellm[.]dev in October. Admitting all of GitHub would still have let the October backdoor reach its dead drop on raw.githubusercontent.com.
  • Shrink the credentials. Keep provider keys in a secret manager, give the gateway's cloud role and database user only what routing needs, block instance metadata if it is unused, and issue virtual keys per team with budgets.
  • Never log tokens. If your gateway stores request or response bodies, scrub authorisation headers and token patterns first.

If you publish one:

  • Lock the source. A release workflow should build one repository, with no free-form source input. Datadog's advice is the same.
  • Make overrides loud. Approval for the release environment should need a device-bound credential, and any branch-protection override should page someone.
  • Keep personal tokens out of CI. Use short-lived, scoped tokens per job, as I argued after PocketOS.
  • Revoke on the day. A force-push hides a leaked key. Only rotation makes it worthless.

If you ran LiteLLM 1.107.0

LiteLLM's guidance, in brief: do not start with pip, because any Python start reruns the payload, pip included. Delete cook.pth, cook.py, sitecustomize.py and their cached .pyc files from every affected site-packages, kill the daemon holding /tmp/remote_code_loader.lock, rebuild pinned to 1.106.2, and rotate everything the host could reach. Treat any container that ran image index sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e as compromised. Finding the files confirms exposure; not finding them proves nothing.

In March, a LiteLLM release could not say where it came from. In October, it said so, down to the hyphen, and nobody asked.


Sources

Written by Eslam Hamed, a Technical Lead and AI Solutions Architect working on enterprise RAG, agentic AI, and release engineering for AI platforms.

Putting agents near real systems and data? I help teams design the controls before something goes wrong. See how we can work together.

Back to all posts

More posts