LiteLLM 1.107.0 Compromise: What Happened and What to Do Now

A poisoned AI gateway release turned pip install into a credential heist

Featured image

For about two hours on Wednesday, 21 October 2026, anyone who ran pip install litellm without a version pin, or pulled the latest LiteLLM Docker image, may have handed over their cloud keys, AI provider API keys, SSH keys and CI/CD secrets. LiteLLM is the open-source gateway that many teams put in front of OpenAI, Anthropic, Bedrock and other model providers. That makes it a high-value target: one compromised gateway can expose the keys for every provider behind it.

This is the second time in 2026 that LiteLLM releases have been poisoned. The attacker did not touch the project’s source code, which is exactly why this incident deserves a careful read.

TL;DR / Key takeaways

  • What: Malicious litellm==1.107.0 was published to PyPI (seven wheels) and to Docker Hub/GHCR under the v1.107.0, latest, main-latest and main-stable tags on 21 October 2026. It installs a credential stealer and a remote code loader.
  • Who is affected: Anyone who installed 1.107.0 or ran those image tags during the exposure windows (roughly 11:39–14:20 UTC). Version 1.106.2 and earlier are clean. The private enterprise release was not affected.
  • Why it’s nasty: The payload runs at every Python start-up through .pth and sitecustomize.py files, even if LiteLLM is never imported, and even when you run pip to investigate.
  • What to do: Isolate first, don’t run Python on the host, rebuild from clean artifacts, and assume exfiltration succeeded if the host had outbound internet. Rotate everything it could reach.
  • Root cause: A master key leaked in a public repo in May and was never revoked. It led to a GitHub organization owner’s token, and from there to the release pipeline.

Timeline

All times are UTC and come from LiteLLM’s incident reports, which the project says were written while the investigation was still going on.

When What happened
24 Mar 2026 Earlier, separate incident: TeamPCP publishes malicious LiteLLM 1.82.7 and 1.82.8 to PyPI using a stolen PyPI token.
27 May 2026 A config file holding the master key for a LiteLLM Agent Platform deployment is committed to a public repo. The commit is force-pushed over, but the key is never revoked.
10 Oct 2026, 21:27 The attacker uses that master key against the Agent Platform API, and a response returns a GitHub organization owner’s GitHub CLI OAuth token.
21 Oct, 10:08 Branch protection blocks an unauthorized change to the release workflow. Seconds later, a privileged developer credential overrides the protection.
21 Oct, 10:46 The modified workflow builds from a lookalike repository, Berri-AI/litellm (the real one is BerriAI/litellm).
21 Oct, 11:11 A second developer’s credential approves the production release gate.
21 Oct, 11:39–11:40 Seven malicious litellm==1.107.0 wheels are uploaded to PyPI.
21 Oct, 11:46 Poisoned container images are published to Docker Hub and GHCR.
21 Oct, 11:53 A third developer account deletes workflow run records, which limits the logs available afterwards.
21 Oct, 11:55 Chainguard’s scanner flags the release, 16 minutes after upload.
21 Oct, by 13:23 PyPI removes 1.107.0. GHCR tags are unavailable by 13:51, and Docker Hub tags are repointed by 14:20.
21 Oct, ~16:20 Wiz sees the attacker testing stolen AWS instance-role credentials and listing Bedrock models.
23 Oct LiteLLM publishes a corrected attack-chain write-up, which replaces its earlier theory that the tokens came from logs.

Wiz cites ClickPy data showing about 49,000 downloads of the malicious version during its short life on PyPI. For scale, BleepingComputer reports that LiteLLM gets around 92 million PyPI downloads a month.

How the attack worked

The pipeline, not the code

The attacker did not slip anything into LiteLLM’s GitHub source. They took over the release process:

  1. A leaked secret that was never revoked. A master key leaked publicly in May. Force-pushing over the commit hid it from casual view, but the key itself stayed valid.
  2. Token pivot. The master key let the attacker query an internal platform API that returned a GitHub organization owner’s CLI token. With owner-level access, they used CI workflows to collect other maintainers’ tokens and secrets.
  3. Bypassing guardrails. Branch protection did block the first attempt. The attacker then used an owner token to disable it, and a second stolen identity to approve the release. Two-person approval only helps if those two people’s credentials can’t both be stolen.
  4. Signed, official-looking artifacts. The workflow built from the lookalike fork, so the published wheels and images came out of LiteLLM’s real pipeline. LiteLLM had hardened that pipeline after the March incident (trusted publishing, immutable tags, signed images), but the provenance checks still came back green.

Chainguard noted a telling detail: the sdist’s pyproject.toml contained an extra data = "cook" line that the Git tag did not have. If you only review source code or tags, you would never see the payload. It lived only in the prebuilt artifacts.

Execution at Python start-up

Each malicious wheel dropped three files into the root of site-packages:

Wiz describes this pairing of .pth and sitecustomize.py as apparently the first documented case in the wild. The practical result is that any Python process in that environment runs the malware, including pip show litellm or pip uninstall litellm. According to Chainguard, the loader fails on Windows but the stealer still runs there.

What it steals

The stealer, which calls itself privcollect, has 16 modules. According to Wiz and BleepingComputer, it targets:

The data is encrypted with AES-256-CBC, the AES key is wrapped with RSA-4096, and the bundle is sent by HTTP POST to the lookalike domain www.litellm[.]dev/healthcheck.

What attackers did with it

About 4.5 hours after exfiltration, Wiz saw the attacker validate stolen AWS instance-role credentials taken from CI runners and Kubernetes nodes. The calls were sts:GetCallerIdentity followed by bedrock:ListFoundationModels, a pattern consistent with LLMjacking, where stolen cloud accounts are used to run expensive AI models at the victim’s expense. In some environments the attacker also enumerated IAM.

Am I affected?

You are potentially affected if any of the following is true:

You are not affected if you were pinned to 1.106.2 or earlier, if you pinned images by a known-good digest, or if you use the private enterprise release. Chainguard also lists 1.107.0rc1 and rc2 as clean.

Detection and hunting

Important: do not start an investigation by running python or pip on a suspect host, because doing so runs the payload. Inspect the files from outside the environment, or with plain shell tools.

Host and container file checks

~|#| Find the start~|-|up hook files and the malicious dist~|-|info, without starting Python
find / ~|-|xdev \( ~|-|name 'cook.pth' ~|-|o ~|-|name 'cook.py' ~|-|o ~|-|name 'litellm~|-|1.107.0.dist~|-|info' \
  ~|-|o ~|-|path '*site~|-|packages/sitecustomize.py' \) 2>/dev/null

~|#| Run~|-|once marker (present ~|=| exfil likely ran) and loader lock file
ls ~|-|la /tmp/.pc_once_72129118c59f5636c0bb /tmp/remote_code_loader.lock 2>/dev/null

~|#| Lockfiles and requirements that resolved to the bad version
grep ~|-|RnE 'litellm[^0~|-|9]*1\.107\.0' ~|-|~|-|include~|=|'*.txt' ~|-|~|-|include~|=|'*.lock' \
  ~|-|~|-|include~|=|'*.toml' ~|-|~|-|include~|=|'*.cfg' . 2>/dev/null

~|#| A running loader daemon or the stealer's environment flag
ps aux | grep ~|-|E 'remote_code_loader|cook' | grep ~|-|v grep
grep ~|-|l 'PRIVCOLLECT_ACTIVE~|=|1' /proc/*/environ 2>/dev/null

LiteLLM and Chainguard both stress that not finding these artifacts does not mean a host is clean. Ephemeral containers and /tmp clean-up can erase the evidence.

Network: DNS and proxy logs

Search for any resolution of, or connection to, litellm.dev or www.litellm.dev. The legitimate project domain is litellm.ai. A Splunk example:

index~|=|dns OR index~|=|proxy (query~|=|"*litellm.dev" OR url~|=|"*litellm.dev*")
| stats earliest(_time) AS first_seen latest(_time) AS last_seen count BY src_ip, query, url

AWS CloudTrail

Look for stolen-credential validation and Bedrock enumeration, especially from the two attacker IPs Wiz reported (168.100.8.57, 165.227.201.8) or from anywhere outside your own infrastructure. A CloudTrail Lake / Athena-style example:

SELECT eventTime, eventName, sourceIPAddress, userIdentity.arn, userAgent
FROM cloudtrail_events
WHERE eventTime > '2026~|-|10~|-|21 11:00:00'
  AND eventName IN ('GetCallerIdentity', 'ListFoundationModels',
                    'ListInferenceProfiles', 'InvokeModel', 'ListUsers', 'ListRoles')
  AND (sourceIPAddress IN ('168.100.8.57', '165.227.201.8')
       OR userIdentity.arn LIKE '%assumed~|-|role%')
ORDER BY eventTime;

Treat role-session credentials being used from IPs outside your VPCs or NAT gateways as a strong signal. Also check your AI provider dashboards (OpenAI, Anthropic, Bedrock and others) for usage spikes or unfamiliar source IPs.

Response checklist

Immediately (today)

  1. Isolate affected hosts, pods and CI runners from the network. Block egress to litellm[.]dev at DNS and the firewall.
  2. Don’t “fix” with pip. If you must clean in place, delete cook.pth, cook.py, sitecustomize.py and the matching __pycache__ .pyc files from every site-packages directory first, then kill the process holding /tmp/remote_code_loader.lock.
  3. Rebuild rather than clean. LiteLLM advises that rebuilding images and virtual environments from scratch is better. Redeploy from verified artifacts pinned to litellm==1.106.2, with images pinned by digest.
  4. Rotate secrets from a clean machine. This includes cloud IAM keys and role sessions, every upstream provider key stored in the LiteLLM proxy database, LiteLLM master and virtual keys, the DATABASE_URL password, any secret in any process environment on the host, and GitHub, CI/CD, SSH, GPG and registry tokens.

This week

Longer term

OffensioX perspective

Two things stand out. First, AI infrastructure has become a credential vault. An AI gateway is designed to hold the keys to many model providers, and it usually runs next to cloud roles and databases. Attackers have noticed: both LiteLLM incidents this year went straight for those keys, and the follow-up activity Wiz observed was about abusing paid AI models.

Second, build provenance is not the same as trust. LiteLLM had done much of what the industry recommends after March (trusted publishing, signed images, immutable tags, approval gates), and the release still shipped malware, because the attacker controlled the identities those controls rely on. The weak point was a key leaked five months earlier that was hidden but never revoked. For most organisations the lesson isn’t to stop using open source. It is to treat every dependency update as untrusted input: pin it, delay it, contain it, and make sure a single stolen token can’t reach your production secrets. If you’d like a second pair of eyes on your CI/CD, cloud or AI-gateway setup, OffensioX offers penetration testing and security assessments built around exactly these attack paths.

References