What happened
LiteLLM says its release pipeline was compromised early on October 21 and pushed v1.107.0 to the Docker registry and PyPI, carrying compiled malware that steals credentials and loads remote-code execution tools. The project says all earlier versions are safe, the affected releases have been pulled, and customers on its private enterprise release were not impacted.
This is a supply-chain compromise, not a vulnerability in LiteLLM's application logic. The code you would audit is legitimate. The built artifacts are what carried the payload.
Exposure windows
All times are UTC on October 21, 2026, per LiteLLM's incident report:
| Channel | Affected | Window |
|---|---|---|
| PyPI | litellm==1.107.0, all seven wheels | 11:39:51 to 13:23 |
| Docker Hub | v1.107.0, latest, main-latest, main-stable | 11:46:56 to 14:20 |
| GHCR | The same four tags, across litellm, litellm-database and litellm-non_root | 11:46:56 until unavailable by 13:51 |
The Docker Hub digest to look for is sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e.
Floating tags are the sneaky part. JFrog notes that systems pulling the mutable latest, main-latest or main-stable tags while they pointed at 1.107.0 should be treated as exposed. You did not have to ask for 1.107.0 by name. LiteLLM's guidance is that anyone who used an unpinned version through pip in the PyPI window, or ran those image tags in the container window, is affected.
Wiz, citing ClickPy data, reports roughly 49,000 downloads of the malicious version. Treat that as one researcher's estimate, not a count of confirmed victims.
Does it hit Windows?
Yes. The wheels cover macOS (x86_64 and arm64), manylinux and musllinux (x86_64 and aarch64), and Windows. StepSecurity adds that the decoded payloads are identical across platforms, and the Windows wheel differs only in CRLF line endings.
JFrog's analysis has one Windows-specific wrinkle. The hourly loader in sitecustomize.py relies on os.fork(), so on Windows that daemon path fails silently. The cook.pth hook still imports cook.py, though, so Windows installs remain exposed to the credential stealer. In short, Windows developer machines and build agents are not protected by the loader quirk. The theft path still works.
How the malware runs
The malicious wheels add three files to site-packages: cook.pth, cook.py and sitecustomize.py. Python's own startup behavior does the rest.
cook.pthcontains a single import line. Python's site module executes such lines during interpreter initialization, socook.pyruns whether or not your app ever imports litellm.sitecustomize.pyis imported automatically after site paths are processed. Here it starts a detached loader.- Result: any Python launch in that environment can retrigger the payload. That includes running
pipto investigate or clean up.
The sdist is a puzzle for reviewers. JFrog found the source tarball lacks the three payload files, but its pyproject.toml carries an added data = "cook" setting. That setting tells the build tool to copy a build-time cook/ directory into the wheel. A reviewer who inspects only the sdist or the Git tag would miss the payload.
What it steals
JFrog's decoded analysis describes a collector (self-named "privcollect") aimed at developer workstations, CI workers, cloud workloads, containers and LiteLLM proxies. Targets include:
- SSH, GPG and TLS private keys, shell histories, and key files such as
.pemand.pfx - Browser passwords, cookies and history from Chromium-family browsers and Firefox
- AWS, Azure and Google Cloud credentials, Kubernetes configs and service-account tokens, and Docker secrets
- CI/CD and infrastructure-as-code secrets (Jenkins, Vault, Terraform and others)
- Package and source-control credentials such as
.npmrc,.pypircand.git-credentials - Configs and tokens for AI developer tools, including GitHub Copilot, Cursor and Claude
- Environment variables, plus cloud metadata credentials from AWS, Google Cloud and Azure
- LiteLLM proxy data reached through
DATABASE_URL, includingLiteLLM_CredentialsTableandLiteLLM_ProxyModelTable
That last item matters for AI teams. A compromised proxy host can leak credentials for many upstream model providers at once.
Collected data is serialized to JSON and encrypted with a random AES-256-CBC key, which is wrapped with an embedded RSA-4096 public key. It is then sent over plain HTTP to www.litellm[.]dev/healthcheck. The official project uses litellm.ai, so the .dev domain is a look-alike. JFrog's registration records show it was created on October 13, 2026.
The loader
The sitecustomize.py loader opens /tmp/remote_code_loader.lock, forks, detaches and loops hourly. Each cycle it reads a pointer file hosted on GitHub raw content. If that file names a domain, the loader fetches /codepy from it and executes the response with no integrity check.
JFrog observed that pointer file to be empty at the time of its check, which left the loader dormant. It stressed that this does not disable the stealer, and that the loader can be re-armed by editing the file. Treat that as a point-in-time observation, not an all-clear.
How the attacker got in
LiteLLM's own post-mortem describes a chain that defeated several controls. Its account is still labeled an investigation in progress, and some pieces are unconfirmed:
- Release permissions had been temporarily widened for two engineers to publish bugfix backports.
- The attacker held three sets of developer GitHub CLI credentials. One belonged to a GitHub organization owner and was used to disable branch protections and force-push a workflow change.
- The modified workflow was pointed at a look-alike repository,
Berri-AI/litellminstead ofBerriAI/litellm, which built and published the poisoned artifacts. - Credentials from a third developer account were then used to delete workflow records, limiting the logs available.
LiteLLM says it suspects the tokens leaked through logs of its own internal LiteLLM deployment, which was exposed to the internet. It says it observed specific situations where Claude Code embeds a user's GitHub CLI OAuth token in a response body, which can end up in logs when response bodies are logged. The project has not yet confirmed how the attacker reached that deployment. It suspects a recently patched vulnerability.
Mend's commit analysis fits the look-alike-repo story. The tagged commit already declared litellm as 1.108.0, and the attacker's commit set it back to 1.107.0. JFrog is more cautious: the artifact evidence alone does not show which CI system, machine, account or credential was compromised. Both views can hold. The project's account is an incident finding, and the artifact analysis simply doesn't prove the route on its own.
Is this the March incident again?
No, and the sources say to keep them apart. Chainguard calls this the second compromise of LiteLLM in seven months. The March incident involved versions 1.82.7 and 1.82.8 and was linked to a malicious Trivy release in the CI pipeline. JFrog says the new compromise uses different payloads and infrastructure, and that no evidence links it to TeamPCP or the earlier access path. Scope each campaign with its own indicators.
LiteLLM says it hardened its pipeline after March with a private release repo, Trusted GitHub Publishers, immutable Docker tags and signed images. It says the attacker still got through by chaining several stolen credentials.
What to do
Work out if you're exposed
- Run
pip show litellmto check for 1.107.0. Treat any environment that started Python with that version as compromised. - Treat any container that ran the affected image digest as compromised.
- Search the filesystem for
litellm-1.107.0.dist-info,cook.pthandsitecustomize.py. Finding one confirms exposure. Not finding one proves nothing. - If 1.107.0 ever sat on a host with outbound internet access, assume exfiltration succeeded. A marker file at
/tmp/.pc_once_72129118c59f5636c0bbconfirms it. Its absence proves nothing, because container and temp cleanup remove it.
Clean up without re-triggering the malware
- Don't start with
pip install. Any Python start in the affected environment reruns the hooks. - Isolate the host or container, and preserve logs and evidence before changing anything.
- Using tools that don't invoke the affected interpreter, delete
cook.pth,cook.py,sitecustomize.pyand the matching__pycache__files from every affectedsite-packages. - Kill the detached Python process holding
/tmp/remote_code_loader.lock. - Reinstall pinned to
litellm==1.106.2. Rebuilding virtual environments and images from scratch is better still.
Rotate everything the host could reach
LiteLLM and JFrog call out:
- Instance or task IAM roles
- Every provider key stored in the LiteLLM proxy database, plus the master and virtual keys
- The
DATABASE_URLpassword - Any secret in the environment of any process on the host
- Browser-saved passwords, SSH/GPG/TLS keys, package-publishing and source-control tokens, cloud and Kubernetes credentials, and AI tool tokens
On AWS, LiteLLM suggests checking CloudTrail for GetCallerIdentity calls from the instance role around install time. Then look for use of the role's credentials from outside your infrastructure. JFrog also advises blocking and investigating access to www.litellm[.]dev, the GitHub-hosted pointer, and any domain it returns. Because the remote stage can run arbitrary Python, treat any /codepy retrieval as serious.
Practical takeaways
- Pin versions and digests. Floating tags turned a two-hour window into a surprise delivery mechanism.
- Verify the artifact, not just the source. The Git tag was clean while the wheels were not. Inspecting a wheel's
RECORDcontents for startup hooks like.pthfiles is a check the JFrog analysis explicitly recommends. - Watch your logs. If response bodies are logged, tokens can hide there. That is LiteLLM's own finding, and it is worth auditing in any AI gateway setup.
- Rebuild rather than repair. With a self-retriggering payload and a possible remote-code stage, "I think I got it all" is not a recovery plan.
Security vendors including Socket, Chainguard and Wiz flagged the release within minutes. Chainguard says its scanning flagged it 16 minutes after the first wheel reached PyPI. Fast detection helped here, but the exposure windows were still long enough for builds and deployments to pull the package. LiteLLM said it would update its incident post every two hours, so check it for new indicators before you close out your own investigation.
Update: Microsoft adds Defender detections and Windows execution evidence (October 23, 2026)
Microsoft’s analysis independently confirms that ordinary Python activity on Windows can activate the credential collector: in one observed case, installing an unrelated dependency triggered it. That reinforces the warning not to run python, pip, or related commands inside a suspect environment—even for triage.
Microsoft says Defender protections were available as of October 22, including signatures named Trojan:PyPI/Stealer.SA, Trojan:PyPI/LiteLlmSupplyChain.A, .B, and Backdoor:PyPI/LiteLlmSupplyChain.C. Defender for Endpoint also has alerts for suspicious scripts and network connections, while Defender Vulnerability Management identifies potentially exposed devices as TVM-2026-0003.
The company also expands scoping advice beyond installed environments: check pip and uv caches, internal mirrors, lock files, build outputs, and container images. Its telemetry observed Windows Python processes launching discovery commands including systeminfo, ipconfig, route, arp, and netstat; those are investigation leads, not proof of compromise on their own.
Update: LiteLLM traces attack to an exposed master key (October 26, 2026)
LiteLLM’s updated postmortem traces the attacker’s initial access to a master key committed to a public repository on May 27, according to The Register. Developers removed the exposed commit but did not revoke the key.
The Register reports that the attacker used the key on October 10 to access LiteLLM’s Agent Platform and extract a GitHub CLI OAuth token with owner-level repository access. This provides a more specific explanation than LiteLLM’s earlier suspicion that the attacker reached the deployment through a recently patched vulnerability.
According to the report, the attacker used that access to plant malicious workflows in internal repositories and steal additional tokens, including GitHub CLI tokens belonging to two engineers with maintainer privileges. LiteLLM says it is revoking tokens across its internal systems and vendor platforms.
References
- Inside the LiteLLM 1.107.0 supply chain compromise: Credential theft through Python startup hooks Microsoft Security Blog · 2026-10-23T18:12:19+00:00
- LiteLLM supply chain attack started with an exposed master key The Register · 2026-10-26T20:30:53+00:00
- LiteLLM release pipeline hacked to distribute info-stealer malware BleepingComputer · 2026-10-22T15:12:51-04:00