All News
securitysupply-chainlitellmpypi

LiteLLM 1.107.0 carried a credential stealer in every wheel, and the tarball never matched its tag

LiteLLM 1.107.0 shipped a credential stealer and an hourly remote-code loader to all seven PyPI wheels and official Docker tags. Two analyses differ on how.

Vlad MakarovVlad Makarovreviewed and published
2 min read
LiteLLM 1.107.0 carried a credential stealer in every wheel, and the tarball never matched its tag

LiteLLM 1.107.0 sat on PyPI for an hour and three quarters on 21 October, and every wheel it served carried a credential stealer that runs before application code, even in a process that never imports litellm.

Three files, two awake at startup

SafeDep's Kunal Singh counts three added files in site-packages across all seven Linux, macOS and Windows wheels: cook.pth, twelve bytes reading "import cook"; cook.py at 22,252 bytes; and sitecustomize.py at 25,509 bytes. Python runs imports from a .pth file at interpreter start and loads sitecustomize by default, so two of the three fire in the app, in pip, in IDE language servers and in CI jobs. The SafeDep report says the stealer takes SSH keys, cloud credentials, CI/CD secrets, AI coding-tool tokens, browser passwords and the provider keys in a LiteLLM proxy database, encrypts the haul and sends it to www.litellm[.]dev; a daemon then re-reads a server address from a GitHub file hourly, re-runs the stealer and pulls fresh code.

The tarball does not match its tag

The source distribution's pyproject.toml gains one line under [tool.maturin], data = "cook", which copies a cook/ folder into the wheel's purelib; the sdist omits that folder, so the line is the only visible change, and no public commit in BerriAI/litellm has it. The v1.107.0 tag points at commit 66c8f5b, stamped 06:59 UTC, whose pyproject.toml declares version 1.108.0. The sdist matches a later main commit, a8113b0, from 08:50 UTC, plus five edited files and cook/. Two edits reset the version and pin the companions to their rc2 values, so a diff against 1.107.0rc2 shows nothing strange. The wheels look factory-made: maturin 1.9.4, correct RECORD hashes, separate build timestamps per platform.

Two accounts, three accounts

SafeDep concludes, with moderate to high confidence, that BerriAI's own pipeline built the wheels from attacker-modified source, and notes no public Actions run published them. LiteLLM's incident post instead describes three engineers' GitHub accounts used, apparently through stolen GitHub CLI OAuth tokens, one to alter the release pipeline and another to approve it, with the build taken from a lookalike repo, Berri-AI/litellm. That post also concedes Docker Hub and GHCR tags, including latest and main-stable, were compromised, which wheel-focused analyses underplay. How the tokens were taken is still unstated, which is the part worth watching.

What to do

Treat an affected host as compromised, not inspectable: pin 1.106.2, confirm with pip show, look for cook.pth and sitecustomize.py on disk, and rotate every secret it could reach, including provider keys in the proxy database. 1.106.2, both release candidates and the pinned companions are clean. It is the second LiteLLM compromise this year: 1.82.7 and 1.82.8 were live for roughly forty minutes in March, which the company blamed on a compromised Trivy dependency in its CI.

Related Articles

Scroll down

to load the next article