# LiteLLM 1.107.0 Left PyPI. The Host Did Not.

> BerriAI says malicious litellm 1.107.0 wheels sat on PyPI from 11:39:51 UTC on 21 October until removal by 13:23 UTC. Docker tags including latest pointed at a bad image from 11:46:56 UTC until 14:20 UTC. Git tag v1.107.0 is absent. PyPI latest is 1.106.2, uploaded at 10:44 UTC, before those wheels. A host that ran it is still in scope.

Source: https://hackerlogs.com/blog/litellm-pypi-wheel-clock
Published: 2026-10-21

## Key takeaways

- BerriAI's update, last marked 20:30 UTC on 21 October, is the primary. Seven litellm 1.107.0 wheels were on PyPI from 11:39:51 UTC until removal by 13:23 UTC.
- Docker Hub tags v1.107.0, latest, main-latest, and main-stable pointed at a bad image index from 11:46:56 UTC until 14:20 UTC. At 13:51 UTC, GHCR was already unavailable and Hub was not.
- A registry read tonight shows PyPI latest 1.106.2, first file at 10:44:22 UTC. Version 1.107.0 is absent. The git ref for tag v1.107.0 returns 404.
- Commit 20535d8 at 10:46:29 UTC is still fetchable and is not on main. Its build file sets data to cook. Ancestor 66c8f5b is on main and does not.
- The impact clock is any host that started the wheel or the bad image. A missing PyPI version does not clear that host. Assume theft if the host had outbound internet.

US trends at 22:27 UTC were BioWare, Mass Effect, and football. Claude Duet was on that list. Hacker News led with [item 50181114](https://news.ycombinator.com/item?id=50181114), created at 14:00:26 UTC, pointing at a SafeDep note on LiteLLM. The primary is [BerriAI's incident post](https://docs.litellm.ai/blog/security-incident-oct-21-2026). Its last-updated line is 20:30 UTC on 21 October. The same page still names 22:30 UTC as the next update.

 Critical because the vendor says the published wheels and images steal credentials and load remote-code tools, on the package and the official images operators actually run.

<img src="/litellm-pypi-wheel-clock.svg" alt="Three panels for the LiteLLM 1.107.0 window: wheels live under two hours, a cook commit that is not the git tag, and a host that stays in scope after PyPI removal." width="1200" height="675" />

## The wheels and the images.

BerriAI says the release pipeline pushed v1.107.0 to PyPI and to the container registries. The post's words for the payload are compiled malware that steals credentials and loads remote-code execution tools. All previous versions are safe, in that post. The private enterprise registry was not part of this publish.

PyPI `litellm==1.107.0` is all seven wheels, live from 11:39:51 UTC to removal by 13:23 UTC. The post calls that about two hours, from 11:39 UTC to 13:23 UTC. The upload burst it prints is 11:39:51 UTC to 11:40:13 UTC. This brief did not download those wheels. A read of the public registry tonight shows no `1.107.0` key. Latest is `1.106.2`: seven wheels and one sdist, none yanked. The first wheel is 10:44:22.902887 UTC. The sdist is 10:44:51.337554 UTC. That is 55 minutes before the malicious upload. The vendor pin and the registry latest are the same version, and that version predates the bad wheels.

Docker Hub tags `v1.107.0`, `latest`, `main-latest`, and `main-stable` pointed at image index `sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e` from 11:46:56 UTC until 14:20 UTC. GHCR used the same tag names from 11:46:56 UTC. At the 13:51 UTC check, those GHCR tags were unavailable, and Docker Hub still resolved to that index. Hub cleanup is 14:20 UTC. The Hub window is longer than the PyPI window. Wheels can be gone while `latest` is still the bad index.

A Docker Hub read tonight: tag `v1.107.0` returns 404. Tags `latest`, `main-latest`, `main-stable`, `v1.106.2`, and `1.106.2` share index `sha256:7294b90f868c5c38c99b83021c7ba46f9b439e95f8e32964c925c7d2563a03f7`. That is not the index in the post. `v1.106.2` last updated at 10:48:12 UTC. `latest` last updated at 17:50:38 UTC, by a different account, onto that same 1.106.2 index. The platform blobs under it were pushed at 10:47 UTC, before 11:46. A pull now is that index. A pull from 11:46:56 UTC to 14:20 UTC was the other one. This brief did not pull either image.

> The package clock is the registry. The impact clock is a process that already started. BerriAI says to assume theft if 1.107.0 was installed on a host with outbound internet. A marker file confirms that. Its absence proves nothing, because containers and temp cleanup remove it. Deleting the version from PyPI does not delete the files already on disk, and it does not rotate the keys those files could read.

## The commit is not the tag.

The post says the official v1.107.0 source tag does not contain the malicious files, and that the artifacts were built from an attacker-controlled repository. A read tonight of `refs/tags/v1.107.0` returns 404. There is no release object under that name either. Tags `v1.107.0-rc.1` and `v1.107.0-rc.2` do exist. They are not 1.107.0.

[Commit 66c8f5b1a9](https://github.com/BerriAI/litellm/commit/66c8f5b1a9cefaa7c55b758f86f4ab552f2e859e) is fetchable. Committer time is 06:59:32 UTC on 21 October. Compare of `main` to that commit is behind: ahead 0, behind 5. It is in main's history. Its `pyproject.toml` has no `cook` line.

[Commit 20535d8fa1](https://github.com/BerriAI/litellm/commit/20535d8fa11f786a6c24df7488a0e449ed5f09ad) is also fetchable. Author name `Berri-AI`, committer time 10:46:29 UTC, signature unsigned. The message says it copies cook data into the image build. `pyproject.toml` on that commit contains `data = "cook"`. The tree contains `cook/purelib/cook.pth`, `cook/purelib/cook.py`, and `cook/purelib/sitecustomize.py`. This brief did not open those three files. Compare of `66c8f5b` to `20535d8` is ahead 11, behind 0. Compare of `main` to `20535d8` is diverged: ahead 10, behind 4. That commit is not on main. Main's tip tonight is `b6f91a221b6b` at 14:03:18 UTC. Repo `pushed_at` is 22:22:41 UTC.

The post's staging run started at 10:46:39 UTC, ten seconds after that commit, using `Berri-AI/litellm` at `20535d8`. The lookalike repository API returns 404 tonight. The commit remains on `BerriAI/litellm` because the objects are still there. StepSecurity published a payload writeup at 14:27 UTC. This brief does not repeat it.

The 20:30 UTC update gives the release path. On Monday they widened publish rights so two engineers could ship bugfix backports. The post does not date that Monday. 21 October 2026 was a Wednesday, so the preceding Monday is 19 October. Branch protection rejected an unauthorized workflow edit at 10:08:29 UTC. At 10:08:34 UTC a privileged developer credential overrode that and applied workflow commit `9cf97e935aeaf791b9c8db224a2bb3ee156134ce`. That commit is not on the public repo. A second developer credential approved the production gate at 11:11:18 UTC. A third deleted the production and staging workflow records from 11:53:49 UTC to 11:54:01 UTC. The unauthorized workflow revision was removed from 12:09:38 UTC to 12:24:52 UTC.

The same update says they have seen Claude Code place a GitHub CLI OAuth token in a response body, and that logged response bodies can leave that token in LiteLLM logs. They found an unexpected account on an internal deployment that was open to the internet. They believe the tokens were read there. They have not confirmed how that deployment was entered. They suspect a vulnerability that is already patched. They do not name it. This brief does not describe how a token is taken from a log.

The private registry note is a second clock. Enterprise customers pulling from it were not in the 1.107.0 publish. That registry is also where an embargoed fix lives, with disclosure scheduled for 27 October. The post does not describe that bug. Do not mix the two dates.

## What to do on a host that started it.

1. If `pip` installed `litellm==1.107.0`, or a container ran `v1.107.0`, `latest`, `main-latest`, or `main-stable` between 11:46:56 UTC and 14:20 UTC, treat that host as the post describes. Do not start with `pip install`. BerriAI says any Python start in that environment runs `cook.pth` and `sitecustomize.py`, and that includes pip.
2. From a shell, delete `cook.pth`, `cook.py`, `sitecustomize.py`, and the matching `__pycache__` files, from every affected site-packages. Then stop the detached Python process the post says holds `/tmp/remote_code_loader.lock`. Rebuilding the venv or the image is the cleaner path.
3. Pin `litellm==1.106.2` after those files are gone. Tonight that is also PyPI latest, and Docker Hub `latest` matches the `v1.106.2` index above, not `sha256:1bbcb1443bc9`. A floating tag that looks right at 17:50 UTC says nothing about a pull at 12:00 UTC.
4. If the host had outbound internet, assume the theft succeeded. Rotate the instance or task role, every provider key in the LiteLLM proxy database, the master and virtual keys, the `DATABASE_URL` password, and any secret that was in the environment of any process on the host.
5. The post says to check CloudTrail for `GetCallerIdentity` from that instance role around install time, then for use of the role's credentials from outside your infrastructure.
6. Leave the 27 October enterprise disclosure on its own ticket. The private registry was not this publish. The post does not say the embargoed bug is what let the tokens out.

## Sources

- [Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised](https://docs.litellm.ai/blog/security-incident-oct-21-2026) (2026-10-21)
- [Commit 20535d8 on BerriAI/litellm](https://github.com/BerriAI/litellm/commit/20535d8fa11f786a6c24df7488a0e449ed5f09ad) (2026-10-21)
- [litellm on PyPI](https://pypi.org/project/litellm/)
- [litellm image tags](https://hub.docker.com/r/litellm/litellm/tags)
- [litellm 1.107.0 compromised on PyPI](https://www.stepsecurity.io/blog/litellm-1-107-0-compromised-pypi-backdoored-wheels-credential-stealer) (2026-10-21)
