At 11:39 UTC on October 21, a malicious version (1.107.0) of the LiteLLM package was uploaded to PyPI. This version was quarantined on PyPI as of 13:20 UTC.
LiteLLM Docker containers on GHCR and Docker hub also included the trojanized version of LiteLLM. These included the v1.107.0 , latest, main-latest and main-stable tags. These were removed ~13:53 UTC.
The most recent safe version is v1.106.2.
When the malicious package is installed, three files are extracted directly into the site-packages root: cook.pth, cook.py and sitecustomize.py. These packages are designed to be executed whenever a python interpreter is started.
Per LiteLLM developer BerriAI:
malicious actions were taken through three engineers’ GitHub accounts, appearing to be by way of stolen GitHub CLI OAuth tokens. The threat actor used one user’s account triggered the project-releaser run, and another user’s account to approve it. All tokens which were involved in this attack have been revoked.
And in a followup:
GitHub CLI OAuth token exposure
After a thorough investigation, we have observed specific situations in which Claude Code will embed a user’s GitHub CLI OAuth token into a response body, and in situations where response bodies are logged, these tokens may appear in LiteLLM logs.
Access to our internal LiteLLM deployment
We identified an unexpected, innocuously named user account in our internal LiteLLM deployment, which was exposed to the public internet. We believe the attacker gained access to this deployment, obtained the GitHub CLI OAuth tokens from its logs, and subsequently used those credentials to compromise our release workflow.
We have not yet confirmed how the attacker gained access to our internal deployment, but currently suspect it was through a recently patched vulnerability.
UPDATES
14:05 UTC – added IOC table
14:15 UTC – added Attack Commits details
14:40 UTC – added Credential Stealer details, noted Docker takedown
15:20 UTC – clarified dual-technique approach novelty and LLM-aided development
16:40 UTC – added diagram of payload flow
16:55 UTC – added LiteLLM statement on root cause
18:35 UTC – LiteLLM has released a blog post
19:05 UTC – added observed post-exploitation activity
21:45 UTC – updated LiteLLM details on root cause
Payload
The payload has strong indications of LLM-aided development, including verbose variable names (encrypted_private_key_output), overly defensive error handling with generic try/except blocks, platform-specific code paths that fail silently on untested platforms (Windows marker path /tmp/ doesn’t exist, fcntl/fork calls crash the remote loader), and a dead code module (exec_py) that was defined but never called. The structure suggests iterative prompting: functional on the happy path but brittle at the edges and untested.
Upon startup, the interpreter processes cook.pth, which includes a command to import cook, which loads cook.py. cook.py deobfuscates itself, runs a stealer routine and writes a maker to /tmp/.pc_once_72129118c59f5636c0bb (note this fails on Windows, as the /tmp/ path often does not exist). The stealer routine gathers system, browser, developer, cloud and AI tool secrets, encrypts them and exfiltrates to www.litellm.dev/healthcheck via a post request. This domain is attacker controlled, impersonating LiteLLM infrastructure.
Python interpreters will also attempt to execute sitecustomize.py – however, it will only run on Linux & MacOS machines. This script attempts to fetch domain.txt from a GitHub dead drop (letters9813/pythonclient-test/main/domain.txt). If it receives a response, it will attempt to download a second stage python script from http:// and run it. As of the time of analysis, the domain.txt file was never populated beyond initial test values. The repo has been taken down by GitHub as of 1:30 PM UTC.
The dual-technique approach, using both .pth files and sitecustomize.py simultaneously, appears to be the first documented case in the wild.
Credential Stealer
The embedded credential stealer, internally named privcollect, harvests sensitive data across 16 modules targeting browsers, cloud providers, and AI services. It extracts:
browser credentials and cookies from Chrome, Firefox, Edge, and Safari
AWS credentials from
~/.aws/, environment variables, and EC2/ECS instance metadata (IMDS)GCP service account keys and application default credentials
Azure tokens from the CLI cache
In terms of AI-specific theft, it targets API keys for OpenAI, Anthropic, Cohere, and other LLM providers stored in environment variables or config files. It also collects SSH private keys, GPG keys, and shell history files.
LiteLLM Proxy stores upstream API keys in a PostgreSQL database. The stealer queries this database directly (SELECT * FROM "LiteLLM_CredentialsTable" LIMIT 500) to extract all configured provider credentials.
Harvested data is encrypted with AES-256-CBC (key wrapped with RSA-4096) and exfiltrated over HTTP to www.litellm[.]dev/healthcheck using the hardcoded X-Token header and an Internet Explorer 7 user-agent string.
Attacker Commits
The payload originates with malicious commits made on github.com/Berri-AI/litellm. Note, this is a hyphenated variant of the legitimate BerriAI organization. This repository, not visible on GitHub, was used to inject the payload into the release pipeline.
Between 08:56 and 10:46 UTC on October 21, the attacker pushed 10 commits using the author email ckdi82fkoos@proton.me. The first nine commits were created on GitHub.com and therefore signed with GitHub’s verified signature.
The first three (within 2 minutes) added the persistence files: cook.pth, cook.py, and sitecustomize.py. Four more commits bumped version numbers to 1.107.0.
After a 39-minute gap, two commits refined the payload and added the critical data = "cook" directive to pyproject.toml, which instructed the build tool to bundle the malware into the wheel.
A final commit, made 58 minutes later with a different email (Berri-AI@users.noreply.github.com) and unsigned (suggesting a different environment), added COPY cook ./cook to the Dockerfiles.
Although the Berri-AI repository is unavailable, all commits remain accessible through the parent repository due to Git’s fork object sharing. The full diff is available at: github.com/BerriAI/litellm/compare/a8113b0d…20535d8f
Post-Exploitation Activity
Wiz Research has observed abuse of stolen credentials, starting approximately 4.5 hours post-exfiltration. Between 16:20 and 17:01 UTC, the attacker validated AWS instance role credentials harvested from compromised CI runners and Kubernetes nodes across multiple environments, from the following IP addresses:
168.100.8[.]57165.227.201[.]8
Each credential was checked with sts:GetCallerIdentity, followed by bedrock:ListFoundationModels and a ListModels call, consistent with LLMjacking.
In a small number of environments, the attacker also enumerated IAM (ListRoles, ListUsers, ListAccessKeys) and performed general discovery (ListAccounts, DescribeTrails, etc.).
Recommendations
Wiz customers should review our Threat Intel Center advisory: Supply Chain Attack Targeting LiteLLM .
| Indicator | Description | Type |
|---|---|---|
| litellm[.]dev | Attacker-controlled exfil (lookalike) | Network |
| www.litellm[.]dev/healthcheck | Exfiltration endpoint | Network |
| http://{domain}/codepy | Dynamic second-stage RCE payload (never observed active) | Network |
| X-Token: XWiA8gZNa7NcX2XRoAEb | Hardcoded exfil auth header | Network |
| /tmp/.pc_once_72129118c59f5636c0bb | Run-once marker file | Host |
| PRIVCOLLECT_ACTIVE=1 | Environment variable indicator | Host |
| 84dc8c82c0a4a4a62552ef826b49fcc86d1a5c4a5d0f2c06665723f93d129d7d | cook.pth persistence hook – Windows | Hash |
| 7f191632e7320705d154b85204f1ae546b543acbd7e8861229b76f5042b92bdb | cook.pth persistence hook – Linux/MacOS | Hash |
| 03ac2a8b752605d1d7710979c17ff18b03dd978203ba23ba97fd8ce3d6f897ea | cook.py credential stealer payload (22,252 bytes) – Windows | Hash |
| 9dd0a3926f96a37eb86cf1ee2310d9be93139cc4ebef3ef9e2efe9531390be25 | cook.py credential stealer payload (22,252 bytes) – Linux/MacOS | Hash |
| b6122440d0d9cb1eb1ade9933f49214da8c31288de7710c076c2680dfa528465 | sitecustomize.py loader with hourly backdoor loop (25,509 bytes) – Windows | Hash |
| 34bef04fe88ac9d0aae0475630b1738681d15b94f233ac8aa46eacf5968305fa | sitecustomize.py loader with hourly backdoor loop (25,509 bytes) – Linux/MacOS | Hash |
Affected Instances
| File/Image | SHA256 |
|---|---|
| litellm-1.107.0-cp310-abi3-macosx_10_12_x86_64.whl | 7598727f209d717041db37f7dca271d3a7d328a279890e0e1d55375729aeb86d |
| litellm-1.107.0-cp310-abi3-macosx_11_0_arm64.whl | 0aaf7ba346d41ef2f40390436738b001180d5a1c03aa093f121078a9969d9a1a |
| litellm-1.107.0-cp310-abi3-manylinux_2_28_aarch64.whl | 668169d35b87bf51324080390b1b1c5ab12bd335cfe5f1a99e3689abd5e02ae6 |
| litellm-1.107.0-cp310-abi3-manylinux_2_28_x86_64.whl | 462d0c9f67ca522d9e0e4acd245e90b36ce0d5353604a66bb7c43a1ec4a6fcf7 |
| litellm-1.107.0-cp310-abi3-musllinux_1_2_aarch64.whl | c2e32d0ce2548aa662c18bf5001911a79fe5b30cf8357132275add02fdcde054 |
| litellm-1.107.0-cp310-abi3-musllinux_1_2_x86_64.whl | 1bbce8caa94f01c0aaa0b7ddae51553a8a32e1ce53d96d44fe583272aff7ad00 |
| litellm-1.107.0-cp310-abi3-win_amd64.whl | 12d4ccc5c6f8d35cf4f2cf233b7f3e418e863e657d81ff5f5fc9e210005b9484 |
| ghcr.io/berriai/litellm:v1.107.0 linux/amd64 | bd1aeee419ea36da9aa2b657288da4101887be468f020ac63d22ba6ed84a3ea5 |
| ghcr.io/berriai/litellm:v1.107.0 linux/arm64 | 56b74573eed6e4aafe4b50603dbe7853e0345f188c8e44a09f1098caf1f4bcdf |
| ghcr.io/berriai/litellm-database:v1.107.0 linux/amd64 | 24581ddc30767f16ecb69d2b0517556f2cd7dea56971adb42a3d1beb97d69523 |
| ghcr.io/berriai/litellm-database:v1.107.0 linux/arm64 | 827dd893c74cf621a860a66a3a447a5c732533f087589ce905573e285c0a239c |
| ghcr.io/berriai/litellm-non_root:v1.107.0 linux/amd64 | 645a67d810628252d63df85dcc8824c58bcc77cf8383f4d3e6321f0ccc3ce9e4 |
| ghcr.io/berriai/litellm-non_root:v1.107.0 linux/arm64 | f75c8edae59b72b1ebfd617295833f465c85bb2fab9c1d60f21b496db86cb8da |
| docker.io/litellm/litellm linux/amd64 | bd1aeee419ea36da9aa2b657288da4101887be468f020ac63d22ba6ed84a3ea5 |
| docker.io/litellm/litellm linux/arm64 | 56b74573eed6e4aafe4b50603dbe7853e0345f188c8e44a09f1098caf1f4bcdf |

