
LiteLLM PyPI Package and Docker Images Compromised to Steal Cloud and AI Credentials
LiteLLM 1.107.0 on PyPI and GHCR images shipped Python startup hooks (cook.pth, sitecustomize.py) that steal cloud, SSH, browser and AI-tool credentials, including LiteLLM proxy database credentials. Image build provenance recorded a lookalike Berri-AI/litellm source repository. The post decodes the payload, encrypted exfiltration and GitHub-driven remote loader, and lists containment steps, MITRE ATT&CK mappings and IOCs.
What it is
LiteLLM 1.107.0, published to PyPI on 21 October 2026 (11:39:51 UTC), carries a credential stealer and a remote Python loader. The same payload is reported in the ghcr.io/berriai/litellm image. Socket's scanner flagged the seven wheels about seven minutes after publication. LiteLLM gets about 23M weekly downloads.
How it works
The wheels install cook.pth, cook.py and sitecustomize.py into the top-level library directory. cook.pth contains only import cook, so any Python startup runs the collector, even if nothing imports LiteLLM. Running Python or pip to inspect or repair the install can trigger it again. Reading the wheel as a ZIP does not.
The collector (privcollect, 16 modules) targets:
- cloud, Kubernetes, Docker, Vault, Terraform and CI credentials;
- SSH, GPG and TLS keys,
.env,.netrc, npm and PyPI config; - AI tool files:
~/.codex/auth.json,~/.claude/.credentials.json,~/.gemini/oauth_creds.json,~/.cursor/mcp.json; - browser databases and process environments;
- cloud metadata endpoints.
One module reads LiteLLM_CredentialsTable and LiteLLM_ProxyModelTable when DATABASE_URL is set.
Results are encrypted (AES-256-CBC, key wrapped with RSA-4096) and POSTed to hxxp://www[.]litellm[.]dev/healthcheck with header X-Token: XWiA8gZNa7NcX2XRoAEb. A separate loader in sitecustomize.py forks a background process that polls domain.txt in the GitHub repo letters9813/pythonclient-test. It then downloads Python from http://<domain>/codepy and execs it without any signature check. At 13:01:44 UTC the pointer file was empty, so the loader could not proceed, but the collector is independent of it.
The container build provenance records source owner Berri-AI (lookalike, one hyphen) instead of BerriAI. The source revision is 20535d8fa11f786a6c24df7488a0e449ed5f09ad, and the workflow run is BerriAI/project-releaser 39587059944. A commit adding data = "cook" to the Maturin config is what packaged the payload. The tagged release's pyproject.toml lacks that line, so reviewing the tag alone misses it.
Preconditions / Affected
litellm==1.107.0, all seven wheels. The sdist build config is also modified but lacks the payload files.- Image tags
v1.107.0,latest,main-latestandmain-stableon ghcr.io/berriai/litellm all resolved to one index (sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e) at about 13:14 UTC. Floating-tag deployments were exposed. - Upwind also reports
litellm-database,litellm-non_rootandlitellm/litellmon Docker Hub. Socket did not extract those independently. - Exposure requires Python to have run after install, including proxy containers and CI runners.
What to do
- Block 1.107.0. Check lockfiles, caches, build logs, layers and running digests. Pin digests, not tags.
- Isolate affected hosts and preserve evidence. Do not start the environment's Python or pip first. Rebuild from a reviewed artifact.
- Revoke and rotate everything the process could read: cloud sessions, provider keys, AI-tool sessions, registry tokens, SSH keys, database credentials. Do this from a clean machine. Removing the package neither reverses exposure nor stops a running loader.
- Upwind recommends 1.106.2. Socket did not certify earlier versions as clean.
- Hunt for the payload hashes, the lock file
/tmp/remote_code_loader.lockand the marker/tmp/.pc_once_72129118c59f5636c0bb. Search egress for/healthcheck, the GitHub pointer path and/codepy. Scope GitHub blocks to that path. - Maintainers: audit run 39587059944 and the
source_owneroverride. Allow publishing builds only from approved repository identities and commits.
Key hashes: cook.pth 7f191632e7320705d154b85204f1ae546b543acbd7e8861229b76f5042b92bdb; cook.py 9dd0a3926f96a37eb86cf1ee2310d9be93139cc4ebef3ef9e2efe9531390be25; sitecustomize.py 34bef04fe88ac9d0aae0475630b1738681d15b94f233ac8aa46eacf5968305fa.
Caveats
- A missing marker file does not clear a host. Failed uploads, temp cleanup or container replacement also leave none.
- Socket does not establish how the source-owner override was authorized, whether an account or token was compromised, or whether the same workflow uploaded the PyPI wheels. The run URL returned 404.
- Provenance shows recorded build inputs, not trustworthy ones.
- The macOS artifact cannot recover plaintext Chromium passwords and its database module fails, but it still copies browser databases and AI-tool files.
- The page does not show that any particular proxy database was accessed.
Organisations named here
- BerriAI
- T1195.001
- T1555.003
Technologies named here
- LiteLLM 1.107.0 on PyPI
- Socket AI scanner
- Upwind public investigation
- LiteLLM 1.107.0 modified source distribution
- maturin
- LiteLLM proxy deployments with DATABASE_URL configured
- AWS STS GetCallerIdentity
- Prisma
- LiteLLM
- PyPI
- Python
- Docker
- GitHub
- Socket
- Upwind
Techniques named here
- T1546.018 - Python Startup Hooks
- T1027.013 - Encrypted/Encoded File
- T1140 Deobfuscate/Decode Files or Information
- T1552.001 Credentials In Files
- T1552.004 Private Keys
- T1552.005 - Cloud Instance Metadata API
- T1555.003 - Credentials from Web Browsers
- T1105 Ingress Tool Transfer
- Detached background loader
Threat actors named here
- privcollect
- cook.pth
Also named here
- sitecustomize.py
- T1546.018
- T1027.013
- T1140
- T1552.001
- T1552.004
- T1552.005
- T1105
- MITRE ATT&CK
Related in Supply Chain


