What happened to LiteLLM 1.107.0

LiteLLM, the open-source Python library and proxy that many teams use to route requests to OpenAI, Anthropic, Google and dozens of other model providers, published a poisoned release on 21 October 2026. Version 1.107.0 reached PyPI at about 11:40 UTC, and according to a detailed analysis by SafeDep, all seven wheels for Linux, macOS and Windows carried a credential stealer and a remote-code backdoor that the project's public GitHub source does not contain.

The problem was first reported publicly in GitHub issue 47826, opened at 12:06 UTC by researchers from the cloud security firm Upwind. By 13:23 UTC PyPI had removed the release, and its JSON endpoint for 1.107.0 now returns a 404; the latest version listed on the LiteLLM PyPI page is 1.106.2. The official Docker images took longer. Upwind found that the floating latest, main-latest and main-stable tags on GitHub Container Registry and Docker Hub pointed at the same infected v1.107.0 image, for the base, database and non-root variants. A BerriAI maintainer confirmed at 14:32 UTC that the malicious images had been removed.

BerriAI, the company behind LiteLLM, says it is treating the incident as SEV-0. Its advice for open-source users is to pin version 1.106.2, or any explicit version published before the incident. SafeDep and Upwind both report that 1.106.2 and the 1.107.0 release candidates rc1 and rc2 are clean, as are the companion packages litellm-proxy-extras 0.4.110 and litellm-enterprise 0.1.78 that 1.107.0 pinned.

How the stealer runs without importing litellm

The implant does not wait for anyone to call LiteLLM. Each infected wheel adds three files that pip installs at the root of site-packages: cook.pth, cook.py and sitecustomize.py. Python reads .pth files at start-up and executes any line beginning with import, and cook.pth contains exactly one such line, import cook. Python also imports a module named sitecustomize automatically if one is present. The result is that both payloads run whenever any Python process starts in that environment, including pip itself, test runners, IDE language servers and CI jobs that never touch litellm.

SafeDep traced how the files got into the build. The source distribution's pyproject.toml has one extra line in its maturin build section, data = "cook", which tells the build tool to copy a cook folder into the wheel's data directory, from where pip places it in site-packages. The sdist itself leaves the cook folder out, so the one-line change is the only visible clue. SafeDep says no public commit in BerriAI/litellm contains that line, and that the release does not match its own tag: the v1.107.0 tag points to a commit whose pyproject.toml already says version 1.108.0.

The edits were made to look routine. The tampered pyproject.toml sets the version back to 1.107.0 and pins the two companion packages to the same versions as rc2, so a dependency diff against the release candidate shows nothing unusual. The wheels themselves carry the same maturin 1.9.4 build metadata as legitimate releases, and the implant files have valid RECORD entries, which indicates they were present at build time rather than added to the archives afterwards.

What it takes from a machine

The first payload, cook.py, is a compressed and base64-wrapped Python program that calls itself privcollect. SafeDep's decoded configuration lists sixteen collection modules. They gather host and network details, Chromium and Firefox saved passwords and browser history, everything in the user's .ssh folder, shell histories, AWS, Azure, Google Cloud, Kubernetes, Docker and GitHub CLI credentials, private key files, GitLab runner, Jenkins, Vault and Terraform secrets, GPG and TLS private keys, and the environment variables of other processes read through /proc.

Two modules stand out for AI teams. One targets credentials and configuration for AI developer tools, including Claude Code and Claude Desktop, Cursor, Windsurf, Copilot, VS Code MCP settings, Gemini CLI, Codex, Continue, Aider and Hugging Face tokens. Another checks for a DATABASE_URL and, if it finds one, dumps the LiteLLM proxy tables that store upstream provider API keys and model configuration. On a LiteLLM proxy with a database, which is the common production setup, that means every provider key the gateway holds. The stealer also queries cloud instance metadata services for role credentials; Upwind says it validates AWS credentials with an STS GetCallerIdentity call.

The data is encrypted with a random AES-256 key that is itself wrapped with an embedded 4096-bit RSA public key, then posted to a lookalike domain, www.litellm.dev, which Upwind says was registered on 13 October. The project's real domain is litellm.ai. After a successful upload the stealer writes a marker file in /tmp so it runs only once per host.

The second payload, sitecustomize.py, forks a detached daemon that wakes every hour, reads a server address from a file in a GitHub repository belonging to an account called letters9813, runs a copy of the stealer and downloads and executes Python code from that server. The two reports differ slightly on whether that second stage was ever armed: Upwind says the address file had been empty since 20 October, while SafeDep notes the GitHub account returned 404 by 13:40 UTC and says it could not recover any server address. Either way, hosts that ran the daemon earlier may have received code that no one has yet seen.

How a lookalike fork reached the official pipeline

Upwind's follow-up on the issue goes further on the build path. It says the build provenance attached to every v1.107.0 Docker image names BerriAI's own project-releaser GitHub Actions run as the builder, but lists the source repository as Berri-AI/litellm, with a hyphen, rather than BerriAI/litellm. The same releaser recorded the genuine repository as the source for 1.106.2, 1.106.1 and rc2. Upwind lists ten attacker commits stacked on a legitimate main commit, made between 08:56 and 10:46 UTC, which add the cook files, roll back the version numbers and add data = "cook" and Docker COPY lines. The lookalike organisation now returns 404.

The maintainers' own explanation arrived at 16:32 UTC. In a status update signed by Oliver, Krrish and Ishaan, BerriAI said the malicious actions were taken through three engineers' GitHub accounts, apparently using stolen GitHub CLI OAuth tokens. One account triggered the project-releaser run and another approved it. The tokens involved have been revoked, but the company says it has not yet found how they were obtained, so it cannot rule out continued access. As a temporary measure it removed all but one unaffected user from its GitHub organisation and removed trusted publishing links between its registries and that organisation.

This pattern of a legitimate release pipeline shipping attacker code has become familiar. Earlier this month AI Lookout covered the Tensorlake npm compromise, where a maintainer account and the official workflow published a worm, and Lumen's report on exposed LiteLLM proxies being used for mining. Gateways like LiteLLM concentrate many provider keys in one place, which makes them attractive targets in a way an ordinary dependency is not. Researchers have also shown that a backdoored model can steal credentials through a coding agent, a reminder that AI developer environments now hold some of the most valuable secrets in a company.

Vertical timeline in UTC for 21 October 2026: attacker commits at 08:57, release run at 10:51, infected Docker images 11:00 to 11:06, wheels built 11:20 to 11:37, PyPI upload 11:39 to 11:40, public report 12:06, PyPI removal by 13:23, Docker images removed 14:32, stolen token finding 16:32.
AI Lookout chart. How the poisoned LiteLLM release moved from a lookalike fork to PyPI and Docker in under three hours, and how fast it was pulled. Times are UTC, from SafeDep and the GitHub issue. Photo: AI Lookout chart; times from SafeDep and GitHub issue BerriAI/litellm 47826. AI Lookout original · Cropped and resized.

Who needs to act and what to do

Anyone who installed litellm 1.107.0 from PyPI, or pulled the latest, main-latest or main-stable Docker tags from about 11:48 UTC on 21 October until they were removed, should treat the environment as compromised. Upwind's remediation guidance starts with a warning: do not begin with pip install, because any Python start re-runs the payload. Delete cook.pth, cook.py, sitecustomize.py and their cached .pyc files from every affected site-packages, kill the detached daemon that holds /tmp/remote_code_loader.lock, and then rebuild the environment or image from scratch pinned to 1.106.2.

Cleaning the files is the smaller job. Both reports advise assuming the exfiltration succeeded on any host with outbound internet access. The marker file /tmp/.pc_once_72129118c59f5636c0bb confirms an upload, but its absence proves nothing, because containers and temp clean-up remove it. Teams should rotate every credential the host could reach: cloud instance roles, every provider key stored in the LiteLLM proxy database plus the master and virtual keys, the database password, SSH and Git credentials, AI tool tokens and anything in process environments. Upwind also suggests checking AWS CloudTrail for GetCallerIdentity calls from instance roles around install time.

Network defenders can block and search for the published indicators: requests to www.litellm.dev, the X-Token header value in SafeDep's report, Python processes contacting 169.254.169.254 unexpectedly, and the SHA-256 hashes of the eight 1.107.0 files. BerriAI says enterprise customers who pull from its private registry are not affected; that registry is currently distributing an embargoed fix for a separate vulnerability due to be disclosed on 27 October.

What is still unknown

Several key questions remain open. BerriAI has not said how the GitHub CLI tokens were stolen, how many systems downloaded the poisoned packages, or whether any stolen data has been used. PyPI download counts for the roughly two hours the release was live have not been published. The address of the second-stage server, if one was ever set, has not been recovered. The maintainers promised another status update at 18:30 UTC and a post-mortem has been requested by users on the issue.

Release metadata is also being cleaned up. A separate issue about the v1.107.0 tag points out that the tag declares version 1.108.0, and a pull request opened the same afternoon adds a CI check that release tags match pyproject.toml. Upwind and SafeDep both note that LiteLLM has never published PyPI provenance attestations, which would let users tell a tampered build from a legitimate one. One commenter on the issue asked whether BerriAI would make its release workflow public, citing two compromises this year. AI Lookout has not independently analysed the malware; technical details here are from SafeDep, Upwind and BerriAI.

Common questions

Which LiteLLM versions are affected?

Only 1.107.0 on PyPI and the v1.107.0 Docker images, plus the latest, main-latest and main-stable tags that pointed to them on 21 October. SafeDep and Upwind report 1.106.2 and 1.107.0rc1 and rc2 as clean.

Is uninstalling litellm enough?

No. The payload files sit in site-packages outside the litellm package and run on every Python start. Remove them from the shell, kill the daemon, rebuild, and rotate every reachable credential.

How did the malicious release get published?

BerriAI says an attacker used stolen GitHub CLI OAuth tokens from three engineers to trigger and approve its project-releaser run, which built from a lookalike Berri-AI/litellm fork.

Sources & further reading

  1. Compromised LiteLLM 1.107.0 on PyPI Installs a Credential Stealer and Backdoor ↗
  2. [SECURITY] litellm 1.107.0 compromised: cook.pth credential stealer + sitecustomize RCE loader ↗
  3. [Bug]: v1.107.0 tag declares version 1.108.0 in pyproject.toml ↗
  4. litellm on PyPI ↗
How this story was made

Written by Kristian Kostov with AI assistance and checked against the linked sources. Company performance claims are attributed to the company. Analysis reflects AiLookout’s interpretation; we have not independently tested the products discussed. The cover places the companies’ own logos, taken from public-domain or openly licensed files credited in the caption, on a plain background. It is not a product photograph, a screenshot, or an endorsement.

Our editorial standards
Back to all stories