LiteLLM’s maintainers believe an attacker obtained GitHub CLI OAuth tokens from the project’s own gateway logs, then used them to publish a credential stealer to PyPI, Docker Hub and GHCR. The malicious build was live for less than three hours. Attackers tested stolen AWS credentials about four and a half hours after exfiltration. For security teams, the lesson of the LiteLLM supply chain attack goes well beyond one Python package. Every step of this attack ran on a non-human identity, and none of the controls in place asked who was holding it.
The release
On October 21, 2026, LiteLLM 1.107.0 was published to PyPI, Docker Hub and GHCR through the project’s own release pipeline. PyPI served it from 11:39 to 13:23 UTC. The affected Docker Hub tags ran from 11:46 to 14:20 UTC, and the GHCR tags were unavailable by 13:51 UTC. Treating this as a LiteLLM 1.107.0 vulnerability misreads it. The release itself was the malicious package, built, signed and published by the project’s own pipeline.
A cook.pth file made Python import cook.py at interpreter start, running a stealer that collected system, browser, developer, cloud and AI tool secrets and posted them to a lookalike domain. Wiz reports it also attempts to query the LiteLLM proxy’s credentials table for stored provider keys, which Socket notes shows targeting, not confirmed access. Separately, sitecustomize.py on Linux and macOS polled a GitHub-hosted file every hour for a command-and-control domain. That file held only test values before GitHub removed it.
Wiz traced the payload to ten commits in a lookalike repository, Berri-AI/litellm. The release workflow pointed at it, so it signed and published the poisoned artefacts. About four and a half hours after exfiltration, Wiz saw AWS instance role credentials from CI runners and Kubernetes nodes validated with sts:GetCallerIdentity, then checked against Bedrock model listing, a pattern consistent with LLM jacking.
Confirmed versus suspected
LiteLLM’s security incident report begins its account of the pipeline compromise on October 19, when it temporarily expanded release permissions for two engineers. The attacker held GitHub CLI credentials for three developers, one of whom could run workflows, and used an organisation owner’s token to disable branch protections and force-push. LiteLLM’s timeline shows one account modified the release workflow, another approved the production run and a third deleted workflow records.
How the tokens were obtained is LiteLLM’s hypothesis, not a finding. It says Claude Code can, in specific situations, embed a user’s GitHub CLI OAuth token in a response body, and that where response bodies are logged, the token can land in LiteLLM logs. LiteLLM has not specified the conditions under which Claude Code does this. It found an innocuously named user account on an internal LiteLLM deployment exposed to the internet, and believes the attacker read the tokens from that deployment’s logs. It has not established how the deployment was exposed or accessed, and suspects a recently patched vulnerability.
The .pth technique resembles the March 2026 compromise, which LiteLLM said may have been linked to the Trivy compromise. Socket told The Hacker News it has no evidence connecting the two incidents.
Why the hardening did not hold
After March, LiteLLM moved releases to a private repository, made Docker tags immutable and signed its images. Each control verifies an artefact or a process. None verifies that the identity invoking the process should be doing so. The signing step signed the output of a workflow pointed at a lookalike repository, so the signature attested to the pipeline, not to the source. Branch protection was disabled by a token with the authority to disable it.
The failure is architectural. A bearer token is valid for whatever its holder can do, and nothing in the path asks who is holding it. If LiteLLM’s theory holds, a second failure sits behind the first: tokens issued to developers pass through a gateway into logs, and a log store has a different access model from the credentials it ends up holding. The LiteLLM credential stealer’s targeting of the project’s own credentials table shows the same pattern from the other side. A gateway concentrates credentials, so it concentrates what an attacker wants.
What conventional controls miss
Repository secret scanning covers commits, not response bodies in a log index. Signing proves the pipeline ran, not that it should have. Pinning protects consumers who pin and does nothing for a CI job that resolved latest inside the window. A valid OAuth token presents as the developer it was issued to, so authentication alone shows no change of holder. A CI runner role calling Bedrock model listing is anomalous only if someone knows what that role is meant to do.
What to do this week
- Run pip show litellm across machines, CI images and containers. Treat any host that ran 1.107.0 as compromised, rotate every reachable credential and rebuild.
- Pin exact versions and image digests, never latest or main-stable.
- Search gateway, proxy and observability logs for GitHub OAuth tokens (the gho_ prefix; ghp_ and github_pat_ for personal access tokens) and cloud keys, and revoke what you find.
- Expire every temporary release permission, and alert when branch protection is disabled or a release workflow runs against an unexpected repository.
- Scope CI runner and Kubernetes node roles so they cannot call Bedrock model listing or enumerate IAM unless the job requires it.
Where identity governance fits
Every credential in this attack was a non-human identity: CLI OAuth tokens, CI runner roles, a release workflow that could be signed. Each one was trusted for what it could do. None was checked for who was holding it.
Unosecur’s Unified Identity Fabric discovers identities across 100+ integrations, including service accounts, shared credentials and other NHIs, and maps the access paths and blast radius of each identity. That is the exposure map teams were reconstructing after the fact. For agent tool traffic, the Uno AI Gateway sits between MCP clients such as Cursor, Claude Code and ChatGPT and the MCP servers behind them. It provides per-connector access, just-in-time requests that close on their own, guardrails on tool calls and an audit trail you can export to a SIEM. Real credentials stay encrypted and never reach the agent or the model. The gateway covers the MCP tool-call path, not LLM response logs. The principle is the same on both paths: credentials should be scoped, time-bound and kept out of places that were never built to hold them.
A bearer token cannot tell you who is holding it. The layer it passes through has to.
See how far your non-human identities reach

.avif)













.webp)

.avif)
.webp)
