⏱️ Reading time: 11 min

LiteLLM 1.107.0 landed on PyPI on October 21, 2026 with something no maintainer approved: a credential stealer and a remote execution backdoor hidden inside three new files. The seven installation wheels, for Linux, macOS, and Windows, copied them straight into site-packages, and both started up with any Python process on the machine, even one that never imported litellm.

📑 En este artículo
  1. TL;DR
  2. What Is LiteLLM
  3. What Happened
  4. Context and History
  5. How LiteLLM 1.107.0 Ended Up with a Backdoor in Its Pipeline
  6. Impact and Analysis
  7. What’s Next
  8. Frequently Asked Questions
    1. What exactly does LiteLLM 1.107.0 do when installed?
    2. Am I at risk if I never imported litellm in my code?
    3. Which versions of the LiteLLM library are safe?
    4. How did BerriAI’s backdoor get into the package if the GitHub repository is clean?
    5. What data did the cook.py malware steal?
    6. Have PyPI or GitHub already fixed the problem?
  9. References

BerriAI’s public repository, the company behind the project, never had this code. According to SafeDep’s investigation, the attack originated from the company’s own private build pipeline, not from a stolen PyPI token used from another machine.

TL;DR

  • LiteLLM 1.107.0 (PyPI, October 21, 2026) installed a credential stealer and backdoor in all 7 packages.
  • Three new files in site-packages ran with any Python process, without importing litellm.
  • The GitHub repository never had this code: the PyPI build didn’t match any commit.
  • SafeDep finds with moderate to high confidence that the attack came from BerriAI’s private pipeline.
  • PyPI pulled the version; 1.106.2, 1.107.0rc1, and 1.107.0rc2 remain confirmed clean.

What Is LiteLLM

LiteLLM is an open source Python library, maintained by BerriAI, that exposes a single OpenAI-compatible API for calling more than 100 language model providers. LiteLLM 1.107.0, released on October 21, 2026, is the version that ended up compromised on PyPI.

The LiteLLM library works as a proxy: a team points its application at it and switches from GPT to Claude or to an open model by changing a single configuration line, without touching the business logic. BerriAI also sells an enterprise layer with an admin dashboard and provider keys stored in its own database, the same type of data that ended up in the credential stealer’s crosshairs.

What Happened

The chain begins at the installer. Running pip install litellm==1.107.0 adds three new files to site-packages: cook.pth, cook.py, and sitecustomize.py. Python automatically executes .pth files and sitecustomize.py on every interpreter startup, so both run as soon as any script opens, without anyone importing the compromised package from PyPI.

cook.pth is twelve bytes with a single line: import cook. That import triggers cook.py, 22,252 bytes, the credential stealer. According to SafeDep, it collects SSH keys, cloud credentials, CI/CD secrets, AI tool tokens, browser-saved passwords, and the LLM provider keys stored in LiteLLM 1.107.0’s proxy database. It encrypts everything and sends it to www.litellm[.]dev, a domain that mimics the real project but doesn’t belong to it.

The seven wheels of the compromised version covered Linux, macOS, and Windows alike. Foto de Albert Stoynov en Unsplash

sitecustomize.py, 25,509 bytes, does two things. First, it carries a backup copy of the credential stealer in case cook.py fails. Second, it spins up a background daemon that, every hour, reads a server address from a file hosted on GitHub, runs the credential theft again, and downloads and executes whatever that server sends it. In effect, this is a persistent remote execution backdoor, not a one-time theft.

BerriAI’s public GitHub repository never had these three files or the configuration line that generates them. PyPI pulled the package as soon as the finding became known. Versions 1.106.2, 1.107.0rc1, and 1.107.0rc2 are clean, as are the companion packages the malicious version pins as dependencies: litellm-proxy-extras==0.4.110 and litellm-enterprise==0.1.78.

Context and History

BerriAI built LiteLLM as a thin layer over dozens of model APIs, and the project became a common piece in AI stacks that need to switch providers without rewriting integrations. That position makes it an attractive target: compromising a single version is enough to reach the LLM provider keys of any team that installs it, exactly the kind of secret that moves real money in AI billing today.

Supply chain attacks on open packages aren’t new: npm and PyPI have piled up cases of typosquatting, stolen maintainer accounts, and trojanized dependencies. What’s different about this case is that the malicious code doesn’t appear in any public commit or in the source code anyone can audit on GitHub; it only lives in the binary artifact that PyPI distributes. Auditing the repository wouldn’t have helped at all.

How LiteLLM 1.107.0 Ended Up with a Backdoor in Its Pipeline

The change that enables the whole attack is a single line in the pyproject.toml of the 1.107.0 sdist, inside the maturin build section, the tool BerriAI uses to compile the wheels:

-version = "1.108.0"
-    "litellm-proxy-extras==0.4.111",
-    "litellm-enterprise==0.1.79",
+version = "1.107.0"
+    "litellm-proxy-extras==0.4.110", # same pins as 1.107.0rc2
+    "litellm-enterprise==0.1.78",
+data = "cook" # the injected line

data = “cook” tells maturin to copy a cook/ folder into litellm-1.107.0.data/purelib/ when building the wheel. pip installs that content directly into site-packages, which is why the three implant files end up there without going through any installation step visible to the user. The sdist doesn’t include the cook/ folder, so that line is the only visible change in the distributed source code.

The wheel’s RECORD file, which lists every installed file, confirms the three additions: cook.pth (12 bytes), cook.py (22,252 bytes), and sitecustomize.py (25,509 bytes). Anyone who wants to confirm whether an installation got infected by the cook.py implant can manually check for those three names in their local site-packages: their mere presence confirms the compromise, since no legitimate litellm release includes them.

VersionStatusNotes
1.106.2CleanThe sdist matches the tag’s commit except for the readme changes in Cargo.toml
1.107.0rc1CleanWithout the three implant files
1.107.0rc2CleanSame litellm-proxy-extras and litellm-enterprise pins used by the malicious build
1.107.0CompromisedIncludes cook.pth, cook.py, and sitecustomize.py in all seven wheels; pulled from PyPI

The v1.107.0 tag on GitHub points to a commit whose pyproject.toml says version = “1.108.0”, not 1.107.0. SafeDep compared every file in the sdist against the git trees by blob hash and found it matches a later commit on main, plus five edited files and the cook/ folder that isn’t in any public commit. Four of those five files are Cargo.toml files that gain a readme = “README.md” line, the same change found in the legitimate 1.106.2 and 1.107.0rc2 releases. The fifth is the pyproject.toml with the injected data = “cook” line.

The malicious wheels don’t look handcrafted: they match BerriAI’s normal pipeline output. The WHEEL file names maturin 1.9.4, the same version the legitimate releases use. The zip timestamps show separate native builds (macOS at 11:20 UTC, Linux at 11:21 UTC, and Windows at 11:37 UTC), and in the Windows wheel the 2,804 .py files have CRLF line endings, including the three implant files, while the Linux wheel has none: the typical pattern of a git checkout done on Windows with line-ending conversion turned on.

SafeDep reviewed 539 GitHub Actions runs between 09:30 and 12:30 UTC that day.

SafeDep reviewed those 539 GitHub Actions runs and didn’t find a single one that published the release. At 11:50 UTC, about ten minutes after the upload to PyPI, the litellm-dispatcher bot published the v1.107.0 GitHub release, the same delay 1.106.2 had. PyPI has no provenance attestations for 1.107.0, 1.106.2, or rc2.

With that evidence, SafeDep concludes with moderate to high confidence that BerriAI’s private pipeline built the wheels from a git source modified by the attacker. It could have been a compromised maintainer or bot credential, a private branch or mirror, or the reference the pipeline uses to build. A stolen PyPI token used from the attacker’s own machine explains the evidence worse, because it wouldn’t produce builds that match the real pipeline’s maturin version, timestamps, and line format.

⚠️ Heads up: if you installed litellm 1.107.0 between October 21, 2026 and the package’s removal, rotate the SSH keys, CI/CD tokens, and LLM provider keys on that machine, even if your code never imported litellm.
flowchart TD
A["Attacker modifies pyproject.toml in a private source"] --> B["BerriAI's build pipeline generates the wheels from that source"]
B --> C["PyPI receives litellm 1.107.0 with three extra files"]
C --> D["pip install copies cook.pth, cook.py, and sitecustomize.py to site-packages"]
D --> E["Any Python process runs the credential stealer"]
D --> F["sitecustomize.py's daemon downloads and runs code every hour"]

Impact and Analysis

The damage doesn’t depend on using the library. Any Python process that started on a machine with litellm 1.107.0 installed triggered the credential theft, including a script that had nothing to do with AI. That turns any CI environment, base container, or development machine that installed that version, even as a transitive dependency, into a compromised target.

The target is also specific to the moment: the implant aims directly at AI secrets (LLM provider tokens, AI tool credentials) on top of the classic SSH and CI/CD ones. An attacker with those keys can bill inference under the victim’s name, read prompts with sensitive data that pass through the proxy, or use them as a bridge into other systems.

The most serious blow to trust in the ecosystem is that the code was never in the auditable repository. Pinning by commit hash wouldn’t have helped if the pipeline builds from a source different from the one the maintainer believes it’s building from. The real mitigation involves verifying the published artifact, not the source code on GitHub.

One question remains unanswered: SafeDep doesn’t have access to BerriAI’s internal logs, so it can’t confirm whether the attacker got in with a maintainer credential, a compromised bot, or a private mirror. Only those records, which BerriAI hasn’t made public, can close that part of the story.

What’s Next

BerriAI still hasn’t published a post-mortem with the root cause of the access to its pipeline. Until that happens, any conclusion about how the attacker got in stays in the territory of the reasoned hypothesis SafeDep lays out. The absence of provenance attestations on PyPI for the latest litellm releases is, for now, the most visible symptom that this kind of control is missing from the pipeline.

The case also adds pressure to a demand that’s been growing in the Python community: that PyPI require, or at least display, build provenance attestations by default for projects with this level of adoption, so that a wheel without them stands out before installation, not after a credential theft.

Try this today: check whether cook.pth or cook.py exist in any site-packages across your infrastructure, and if you ever installed 1.107.0, rotate every secret that passed through that machine before continuing to use it.

📬 Get new articles by email

We only email about big articles (1-2 a month).

Frequently Asked Questions

What exactly does LiteLLM 1.107.0 do when installed?

It adds three files to site-packages: cook.pth and sitecustomize.py, which Python runs on every startup, and cook.py, the credential stealer both of them invoke. It steals SSH keys, cloud credentials, CI/CD secrets, AI tokens, browser passwords, and LLM provider keys, and also leaves behind a daemon that runs again every hour.

Am I at risk if I never imported litellm in my code?

Yes. The .pth files and sitecustomize.py run with any Python process that starts on that machine, without the script importing the library or even knowing it exists.

Which versions of the LiteLLM library are safe?

1.106.2, 1.107.0rc1, and 1.107.0rc2 are confirmed clean, as are litellm-proxy-extras==0.4.110 and litellm-enterprise==0.1.78, the companion packages the malicious version pins.

How did BerriAI’s backdoor get into the package if the GitHub repository is clean?

The malicious sdist has an extra line in pyproject.toml (data = “cook”) that doesn’t exist in any public commit, and the wheels were built on the company’s private pipeline from a git source the attacker modified, not from the code visible on GitHub.

What data did the cook.py malware steal?

SSH keys, cloud credentials, CI/CD secrets, AI tool tokens, browser-saved passwords, and the LLM provider keys stored in the proxy’s database. All of it encrypted and sent to www.litellm[.]dev.

Have PyPI or GitHub already fixed the problem?

PyPI pulled version 1.107.0 from the index. The GitHub repository never had the malicious code, so it didn’t need cleanup; the risk lies in any machine where that version was already installed before the removal.

References

📱 Like this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion

Featured image: Foto de Markus Spiske en Unsplash

Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.

Leave a comment
Categories: Security

Clara Vásquez

Cybersecurity analyst focused on critical vulnerabilities, zero-days, and emerging threats. Covers high-impact CVEs, malware analysis, ransomware incidents, and security trends with a LATAM lens.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

You can include code inside <code>…</code> or, for several lines, <pre><code>…</code></pre>.

This site uses Akismet to reduce spam. Learn how your comment data is processed.