On 21 October 2026 a malicious build of LiteLLM, the widely deployed AI gateway, went up on PyPI and Docker. The payload is interesting, but the entry is the part worth your afternoon: according to the maintainers, the attacker got in by reading credentials out of an AI gateway's own logs. A proxy that sits between your agents and every model provider does not just route traffic. It accumulates secrets, in its database, in its environment, and in whatever it writes to disk.
TL;DR
- LiteLLM 1.107.0 was published at 11:39 UTC on 21 October with a credential stealer in all seven wheels and in the v1.107.0 container images. PyPI quarantined it at 13:20 UTC.
- Wiz reports roughly 49,000 downloads of the malicious version, citing ClickPy data.
- The payload installs a .pth file, so it runs at every Python interpreter start in that environment, whether or not anything imports LiteLLM.
- Among its targets is the proxy's own PostgreSQL table LiteLLM_CredentialsTable, which holds upstream provider keys.
- Wiz observed stolen AWS instance role credentials from CI runners and Kubernetes nodes being validated about 4.5 hours later, then used to list Bedrock models.
#What shipped on 21 October
The malicious artefacts were LiteLLM 1.107.0 on PyPI, in all seven prebuilt wheels, plus the v1.107.0 container images and, on Docker Hub, the latest, main-latest and main-stable tags. Chainguard names 1.106.2, 1.107.0rc1 and 1.107.0rc2 as clean, and flagged the release 16 minutes after it appeared.
Chainguard makes a point that matters for anyone auditing this: the v1.107.0 Git tag points at an ordinary signed commit with no payload. One added line in the source distribution's build settings makes the wheel copy a folder into site-packages. The repository looked fine. The artefact did not.
Three files land in the site-packages root: cook.pth, cook.py and sitecustomize.py. Because Python processes .pth files at interpreter startup, the stealer runs in any Python process in that environment. A container that merely has the package installed is in scope.
#The access path, step by step
# per Wiz, Chainguard and the BerriAI advisory gateway log # GitHub CLI OAuth tokens readable in an exposed deployment → oauth token # three engineer accounts, one triggered the release, one approved → release build # one line in the sdist build config bundles the payload → python startup# a .pth file runs the stealer in every interpreter → provider keys # cloud roles, SSH keys, and the proxy credentials table
Wiz describes a stealer with sixteen modules: browser credentials, AWS files and instance metadata, GCP service account keys, Azure CLI tokens, provider API keys, SSH and GPG private keys, shell history. It then queries the LiteLLM proxy's PostgreSQL credentials table for upstream provider keys, encrypts the lot and posts it to a lookalike domain.
The follow-on activity is the ordinary economics of stolen machine credentials. Wiz observed AWS instance role credentials, taken from compromised CI runners and Kubernetes nodes, being validated from two addresses about 4.5 hours after exfiltration. Each credential got an identity check, then a Bedrock model listing. Wiz describes this as consistent with LLMjacking. In a few environments the attacker also enumerated IAM roles, users and access keys.
#An AI gateway is a credential store
Most teams install a gateway for sensible reasons: one endpoint for many models, central rate limits, cost attribution, a single place to rotate provider keys. That last benefit is the problem restated. Centralising provider keys means building a credential store, and a credential store that is treated as middleware rather than as a vault.
The ranking is clear from what the stealer went for. The provider keys in the proxy database are the prize: organisation-wide, long-lived and expensive. The instance role on the node is next, because gateways sit in clusters with cloud permissions attached. Neither is a LiteLLM flaw. It is what a gateway holds by design.
So the useful question is not whether your gateway is patched. It is what one machine identity on that host reaches: which providers, which accounts, which budgets, and which cloud role comes attached to the pod.
#How a token ends up in a log
The entry deserves care, because the maintainers present it as a suspicion rather than a confirmed finding. BerriAI says the malicious actions went through three engineers' GitHub accounts, apparently via stolen GitHub CLI OAuth tokens, that one account triggered the release run and another approved it, and that all tokens involved have been revoked. It also says an innocuously named user account was found in an internal LiteLLM deployment exposed to the internet, and that the attacker likely pulled tokens from its logs.
The mechanism named for the tokens is the part worth internalising. Per LiteLLM, a coding agent can embed a user's GitHub CLI OAuth token in a response body, and if response bodies are logged, the token lands in the gateway's logs. Nobody leaked a credential. A tool put a token where tokens travel, and a log did its job.
That is a new shape of exposure, and it generalises past this one product. Agent traffic carries credentials in both directions: tool call arguments, authorisation headers, error payloads that quote a failed request back. Any component that records full request or response bodies on that path is now a secrets store, whether or not anyone designed it to be one. Logs are rarely scoped like vaults, rarely rotated, and often shipped onward to a third system.
| Where the credential sat | Why it was reachable |
|---|---|
| Gateway logs | Response bodies were logged, and an agent put a token in one |
| Release pipeline | Engineer OAuth tokens could trigger and approve a publish |
| Every install | A startup hook runs before any application code decides anything |
| Proxy database | Upstream provider keys are centralised there by design |
| Node instance role | The gateway runs in a cluster with cloud permissions attached |
#What to check this week
- Find 1.107.0 anywhere it landed. Pinned requirements, lockfiles, base images, CI caches. Check container image digests rather than tags, since the Docker Hub latest and main-stable tags carried the build too.
- Look for the startup hook, not just the package. A cook.pth, cook.py or unexpected sitecustomize.py in a site-packages root is the signal. Rebuild affected hosts rather than uninstalling in place.
- Rotate what the host could reach, which is more than the package. Provider keys in the proxy database, cloud instance role sessions, SSH and GPG keys, and any CI secret that was present in that environment.
- Check your cloud logs for the validation pattern. An identity check immediately followed by a model listing call, from an address you do not recognise, is the signature Wiz describes. Unexpected model usage is the cheapest detection you have.
- Audit what your gateway writes down. Decide whether full request and response bodies are logged, who can read those logs, where they are shipped, and how long they live. If the answer is unknown, that is this week's finding.
- Scope the gateway's own identity. List what a single credential taken from that host reaches across providers, accounts and budgets. The blast radius of a gateway is the union of everything it fronts.
On Elmoz
#FAQ
Which LiteLLM versions are affected?
The malicious release is 1.107.0 on PyPI, in all seven prebuilt wheels, together with the v1.107.0 container images and the Docker Hub tags latest, main-latest and main-stable. Chainguard names 1.106.2, 1.107.0rc1 and 1.107.0rc2 as clean.
Does the stealer run if my code never imports LiteLLM?
Yes. Chainguard notes that the payload installs a .pth file in the site-packages root, which Python executes at interpreter startup. Any Python process in that environment runs it, whether or not it imports litellm.
What did the stealer take?
Per Wiz, browser credentials and cookies, AWS files and environment variables plus EC2 and ECS instance metadata, GCP service account keys, Azure CLI tokens, provider API keys for OpenAI, Anthropic, Cohere and others, SSH and GPG private keys, shell history, and the proxy’s own PostgreSQL credentials table holding upstream provider keys.
How did the attacker reach the release pipeline?
BerriAI says the malicious actions went through three engineers’ GitHub accounts, apparently via stolen GitHub CLI OAuth tokens, and that all tokens involved have been revoked. It suspects the tokens were read from the logs of an internal LiteLLM deployment that was exposed to the internet. BerriAI has not confirmed how that deployment was accessed.
A gateway is a machine identity holding every other machine identity you gave it. Elmoz maps that reach, from the agent and the proxy in front of it to the provider accounts and cloud roles behind, so the blast radius of one host is a number rather than a guess.