A malicious LiteLLM release stole credentials on install
LiteLLM 1.107.0 on PyPI shipped a credential stealer and an hourly backdoor that ran at every Python startup; the upstream GitHub source never had them.
The malicious LiteLLM 1.107.0 release on PyPI did not wait for you to import it. Installing the wheel dropped three files into site-packages, and two of them (cook.pth and sitecustomize.py) are the kind Python executes at interpreter startup. The credential stealer and the backdoor ran in every Python process on the host: the application, pip, an IDE language server, a CI job. The upstream GitHub source never contained any of it, and PyPI has since pulled the release.
Python runs any line in a .pth file that begins with import. cook.pth is twelve bytes: import cook. That pulls in cook.py, about 61 KB of Python hidden behind one layer of exec(zlib.decompress(base64.b64decode(...))). It walks a fixed module list (SSH keys, cloud credentials, CI/CD secrets, AI tool tokens, browser passwords, shell history, GPG keys, TLS private keys, and the LLM provider keys sitting in a LiteLLM proxy database), encrypts the results with a random AES-256-CBC key, wraps that key with an RSA-4096 OAEP public key baked into the file, and POSTs a tar.gz to www.litellm[.]dev/healthcheck with a hardcoded X-Token header. After a successful upload it writes /tmp/.pc_once_72129118c59f5636c0bb so it exfiltrates only once per host.
sitecustomize.py is the persistent half. Python imports sitecustomize automatically at startup, so this one also fires everywhere. It takes an exclusive lock, forks, calls setsid(), redirects stdio to /dev/null, and detaches into a daemon. Every hour it reads a single line from a GitHub raw URL (letters9813/pythonclient-test/main/domain.txt), runs its own embedded copy of the stealer (this one with no once-per-host marker), then downloads http://<domain>/codepy and runs it through exec(). Whatever that server returns is what runs. The operator repoints the whole fleet by editing one text file.
The injection is one line
The [tool.maturin] section of pyproject.toml in the sdist gained data = "cook". maturin copies a cook/ folder into the wheel’s data tree, and pip lands it in site-packages. The sdist itself omits the cook/ folder, so that single config line is the only visible change in the source distribution. No public BerriAI commit contains data = "cook" or a cook/ folder.
SafeDep compared every sdist file against the git history by blob hash. The tag v1.107.0 points to commit 66c8f5b, whose pyproject.toml already reads version = "1.108.0", so the sdist does not match its own tag. It matches a later main commit (a8113b0) plus five edited files and the planted folder. Four of the edits are harmless readme = "README.md" lines in Cargo.toml that also appear in the legitimate 1.106.2 build. The fifth edit, to pyproject.toml, rolls the version back to 1.107.0 and pins litellm-proxy-extras and litellm-enterprise to the rc2 values. Because the pins match rc2, a dependency diff against rc2 shows nothing unusual, and the injected data = "cook" rides along in the same edit.
The wheels look pipeline-built
The WHEEL metadata names maturin 1.9.4, the same version as the clean releases. The RECORD entries for the three implant files are correct, so nothing was edited after the build. Zip timestamps show three separate native builds (macOS 11:20, Linux 11:21, Windows 11:37 UTC), and the Windows wheel carries CRLF line endings across all 2,804 .py files including the implants, the signature of a git checkout on Windows with line-ending conversion. No public GitHub Actions run produced it; SafeDep checked all 539 runs in the window. litellm-dispatcher[bot] published the GitHub release about ten minutes after the PyPI upload, the same lag seen for 1.106.2, and PyPI holds no provenance attestations for 1.107.0, 1.106.2, or rc2.
SafeDep’s read, at moderate to high confidence, is that BerriAI’s own private release pipeline built these wheels from an attacker-modified git source. A compromised maintainer or bot credential, a private branch or mirror, or a tampered build reference all fit. A stolen PyPI token uploaded from an attacker’s own machine fits worse, because the wheels carry the pipeline’s fingerprints. Only BerriAI’s pipeline logs settle it.
What to do
The clean versions are 1.106.2, 1.107.0rc1, and 1.107.0rc2, with companion pins litellm-proxy-extras==0.4.110 and litellm-enterprise==0.1.78. If 1.107.0 touched a host, assume the stealer ran at the first Python startup after install, not at import, so “that job never imported litellm” is not a defense. Rotate everything in the module list: SSH keys, cloud credentials, CI/CD secrets, AI provider keys, and any keys held in a LiteLLM proxy database. Check for /tmp/.pc_once_72129118c59f5636c0bb and /tmp/remote_code_loader.lock, and for the three files in site-packages. Treat a release whose artifacts do not match their git tag, and which lacks PyPI provenance attestations, as the detectable signal it was here.
The backdoor’s control channel is already dead: the letters9813 account and repository returned 404 about two hours after release, so hosts can no longer fetch a new codepy. Any host that ran the daemon inside that two-hour window may have pulled and executed second-stage code that SafeDep could not recover.
Keep Reading
GhostAction hit 318 repositories in 16 minutes
The GhostAction campaign injects fake security-audit workflows into GitHub repos to exfiltrate secrets from Actions settings and full git history.
supply-chainfunction-flag took 37,419 of MALFEX's 40,767 downloads
Eight malicious npm packages in the MALFEX campaign drew 40,767 downloads, delivering Overlord RAT and a Node.js stealer to Windows hosts.
securityA reverse shell dressed as a Firefox patch
More than 408 AUR packages were infected through malicious install scripts pulling npm and Bun dependencies with infostealer and rootkit behavior.
Latest on the Wire
Full wire →- Apple Reverses Menu Icon Strategy After Year With Golden GateHacker News
- Behind Apple Music's New Brand: An Elaborate ProductionArs Technica
- Code Is Clay: How Agentic AI Reshaped DevelopmentHacker News
- Critical PHP Vulnerabilities Expose 70% of Web to RCEHacker News
New signal daily · RSS
Stay in the loop
New writing delivered when it's ready. No schedule, no spam.