aighten
← Back to blog

LiteLLM 1.107.0 Compromise: Are You Affected?

LiteLLM 1.107.0LiteLLM compromiseLiteLLM supply chain attackPyPI malwarecook.pth+2 more
LiteLLM 1.107.0 Compromise: Are You Affected?

The LiteLLM 1.107.0 compromise is a software supply-chain attack: on October 21, 2026, attackers used LiteLLM's own release pipeline to publish a malicious litellm==1.107.0 to PyPI and to the project's Docker images. The release installs a credential stealer and an hourly remote-code loader that run every time Python starts, whether or not your code imports LiteLLM. If any machine, CI job or container ran 1.107.0, LiteLLM's incident report says to treat it as compromised, rebuild it from scratch and rotate every secret it could reach.

The PyPI release was live for under two hours, from 11:39:51 to about 13:23 UTC. That is enough time to matter for this project: LiteLLM had about 3.4 million PyPI downloads in the day before we checked, and about 92 million over the past month (pypistats, retrieved October 22, 2026). It is also the second compromise of LiteLLM's release process this year.

Key takeaways

  • Only litellm 1.107.0 is malicious. All seven wheels carried the payload. LiteLLM recommends pinning to 1.106.2 or any version published before the incident.
  • Docker images were hit too. The v1.107.0, latest, main-latest and main-stable tags on GHCR and Docker Hub pointed to a poisoned image from 11:46:56 UTC until 13:51 (GHCR) and 14:20 UTC (Docker Hub).
  • The payload steals SSH keys, cloud and Kubernetes credentials, CI/CD secrets, AI coding tool tokens and the LLM provider keys stored in a LiteLLM proxy database, then tries to load more code from the attacker every hour.
  • The attacker got in with stolen GitHub CLI credentials from three LiteLLM developers, overrode branch protection and pointed the private release workflow at a lookalike repository, Berri-AI/litellm.
  • Uninstalling the package is not enough. Remove the implant files, kill the background daemon, rebuild and rotate credentials.

What happened to LiteLLM 1.107.0?

LiteLLM is an open-source Python library and proxy server from BerriAI that puts one OpenAI-style API in front of more than 100 model providers. Teams use the proxy as an internal LLM gateway, so it usually holds the API keys for OpenAI, Anthropic, Google, AWS Bedrock and others in its database. The GitHub repository has about 62,000 stars.

According to LiteLLM's incident report, the attacker held GitHub CLI credentials for three developers. At 10:08:29 UTC on October 21, branch protection rejected an unauthorized change to the private release workflow. Five seconds later, a credential belonging to a GitHub organization owner overrode that protection and applied the change. The modified workflow built the release from Berri-AI/litellm, a repository whose name differs from the real BerriAI/litellm by one hyphen. A second stolen credential approved the production gate, and the pipeline signed and published the result.

The poisoned artifacts came out of LiteLLM's real pipeline; no PyPI token was hijacked. SafeDep found that the wheels' build metadata matched BerriAI's normal tooling, and the official v1.107.0 tag on GitHub does not contain the malicious files. A diff between the GitHub source and the published package would have shown the problem, while a check that the package came from the official pipeline would have passed.

LiteLLM also explained how the tokens leaked. Its report says Claude Code can include a user's GitHub CLI OAuth token in a response body, and that such tokens can end up in LiteLLM logs when response bodies are logged. An unexpected user account on an internal LiteLLM deployment exposed to the internet let the attacker read those logs. LiteLLM suspects a recently patched vulnerability but had not confirmed the entry point when the report was last updated.

Timeline of the attack (UTC, October 21, 2026)

TimeEvent
10:08:29Branch protection blocks an unauthorized change to the release workflow
10:08:34An org-owner credential overrides the protection and applies the change
10:46:39Staging run starts, building from the lookalike repository
11:11:18A second developer credential approves the production gate
11:39:51 to 11:40:13Seven malicious wheels uploaded to PyPI
11:43:05AISLE's dependency firewall flags the first wheel
11:46:56Malicious Docker images published
11:53:49A third credential deletes the workflow run records
13:23PyPI removes litellm==1.107.0
13:51GHCR tags no longer available
14:20All Docker Hub tags removed or repointed to clean images

Sources: LiteLLM incident report and AISLE for the detection time.

AISLE says its scanner flagged the first wheel 3 minutes 14 seconds after upload and all seven by 12:00 UTC. PyPI pulled the release about an hour and 40 minutes after upload, and the Docker Hub tags kept serving the bad image for about two and a half hours.

What the malicious package does

Each wheel drops three extra files into site-packages. Python's site module runs .pth files with import lines and any sitecustomize module automatically at interpreter startup. That is why the payload runs in every Python process on the machine, including pip, test runners, IDE language servers and CI jobs, even ones that never touch LiteLLM.

FileRole
cook.pthRuns import cook at every Python startup
cook.pyAbout 60 KB of obfuscated credential-stealing code
sitecustomize.pyStarts a detached background daemon that fetches and runs remote code every hour

Stage 1: the credential stealer

SafeDep counted 16 collection modules in cook.py. They gather system and network details, browser passwords and history, SSH keys, shell history, AWS, Azure, GCP, Kubernetes and Docker credentials, .pem and .key files, GitLab, Jenkins, Terraform and Vault secrets, tokens for AI developer tools such as Claude Code, Cursor, Copilot and Codex, environment variables of other processes, AWS instance-metadata credentials, GPG keys and TLS private keys.

For a LiteLLM proxy, the stealer also reads the database tables that hold LLM provider API keys. The data is encrypted (AES-256-CBC with an RSA-wrapped key) and uploaded to www.litellm[.]dev, a lookalike of the real litellm.ai domain. It runs once per host and leaves a marker file in /tmp.

Stage 2: the hourly loader

sitecustomize.py forks a daemon that detaches from its parent and holds a lock at /tmp/remote_code_loader.lock. Every hour it reads a server address from a file in an attacker-controlled GitHub repository, runs the stealer again, and downloads and executes Python code from that server. StepSecurity's history of that file shows it was emptied on October 20, the day before the release, and AISLE also found it empty, so the second stage appears never to have been switched on. The GitHub account behind it was gone by about 13:40 UTC. SafeDep still does not rule out that some hosts fetched second-stage code before then. On Windows the loader fails because it relies on Unix-only calls, but the stealer still runs (StepSecurity).

Stolen keys were used within hours

Wiz reported that AWS instance-role credentials taken from CI runners and Kubernetes nodes were tested between 16:20 and 17:01 UTC, about four and a half hours after the release. The attacker called sts:GetCallerIdentity and bedrock:ListFoundationModels, a pattern Wiz links to LLMjacking: using a victim's cloud account to run paid AI models. Wiz is the only source so far to report use of the stolen credentials.

Am I affected by the LiteLLM compromise?

You are affected if any of these ran at any point on October 21, 2026 or later:

  1. A Python environment with litellm==1.107.0 installed. LiteLLM's checklist uses pip show litellm, but on a suspect machine that command starts Python and re-runs the implant. Check without Python first: search lockfiles (uv.lock, poetry.lock, requirements*.txt) for 1.107.0 and look for a litellm-1.107.0.dist-info folder or cook.pth with find.
  2. A container started from ghcr.io/berriai/litellm, litellm-database or litellm-non_root, or the Docker Hub image, using the v1.107.0, latest, main-latest or main-stable tag pulled during the windows above. LiteLLM gives the compromised Docker Hub index digest as sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e.
  3. A CI job that installed LiteLLM without a pinned version during the PyPI window.

Indicators to search for on hosts:

  • cook.pth, cook.py or sitecustomize.py in any site-packages directory, and a litellm-1.107.0.dist-info folder
  • /tmp/.pc_once_72129118c59f5636c0bb (the stealer already uploaded data) and /tmp/remote_code_loader.lock (the loader ran)
  • The environment variable PRIVCOLLECT_ACTIVE=1 in Python processes
  • A detached Python process with parent PID 1 and / as its working directory
  • Outbound traffic to www.litellm[.]dev or to raw.githubusercontent[.]com/letters9813/..., and requests to 169.254.169.254 from Python processes

LiteLLM's report stresses that finding none of these does not prove a host is clean.

Not affected, per LiteLLM: earlier releases, including 1.106.2 and the 1.107.0 release candidates, and enterprise customers who pull from LiteLLM's private registry.

How to clean up and what to rotate

LiteLLM's guidance, in order:

  1. Do not run pip install first in an affected environment. Any Python start re-runs the implant.
  2. Delete cook.pth, cook.py, sitecustomize.py and their __pycache__ copies from every affected site-packages.
  3. Kill the daemon holding /tmp/remote_code_loader.lock.
  4. Reinstall with litellm==1.106.2 pinned. Better still, rebuild virtual environments and images from scratch.
  5. If the host had outbound internet access, assume the data left. Rotate the instance or task IAM role, every provider key stored in the LiteLLM proxy database, LiteLLM master and virtual keys, the DATABASE_URL password and any secret in any process environment on the host.
  6. On AWS, check CloudTrail for GetCallerIdentity calls from the instance role around install time and for use of that role's credentials from outside your network.

Developer laptops need the same treatment. The stealer targets tokens for Claude Code, Cursor, Copilot and Codex, SSH keys and browser-stored passwords, so a developer laptop that installed 1.107.0 needs those re-issued too.

Why LLM gateways are a prime supply-chain target

A gateway such as LiteLLM concentrates the keys to every model provider a company uses, often next to cloud credentials and a database. One poisoned dependency on that box exposes far more than one application.

This is the second time in 2026. In March, LiteLLM 1.82.7 and 1.82.8 were published with a credential stealer after attackers used credentials stolen through a compromised Trivy dependency in LiteLLM's CI. After that incident, LiteLLM's October report says, the team moved releases to a private repository, adopted trusted publishing, made Docker tags immutable and began signing images. The October attacker went around all of that by using real maintainer credentials inside the real pipeline.

The lesson for consumers is that signatures and PyPI attestations prove where a package was built, not that the build input was the code you reviewed. SafeDep also found that 1.107.0 had no PyPI provenance attestation at all, and neither did the clean 1.106.2, so attestation checks would not have flagged anything unusual. Three habits would have contained this incident:

  • Pin exact versions with hashes (pip install --require-hashes, or uv and Poetry lockfiles) and update on purpose, not on every build.
  • Pin Docker images by digest instead of floating tags such as latest or main-stable.
  • Give a new release a cooling-off period of a day or two before production pulls it. uv supports this with its exclude-newer setting, and dependency firewalls can block packages flagged as malicious. AISLE's flag came about three minutes after upload; PyPI quarantined the release about 100 minutes after it.

What LiteLLM has changed so far

The report lists four actions: the packages were removed from PyPI, maintainer credentials were revoked and cut back to the minimum, the malicious workflow revision was removed and the prior version restored, and the affected Docker tags were removed or repointed to verified clean images. As of the report's 20:30 UTC update on October 21, the investigation was ongoing and LiteLLM said it would publish more indicators of compromise.

FAQ

Which LiteLLM versions are safe?

LiteLLM says every version before 1.107.0 is safe and recommends pinning to 1.106.2. SafeDep also lists 1.107.0rc1 and 1.107.0rc2 as clean. Only the final 1.107.0 release on PyPI and the Docker images tagged during the incident window carried the payload.

I never imported LiteLLM in my code. Am I still at risk?

Yes, if 1.107.0 was installed in that environment. The payload uses a .pth file and sitecustomize.py, which Python executes at startup, so any Python process in that environment ran it, including pip itself.

Is uninstalling LiteLLM 1.107.0 enough?

No. The implant files stay in site-packages, the hourly daemon keeps running, and stolen credentials stay valid. LiteLLM recommends removing the files, killing the daemon, rebuilding the environment and rotating every credential the host could reach.

Were LiteLLM Docker images affected?

Yes. The v1.107.0, latest, main-latest and main-stable tags of ghcr.io/berriai/litellm, litellm-database and litellm-non_root pointed to compromised images from 11:46:56 UTC until 13:51 UTC on GHCR and 14:20 UTC on Docker Hub. Images pinned to an earlier digest or version were not affected.

How did the attacker publish through LiteLLM's official pipeline?

With stolen GitHub CLI credentials from three LiteLLM developers. One belonged to an organization owner and was used to override branch protection; the modified workflow built from the lookalike repository Berri-AI/litellm, and a second credential approved the production release.

Sources

Share this post