LiteLLM 1.107.0 Carried a Credential Stealer on PyPI for 104 Minutes and in Docker Tags for Two and a Half Hours
Published on gastropod.io on 10-27-2026
LiteLLM 1.107.0 Carried a Credential Stealer on PyPI for 104 Minutes and in Docker Tags for Two and a Half Hours
Tags: vulnerability-intelligence, software-supply-chain-security, pypi, python, oci-containers, credential-theft, ci-cd-security
An attacker repointed LiteLLM's release workflow at a lookalike repository and published version 1.107.0 to PyPI, Docker Hub, and GHCR on October 21. LiteLLM provides a unified interface to language model APIs and records roughly 92 million monthly PyPI downloads, with NVIDIA, Netflix, AT&T, and Okta among its users (BleepingComputer). All seven 1.107.0 wheels contained the payload, covering Windows, Linux, and macOS.
The exposure windows were short. The wheels were live on PyPI from 11:39 to 13:23 UTC. The Docker Hub tags v1.107.0, latest, main-latest, and main-stable pointed at the compromised image index from 11:46 until 14:20 UTC, and the GHCR tags until 13:51 UTC. Wiz counted approximately 49,000 downloads of the malicious version in that window (Wiz). LiteLLM identifies 1.106.2 as the most recent safe release and states that all earlier versions are unaffected.
Installing the package wrote three files directly into the site-packages root: cook.pth, cook.py, and sitecustomize.py. Python processes .pth files and sitecustomize.py at interpreter startup, so the payload ran on any Python invocation, with no import of LiteLLM required. Wiz describes the simultaneous use of both techniques as the first documented case in the wild.
The stealer, named privcollect in the code, runs 16 modules. It collects browser credentials and cookies from Chrome, Firefox, Edge, and Safari; AWS credentials from ~/.aws/, environment variables, and the EC2 and ECS instance metadata service; GCP service account keys and application default credentials; Azure tokens from the CLI cache; API keys for OpenAI, Anthropic, Cohere, and other providers from environment variables and config files; and SSH private keys, GPG keys, and shell history. On a host running LiteLLM Proxy, it queries the PostgreSQL credentials table directly to read every configured provider credential. It encrypts the collected data with AES-256-CBC under an RSA-4096 wrapped key and posts it to www.litellm[.]dev/healthcheck, a lookalike domain the attacker registered on October 13. The sitecustomize.py component polls a GitHub file for a domain to fetch a second-stage script from, and Wiz found that file never populated beyond test values.
Wiz observed the attacker using the stolen credentials about four and a half hours after exfiltration began. Between 16:20 and 17:01 UTC, the attacker validated AWS instance role credentials taken from CI runners and Kubernetes nodes across multiple environments, calling sts:GetCallerIdentity followed by bedrock:ListFoundationModels, consistent with hijacking model access for compute. In some environments the attacker also enumerated IAM roles, users, and access keys.
What Happened?
LiteLLM attributes the release-pipeline access to stolen GitHub CLI OAuth tokens belonging to three engineers. In a follow-up, the project reported that Claude Code embeds a user's GitHub CLI OAuth token into a response body under specific conditions, that those response bodies were logged by LiteLLM's own internal deployment, and that the deployment was reachable from the public internet. LiteLLM states that the attacker read the tokens from those logs and used them to trigger and approve the release run (The Hacker News). It is LiteLLM's second supply chain compromise this year, after the March incident whose stolen data Hudson Rock later linked to 2,488 corporate domains (Help Net Security).
What should you do?
Anyone who installed 1.107.0 should treat every credential reachable from that host as compromised and rotate it, remove the three payload files, and rebuild affected hosts and images. Upgrading alone leaves the startup hooks in place.
Finding which hosts those are is the harder half. The malicious wheels passed through the project's real release pipeline, so the artifacts carried the identity and provenance a build expects, the same gap as the AsyncAPI compromise I have already covered here: https://gastropod.io/news/asyncapi-npm-supply-chain-attack-a-valid-signature-isn-t-the-same-as-a-safe-package. Credentials taken from a project's own release pipeline produced the same result at MemTensor last month: https://gastropod.io/news/memtensor-versions-contained-a-credential-stealer-published-with-tokens-taken-from-its-own-release-pipeline.
Two details make the scoping work harder than just reading a lockfile. The compromise hit an unpinned dependency range and four Docker tags, so a nightly build, a CI cache refresh, or a docker pull litellm/litellm:latest during a 104-minute window decides whether a host is affected, and none of those runs leave a record in a lockfile. PyPI quarantined the version and Docker Hub repointed the tags, so the upstream registries no longer show what they served during the window.
BTW; Gastropod keeps that record at your own intake point. Every pull through the proxy is written to the access spine with the PURL, the sha256 digest, the client, the user or token, the source IP, and the timestamp, so answering "did anything here pull litellm 1.107.0 between 11:39 and 13:23 UTC on October 21" is a filter on the log rather than a search across build hosts. Because Gastropod identifies artifacts by digest rather than by tag, a pull of latest during the window is recorded against the compromised image index, which separates it from a pull of the same tag an hour later. The blast-radius graph shows which of your services pulled an affected version, and a block rule at intake stops that version from entering a new build on any workstation or runner while you rotate credentials.
To be clear, Gastropod did not detect this release as malicious before anyone reported it. The build came from the project's genuine pipeline with valid credentials, so content inspection and provenance checks are the wrong tool for it. The product page shows the audit log and blast-radius views on a real malicious package, and you can point pip and Docker at Gastropod from app.gastropod.io/start to get the same records for your own traffic.
Sources:
- LiteLLM Compromised, Steals Credentials (Wiz)
- LiteLLM release pipeline hacked to distribute info-stealer malware (BleepingComputer)
- LiteLLM Suspects GitHub Token Leak in AI Gateway Logs Behind Malicious Package Release (The Hacker News)
- LiteLLM hit by second supply chain attack this year, users urged to rotate credentials (Help Net Security)
- litellm 1.107.0 compromised, issue #47826 (BerriAI/litellm on GitHub)