PyPI

The LiteLLM 1.107.0 Supply Chain Attack

Updated 24 Oct 2026 · Incident date 21 Oct 2026

Version 1.107.0 of the litellm Python package carried a credential stealer and a remote-access trojan. It reached PyPI and the official LiteLLM Docker images on 21 October 2026. LiteLLM reports that the bad files were live for about two hours.

LiteLLM confirms the attack in its incident post. Aikido, Wiz and StepSecurity describe the release. PyPI quarantined it, and LiteLLM removed or repointed the Docker tags. LiteLLM tells users to pin 1.106.2. The malware starts with every Python process in the environment, even one that never imports LiteLLM.

What happened

A poisoned LiteLLM release went out on 21 October 2026. LiteLLM reports that the seven 1.107.0 wheels reached PyPI between 11:39:51 and 11:40:13 UTC. The Docker images followed at 11:46:56 UTC. Wiz counts about 49,000 downloads of the bad version, from ClickPy data.

LiteLLM published the full attack chain on 23 October 2026. In May 2026, the master key for an internal service, the LiteLLM Agent Platform, went into a public repository and stayed valid. On 10 October, the attacker used that key to get the GitHub token of an organization owner. CI workflows pushed with that token then sent out the tokens of two more maintainers. On 21 October, the three tokens let the attacker point the release at a lookalike repository, Berri-AI/litellm. The same tokens approved the release and deleted the run records.

LiteLLM's first post said that the tokens came from the logs of an internal deployment. Wiz and StepSecurity repeat that account, and the attack-chain post corrects it. The official v1.107.0 tag on GitHub does not hold the malicious files. StepSecurity notes that the bad artifacts carried valid provenance, because BerriAI's own pipeline built and signed them.

Aikido reports that a data = "cook" line under [tool.maturin] in pyproject.toml put a cook folder into each wheel. The wheels put three files at the root of site-packages: cook.pth, cook.py and sitecustomize.py. Aikido describes cook.pth as a 12-byte file that holds only import cook. Python reads .pth files at every start, so cook.py runs every time Python starts.

The file cook.py is the infostealer. Aikido reports that it decompresses and runs a 61,000-byte payload. Wiz reports that it writes a marker file, /tmp/.pc_once_72129118c59f5636c0bb, and that this write fails on Windows. Aikido reports that the marker stops the stealer from running more than once per machine. StepSecurity adds that on Windows the stealer still runs, and likely runs again at every Python start.

Aikido lists what the stealer collects:

  • System details, saved browser passwords and history, SSH private keys and shell history.
  • AWS, Azure and GCP credentials, from files and from the live instance metadata endpoints.
  • Docker, Docker Swarm, Kubernetes, GitLab runner, Jenkins and HashiCorp Vault secrets.
  • Config files for AI developer tools such as Claude Code, Cursor, GitHub Copilot and Codex CLI.
  • Git, npm, PyPI and pip credentials, plus GPG and TLS private keys.
  • Environment variables. On Linux, this includes every other running process.
  • All rows of LiteLLM_CredentialsTable, which holds the stored provider API keys, and LiteLLM_ProxyModelTable.

Wiz reports that the stealer encrypts the data with AES-256-CBC and wraps the key with RSA-4096. It sends the data by HTTP POST to http://www[.]litellm[.]dev/healthcheck, a lookalike domain that Wiz reports is attacker controlled.

The file sitecustomize.py is the remote-access trojan. Aikido reports that it starts a background daemon with a lock at /tmp/remote_code_loader.lock. Every hour, the daemon reads a C2 domain from domain.txt in the GitHub repository letters9813/pythonclient-test. It then runs a Python script from http://<domain>/codepy with the full privileges of the process.

Wiz reports that the trojan runs only on Linux and macOS, and that domain.txt never held more than test values. StepSecurity lists those values as localhost:18909 and test.test.com:18909. GitHub took the repository down on 21 October.

Wiz saw stolen AWS instance role credentials in use between 16:20 and 17:01 UTC that day. The calls included sts:GetCallerIdentity, bedrock:ListFoundationModels and, in a few environments, IAM listing calls.

An earlier LiteLLM compromise, in March 2026, has its own page. None of the sources names a group behind 1.107.0.

Affected versions

Only litellm 1.107.0 is malicious, on PyPI and in the official Docker images.

  • litellm 1.107.0 on PyPI: all seven compiled wheels carry the payload, for macOS, Linux and Windows. StepSecurity reports that the source tarball was altered but holds no payload.
  • LiteLLM gives the PyPI window as 11:39:51 to 13:23 UTC on 21 October 2026. Wiz dates the quarantine to 13:20 UTC. Aikido gives the install window as 11:39 to 12:47 UTC.
  • ghcr.io/berriai/litellm, litellm-database and litellm-non_root at the tags v1.107.0, latest, main-latest and main-stable. LiteLLM reports that these tags were unavailable by 13:51 UTC.
  • docker.io/litellm/litellm at the same four tags, from 11:46:56 to 14:20 UTC. By 14:20 UTC, LiteLLM removed v1.107.0 and pointed the other tags at clean images. Aikido names the Docker Hub tag as 1.107.0.
  • Not affected, per StepSecurity: 1.107.0rc1, 1.107.0rc2 and 1.103.x through 1.106.x.

Indicators of compromise

Aikido, Wiz, StepSecurity and LiteLLM publish these indicators. Network addresses are defanged.

  • Files at the root of site-packages: cook.pth, cook.py, sitecustomize.py, plus matching __pycache__/cook*.pyc and sitecustomize*.pyc
  • Stealer marker: /tmp/.pc_once_72129118c59f5636c0bb. Aikido states that any /tmp/.pc_once_* file means the stealer ran. LiteLLM states that a missing marker proves nothing.
  • Temporary folders .pc_sq_* and .pc_e_*, per StepSecurity
  • Environment variable: PRIVCOLLECT_ACTIVE=1
  • Trojan lock: /tmp/remote_code_loader.lock. Aikido states that this file means the C2 daemon ran or still runs.
  • Exfiltration endpoint: http://www[.]litellm[.]dev/healthcheck
  • C2 domain lookup: https://raw[.]githubusercontent[.]com/letters9813/pythonclient-test/main/domain[.]txt
  • Second stage: http://<domain>/codepy
  • HTTP header: X-Token: XWiA8gZNa7NcX2XRoAEb
  • User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0)
  • Source of the AWS checks that Wiz saw: 168.100.8[.]57 and 165.227.201[.]8
  • Lookalike source repository: github[.]com/Berri-AI/litellm, commit author ckdi82fkoos@proton[.]me

SHA256 hashes of the three injected files, from Wiz and StepSecurity:

FilePlatformSHA256
cook.pthLinux, macOS7f191632e7320705d154b85204f1ae546b543acbd7e8861229b76f5042b92bdb
cook.pthWindows84dc8c82c0a4a4a62552ef826b49fcc86d1a5c4a5d0f2c06665723f93d129d7d
cook.pyLinux, macOS9dd0a3926f96a37eb86cf1ee2310d9be93139cc4ebef3ef9e2efe9531390be25
cook.pyWindows03ac2a8b752605d1d7710979c17ff18b03dd978203ba23ba97fd8ce3d6f897ea
sitecustomize.pyLinux, macOS34bef04fe88ac9d0aae0475630b1738681d15b94f233ac8aa46eacf5968305fa
sitecustomize.pyWindowsb6122440d0d9cb1eb1ade9933f49214da8c31288de7710c076c2680dfa528465

SHA256 hashes of the seven 1.107.0 wheels, from Aikido, Wiz and StepSecurity:

WheelSHA256
litellm-1.107.0-cp310-abi3-macosx_10_12_x86_64.whl7598727f209d717041db37f7dca271d3a7d328a279890e0e1d55375729aeb86d
litellm-1.107.0-cp310-abi3-macosx_11_0_arm64.whl0aaf7ba346d41ef2f40390436738b001180d5a1c03aa093f121078a9969d9a1a
litellm-1.107.0-cp310-abi3-manylinux_2_28_x86_64.whl462d0c9f67ca522d9e0e4acd245e90b36ce0d5353604a66bb7c43a1ec4a6fcf7
litellm-1.107.0-cp310-abi3-manylinux_2_28_aarch64.whl668169d35b87bf51324080390b1b1c5ab12bd335cfe5f1a99e3689abd5e02ae6
litellm-1.107.0-cp310-abi3-musllinux_1_2_x86_64.whl1bbce8caa94f01c0aaa0b7ddae51553a8a32e1ce53d96d44fe583272aff7ad00
litellm-1.107.0-cp310-abi3-musllinux_1_2_aarch64.whlc2e32d0ce2548aa662c18bf5001911a79fe5b30cf8357132275add02fdcde054
litellm-1.107.0-cp310-abi3-win_amd64.whl12d4ccc5c6f8d35cf4f2cf233b7f3e418e863e657d81ff5f5fc9e210005b9484

Docker image digests. LiteLLM and StepSecurity give the multi-platform index. Wiz gives the per-platform digests, and reports that docker.io/litellm/litellm carries the same digests as ghcr.io/berriai/litellm:v1.107.0.

ImagePlatformDigest
ghcr.io/berriai/litellm:v1.107.0indexsha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e
ghcr.io/berriai/litellm:v1.107.0linux/amd64sha256:bd1aeee419ea36da9aa2b657288da4101887be468f020ac63d22ba6ed84a3ea5
ghcr.io/berriai/litellm:v1.107.0linux/arm64sha256:56b74573eed6e4aafe4b50603dbe7853e0345f188c8e44a09f1098caf1f4bcdf
ghcr.io/berriai/litellm-database:v1.107.0linux/amd64sha256:24581ddc30767f16ecb69d2b0517556f2cd7dea56971adb42a3d1beb97d69523
ghcr.io/berriai/litellm-database:v1.107.0linux/arm64sha256:827dd893c74cf621a860a66a3a447a5c732533f087589ce905573e285c0a239c
ghcr.io/berriai/litellm-non_root:v1.107.0linux/amd64sha256:645a67d810628252d63df85dcc8824c58bcc77cf8383f4d3e6321f0ccc3ce9e4
ghcr.io/berriai/litellm-non_root:v1.107.0linux/arm64sha256:f75c8edae59b72b1ebfd617295833f465c85bb2fab9c1d60f21b496db86cb8da

How to check

Use commands that only list files. Python runs cook.py at every start, and pip is a Python program. Do not run pip show in an environment that can hold 1.107.0.

On Linux or macOS, check for the stealer marker and the trojan lock. The second command shows any process that still holds the lock:

ls -la /tmp/.pc_once_* /tmp/remote_code_loader.lock
lsof /tmp/remote_code_loader.lock

A missing marker file does not prove that the machine is clean. Search every Python environment for the bad version and the injected files:

find / -type d -name "litellm-1.107.0.dist-info" 2>/dev/null
find / -path "*site-packages*" \( -name cook.pth -o -name cook.py \) 2>/dev/null

On Windows, run this in PowerShell:

Get-ChildItem -Path C:\ -Recurse -Force -Include cook.pth,cook.py,litellm-1.107.0.dist-info -ErrorAction SilentlyContinue

If you keep wheels in a cache, mirror or artifact store, hash them and compare the result with the wheel table:

find / -name "litellm-1.107.0-*.whl" -exec shasum -a 256 {} \; 2>/dev/null

For Docker, list the LiteLLM images on the host with their digests:

docker images --digests | grep -i litellm

Any image tagged v1.107.0 or 1.107.0 is affected. Compare every other LiteLLM digest with the Docker table. A local listing can show the index digest or a per-platform digest.

Search proxy, DNS and firewall logs for litellm[.]dev, letters9813/pythonclient-test and any path that ends in /codepy. StepSecurity states that a /codepy request means attacker code ran on that host. In AWS CloudTrail, look for GetCallerIdentity calls from instance or task roles near the install time, and for calls from the two Wiz addresses.

What to do now

  1. If any check finds 1.107.0, the cook files or the /tmp markers, treat the machine as compromised. If 1.107.0 ran on a host with internet access, LiteLLM tells users to assume that data left the host.
  2. Do not run pip in an affected environment. LiteLLM states that each Python start runs the malicious files again.
  3. Stop the Python daemon that holds /tmp/remote_code_loader.lock, the LiteLLM processes and any containers that run the affected images.
  4. Delete cook.pth, cook.py, the injected sitecustomize.py and the matching __pycache__ files from every affected site-packages folder. Some environments hold their own sitecustomize.py, so read that file before you delete it.
  5. Rebuild virtual environments and images from scratch with litellm==1.106.2, the version that LiteLLM names. Pin Docker images by digest, and purge cached v1.107.0 images from hosts and mirrors.
  6. Rotate every secret that the host had access to. On a LiteLLM proxy host, start with the provider keys, the master key, the salt key, the virtual keys and the DATABASE_URL password.
  7. Then rotate cloud keys, instance roles, SSH and GPG keys, and Kubernetes and Vault tokens. Also rotate CI/CD secrets, Git and registry tokens, AI tool tokens and saved browser passwords.
  8. Block litellm[.]dev, and alert on requests to raw[.]githubusercontent[.]com/letters9813/.

What Vigilance would show (rebuilt from public reports)

Vigilance compares the version you trust with the new one and names the file that gained a new power. The block below is rebuilt from the public reports in the words Vigilance prints. It is not a captured scan, because the malicious release is not redistributed.

vigi diff --old litellm-1.106.2 --new litellm-1.107.0
files scanned: 67

HEADS UP  1 file can now do things the old version could not. The rest changed and gained nothing.

CHANGED    cook.py
           It now downloads from the internet, reads saved passwords and access keys and runs other programs. It did not before.

Frequently asked questions

Is LiteLLM safe to use now?

Version 1.107.0 is malicious. PyPI quarantined it on 21 October 2026, and LiteLLM cleaned the bad Docker tags the same day. LiteLLM tells users to pin 1.106.2. Pin Docker images by digest.

What does the LiteLLM 1.107.0 malware steal?

Aikido reports that it takes browser passwords, SSH keys, cloud credentials, Docker and Kubernetes secrets, CI/CD secrets, AI tool configs and environment variables. It also reads the API keys stored in the LiteLLM database.

Was the LiteLLM Docker image compromised?

Yes. The official images on GHCR and Docker Hub carried the bad build at v1.107.0, latest, main-latest and main-stable. LiteLLM reports that the tags pointed at it from 11:46:56 UTC to 14:20 UTC at the latest on 21 October 2026.

How did attackers publish LiteLLM 1.107.0?

LiteLLM reports that the attacker used the GitHub tokens of three maintainers to build the release from a lookalike repository, Berri-AI/litellm.

Is uninstalling LiteLLM 1.107.0 enough?

No. Aikido reports that the stealer sends data out on its first run. The trojan also lets the attacker run more code. Rotate every exposed secret, and rebuild the environment or container from scratch.

Sources

  1. docs.litellm.ai/blog/security-incident-oct-21-2026
  2. docs.litellm.ai/blog/security-incident-oct-21-2026-attack-chain
  3. aikido.dev/blog/litellm-compromised-infostealer-rat
  4. wiz.io/blog/litellm-compromised-steals-credentials
  5. stepsecurity.io/blog/litellm-1-107-0-compromised-pypi-backdoored-wheels-credential-stealer

More supply chain attacks

All 122 attacks in the library · What is a supply chain attack? · How to prevent supply chain attacks

Check the next update before you install it

Vigilance compares the version you trust with the new one. It names the one file that gained a power the old version did not have.

Start the free 14-day watchBrowse the attack library