The poisoned LiteLLM release did not sneak around the release pipeline. It went through it.
On October 21, version 1.107.0 was built and published through official workflows to PyPI and the project’s container registries. The public GitHub tag stayed clean. The distributed artifacts did not.
LiteLLM’s incident report says stolen developer GitHub credentials were used to alter a private release workflow so it pulled from a lookalike repository. The project says that workflow built, signed and published poisoned artifacts. The artifact-level evidence is narrower: affected container images were signed and carried build provenance, while the seven PyPI wheels were uploaded through Trusted Publishing but PyPI shows no provenance for the release files.
The container signature was not forged. The source decision that preceded it was.
The decisive failure occurred before the signed container build: a trusted workflow was redirected to attacker-controlled source. The PyPI wheels followed a Trusted Publisher path without recorded PyPI provenance.
Five seconds erased the protection boundary
The maintainer’s timeline is unusually precise. At 10:08:29 UTC, branch protection rejected an unauthorized modification to the private release workflow. Five seconds later, a credential tied to a GitHub organization owner was used to override that protection and apply the change.
The altered workflow pointed the releaser at Berri-AI/litellm, a hyphenated lookalike of the legitimate BerriAI/litellm repository. An official staging run began at 10:46:39. The production pipeline followed, and a second compromised developer credential satisfied its approval gate.
By 11:40 UTC, seven malicious wheels had landed on PyPI. Container publication followed minutes later. Credentials associated with a third developer account were then used to delete the production and staging workflow records, limiting the evidence left behind.
LiteLLM says the credentials appear to have been GitHub CLI OAuth tokens exposed in logs from its own internal deployment. It has not established the original intrusion path and says that part of the investigation remains open. That distinction matters: the token abuse is documented; the root entry point is not.
The tag was clean. The artifacts were not.
The public v1.107.0 source tag did not contain the malicious files. That is exactly why a source-only review could produce a false sense of safety.
Independent analysis by StepSecurity and Socket found three unexpected startup files in all seven wheels: cook.pth, cook.py and sitecustomize.py. The affected container images carried the same payload.
The container build provenance supplied an important clue. It recorded the lookalike repository as the source. In other words, the evidence was present—but it authenticated the wrong input because the legitimate workflow had been reconfigured.
A clean tag could not vouch for artifacts produced from a different repository.
This is the uncomfortable limit of release assurance: the image signature authenticated the signing identity and artifact integrity, while the provenance recorded the official workflow and the wrong source. Neither proved that the source change was intended.
Python only had to start
The malicious wheels used Python startup behavior rather than waiting for an application to import LiteLLM. Once version 1.107.0 was installed, an ordinary Python start could execute the credential collector and launch a background remote-code loader.
That expands the exposure boundary beyond a single AI gateway process. The research describes collection targeting cloud identities, provider API keys, process environments, developer credentials and secrets stored in a LiteLLM proxy database. The loader was designed to poll for remotely supplied code on Linux and macOS.
The compromised runtime sat next to the exact secrets an AI gateway is expected to handle.
There is no published victim count, and a download is not the same as execution. The defensible boundary is narrower: LiteLLM says an environment that started Python with 1.107.0 should be treated as compromised. A forensic marker can confirm exfiltration, but its absence does not prove the host remained clean.
A short distribution window can create a long credential problem
The PyPI wheels were live for roughly two hours, from 11:39 to 13:23 UTC. Affected GHCR tags were unavailable by 13:51. Docker Hub tags—including v1.107.0, latest, main-latest and main-stable—remained exposed from 11:46 to 14:20 UTC before removal or repointing.
That containment was fast. It is also not the end of the incident.
Package removal stops new pulls. It does not revoke a cloud role copied during execution, rotate a provider key read from a proxy database or kill a loader already running on a host. Floating container tags make the reconstruction harder because today’s clean latest does not show what a system pulled yesterday.
The PyPI release record now marks 1.107.0 as quarantined. Responders still need their own deployment, image-digest and runtime evidence to decide whether it ever reached an environment.
Start with containment, not another Python command
LiteLLM’s guidance includes a warning that is easy to miss: do not begin by running pip inside a suspect environment. Starting Python can execute the same startup hooks again.
A bounded response sequence is:
1 — Isolate. Isolate the affected host or workload and preserve deployment evidence.
2 — Remove the hooks. Remove the malicious startup files and stop the detached loader without invoking the suspect interpreter.
3 — Rebuild cleanly. Rebuild the environment from a verified clean release; the project currently directs users to 1.106.2 or an earlier explicit version.
4 — Rotate reachable secrets. Rotate every secret the host could reach—not only the obvious AI provider key. That includes instance or task roles, proxy credentials, database passwords and secrets exposed through process environments.
5 — Hunt the credential window. Review cloud, identity and network records for use of those credentials outside the expected infrastructure.
Do not let the first diagnostic step re-trigger the mechanism under investigation.
Release assurance has to verify intent
After a separate March compromise, LiteLLM had moved releases into a private repository, adopted trusted publishing, made container tags immutable and signed images. Those controls addressed direct artifact tampering. They did not survive a principal with enough authority to rewrite the workflow itself.
The next control layer has to test intent independently:
Bind production workflows to an immutable repository identity, not a human-readable name or editable input.
Require out-of-band review when an owner bypasses branch protection or changes a release source.
Keep developer OAuth tokens and model response bodies out of production logs.
Compare packaged artifacts with the qualified source before publication, including files installed outside the normal package tree.
Retain workflow and approval telemetry somewhere a compromised repository identity cannot erase.
None of those controls is glamorous. That is the point. The attack did not need to defeat cryptography. It needed to make an authorized system sign a container built from the wrong source.
A trustworthy release needs five separate answers: who approved it, which workflow ran, which source identity it used, what files it produced and what executed at runtime. A green check on only one of those layers is not the chain of custody.






