Jorge Turbica / blog · Blog ← All articles ES · Leer en español
AI

LiteLLM 1.107.0 stole credentials every time Python started, no import needed

A malicious LiteLLM release on PyPI and Docker stole SSH, cloud and LLM keys on October 21. How to check whether you are hit and what to rotate.

Seven LiteLLM wheels sat on PyPI for an hour and three quarters on October 21, 2026, and all it took was a Python interpreter starting on the same machine for them to steal SSH keys, cloud credentials and LLM provider tokens. Nobody had to import litellm: the payload rode in on a .pth file. It is the second time this year the package has been used as a way in.

What was published, and for how long

According to the project's own incident report, the malicious 1.107.0 was live on PyPI from 11:39:51 to 13:23 UTC. The GHCR Docker tags v1.107.0, latest, main-latest and main-stable pointed at the compromised image from 11:46:56 UTC until they were pulled around 13:51 UTC; the Docker Hub copies stayed up until 14:20 UTC.

ArtifactWindow (UTC, October 21)
PyPI wheels (7)11:39:51 – 13:23
GHCR images11:46:56 – ~13:51
Docker Hub images11:46:56 – 14:20

The v1.107.0 tag on GitHub is clean. The malicious code was added at packaging time, not in the source, so reviewing the release diff would have turned up nothing.

How they got in

The maintainers trace the attack to stolen GitHub CLI OAuth tokens belonging to three engineers. One account was used to change the release pipeline and trigger the publishing run; another approved it. The production approval gate was there, but it only needed sign-off from a team account, and the attacker had several. All the tokens involved have been revoked.

Socket spotted the most useful clue for anyone auditing dependencies: the provenance of the malicious wheels records github.com/Berri-AI/litellm, with a hyphen, as the source, while 1.106.2 points at the real BerriAI/litellm. The attestation was there; nobody checked where it came from.

What the payload does

Each wheel drops three files into litellm-1.107.0.data/purelib: cook.pth, cook.py and sitecustomize.py. The .pth file holds a single line, import cook, and Python runs such lines every time the interpreter starts. That is why the theft hits every Python process in the environment, not just the LiteLLM proxy.

The stealer goes after SSH keys, cloud credentials, CI/CD secrets, AI tool tokens, LLM provider keys and browser-saved passwords, encrypts them and ships them to litellm.dev, a domain the project does not own. StepSecurity also describes a second component that fetches and runs attacker Python code every hour, so uninstalling the package does not make the machine clean again.

Second time in seven months

On March 24, 2026, compromised versions 1.82.7 and 1.82.8 shipped with the same trick: a litellm_init.pth that ran without the library ever being imported. They were pulled the same day, as this one was. What changed is the way in. This time nobody needed to steal a PyPI token, because the attacker drove the project's own pipeline through its own approval gate.

The takeaway for anyone who publishes packages is uncomfortable: a human approval step in the workflow protects nothing if the approver's credentials live on a laptop with an authenticated gh. And for consumers, provenance only helps if something compares the source repository with the one you expect.

Are you affected, and what to do

You are affected if you installed litellm==1.107.0 from PyPI or deployed the proxy from one of those Docker tags between 11:39 and 14:20 UTC on October 21. Floating tags (latest, main-stable) and unpinned requirements.txt files are the usual way in.

bash
# Installed version in each virtualenv or image
pip show litellm | grep -i '^version'

# Leftovers of the payload in site-packages
find / \( -name cook.pth -o -name cook.py \) -path '*site-packages*' 2>/dev/null

# Pin the last release published before the incident
pip install "litellm==1.106.2"

Also look for litellm.dev in your DNS and egress proxy logs. If the version or the file shows up, treat the host as compromised: isolate it, rebuild it from a clean image and rotate everything it could read, not just the LLM keys. That means SSH keys, AWS, GCP or Azure credentials and CI runner secrets if the package was installed inside a job.

Sources

Jorge Turbica

Software Engineer and SRE. I write about the real challenges I run into in production.

See portfolio ↗