LiteLLM's Git tag is clean. The wheel still runs at Python start.
Vendor RCA 23 October. An unrevoked May master key minted an org-owner GitHub token on 10 October. 1.107.0 wheels and latest Docker tags shipped a credential stealer. cook.pth is outside the package directory. Pinning 1.106.2 is not the hunt.
Bottom line up front
CONFIRMED On 21 October 2026 LiteLLM’s official release pipeline published litellm==1.107.0 to PyPI and pointed Docker Hub / GHCR tags v1.107.0, latest, main-latest, and main-stable at a compromised image. The wheels carried three files that Python runs at interpreter start: cook.pth, cook.py, sitecustomize.py. They sit in site-packages, not under the litellm application directory. The official Git tag v1.107.0 does not contain them. PyPI pulled the wheels by 13:23 UTC. Docker Hub tags were cleaned by 14:20 UTC. Wiz counted about 49,000 PyPI downloads of the bad version.
On 23 October 21:00 UTC LiteLLM published the canonical attack-chain post. That is the overnight increment. The 21 October incident post had guessed GitHub CLI tokens came from logs of an internet-facing LiteLLM deployment. The RCA retracts that.
Do not collapse five facts.
- The Git tag is not the artifact. Reviewers who inspected
BerriAI/litellmatv1.107.0, or the published sdist, did not seecook.pth. The seven platform wheels did. JFrog: the source tarball lacks the three payload files;pyproject.tomlin the sdist gained adata = "cook"setting that the Git tag does not have. pip uninstall litellmis not the close. Microsoft: because the startup files lived outside the LiteLLM application directory, removing only the application files can leave the hooks. Starting Python — including starting pip — re-runs them. LiteLLM’s own cleanup: delete the three files and their__pycache__copies from the shell, then rebuild the venv or image.- Pinning
1.106.2on a dirty interpreter is a second false green.1.106.2is the last safe OSS release LiteLLM named. It does not deletecook.pth. Rebuild from a known-clean Python, or the pin sits on top of the implant. - The access path was an unrevoked key, not a novel CVE. 27 May 2026: master key for an experimental LiteLLM Agent Platform committed to public
LiteLLM-Labs/lite-harness. The commit was force-pushed over. The key was not revoked. 10 October, from 69.48.229.143: that key calledGET /api/v1/managed_agents/agents?limit=100and the response contained a GitHub CLI OAuth token for an organization owner. GitHub CLI tokens are post-2FA and skip the “super-user” reauth GitHub uses for sensitive org actions. - This is not Collabora, not PHP2Shell, and not a PLC. CISA KEV is still catalog
2026.10.23/ 1756. Newest rows remain [CVE-2026-110447](/posts/2026-10-24-collabora-code-cve-2026-110447-kev/) and [CVE-2026-114625](/posts/2026-10-23-arista-velocloud-cve-2026-114625/), both due 26 October. No LiteLLM CVE is on that list. [PHP2Shell](/posts/2026-10-22-php2shell-cve-2026-109618-109619/) is still off KEV.
The operator sentence: if any host or image pulled litellm==1.107.0 or the four Docker tags in the window, assume exfil, delete the three startup files without starting that Python, rebuild the environment, and rotate every secret the host could reach — including every provider key in the LiteLLM proxy database.
What happened
LiteLLM is an open-source gateway that fronts many LLM providers through one API. It is a Python package on PyPI and a set of official container images. Enterprise customers on LiteLLM’s private registry were not in this blast radius. The OSS artifacts were.
Timeline that is in primary sources, not reconstructed. All times UTC.
| When | What |
|---|---|
| 27 May 2026, 18:53 | opencode.json with the Agent Platform master key committed to public LiteLLM-Labs/lite-harness. Commit later force-pushed. Key not revoked. |
| 10 Oct, 21:13–21:28 | Attacker uses the master key from 69.48.229.143. GET /api/v1/managed_agents/agents?limit=100 returns an org-owner GitHub CLI token. |
| 10 Oct, 22:11 | Throwaway branch ci-tmp-a351cc on BerriAI/litellm runs a workflow that posts six repo secrets, including GH_TOKEN, to webhook.site. Run deleted three minutes later. |
| 10 Oct, 22:12 | Same pattern on BerriAI/litellm-auto posts RELEASE_PLEASE_TOKEN. |
| 12 Oct, 12:08–12:30 | Second collection pass across private repos and GitHub Environments. Socket recovered the workflows independently. |
| 21 Oct, 10:08:29 | Branch protection rejects a push to BerriAI/project-releaser. |
| 21 Oct, 10:08:34 | Org-owner token overrides branch protection. Same commit lands. |
| 21 Oct, 10:46 | Staging workflow dispatched with source Berri-AI/litellm (hyphen) at commit 20535d8f, not BerriAI/litellm. |
| 21 Oct, 11:11 | Second maintainer token approves the production gate from 199.91.221.39. |
| 21 Oct, 11:39:51–11:40:13 | Seven litellm==1.107.0 wheels on PyPI. |
| 21 Oct, 11:46:56 | Docker Hub and GHCR images published. Compromised index sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e. |
| 21 Oct, 11:53–11:54 | Third maintainer token deletes production and staging run records. |
| 21 Oct, 12:06 | Lidor Machluf opens GitHub issue #47826. |
| 21 Oct, by 13:23 | PyPI quarantines 1.107.0. Live window ~1 hour 44 minutes. |
| 21 Oct, by 13:51 | Affected GHCR tags unavailable. |
| 21 Oct, 14:20 | Docker Hub tags removed or repointed. Image window ~2 hours 34 minutes. |
| 21 Oct, 16:20–17:01 | Wiz: stolen AWS instance-role credentials validated from 168.100.8.57 and 165.227.201.8 (sts:GetCallerIdentity, then bedrock:ListFoundationModels). |
| 23 Oct, 21:00 | LiteLLM publishes the attack-chain RCA. Canonical. |
LiteLLM had temporarily relaxed the production gate earlier that week so any two engineers could trigger and approve a release, to ship bugfix backports. The attacker used two stolen maintainer tokens to satisfy that gate. LiteLLM notes the owner token could have loosened the restriction itself; using two identities was the quieter path.
This is the second documented LiteLLM hijack in 2026. JFrog tracked March’s 1.82.7 / 1.82.8 (stolen PyPI credentials via an unpinned Trivy in CI) under a different payload and infrastructure. JFrog: treat the campaigns as separate. No public evidence in this retrieval ties 1.107.0 to TeamPCP.
Why it matters
Three operational reasons this is the daily.
First, the implant is the interpreter, not the import. cook.pth is one line: import cook. Python’s site module executes that at startup. Any process that starts Python in that environment — a proxy, a CI job, a developer laptop, pip itself — runs the stealer. Microsoft called the technique T1546.018. You do not have to “use LiteLLM” that hour.
Second, the blast radius is every secret the host could see. JFrog decoded privcollect (static, not a how-to): browser stores; AWS / Azure / GCP credentials including IMDS; Kubernetes and Docker secrets; GitHub CLI; package-manager tokens; AI-tool configs (Cursor, Claude, Codex, Copilot, and others named in that write-up); /proc/<pid>/environ on Linux; and, on a LiteLLM proxy host, **SELECT * FROM "LiteLLM_CredentialsTable". One compromised gateway is every upstream provider key it held. Wiz then watched those AWS roles used the same afternoon for Bedrock model listing, consistent with LLMjacking**. That is confirmed use of stolen cloud credentials, not a victim census.
Third, source review failed as a control. The GitHub source tag is clean. The sdist is almost clean. The wheel is not. Teams that attest “we reviewed the repo” or “we pin by Git SHA” still consumed cook.pth if they installed the PyPI wheel or the latest image during the window. Mutable tags latest / main-latest / main-stable extended the same payload past anyone who never typed 1.107.0.
Who / what is affected
- Affected (vendor): OSS
litellm==1.107.0, all seven wheels, live 11:39:51–13:23 UTC on 21 October. Docker Hub / GHCR tagsv1.107.0,latest,main-latest,main-stablewhile they pointed at indexsha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e(11:46:56–14:20 UTC Docker Hub; GHCR unavailable by 13:51). GHCR also publishedlitellm-databaseandlitellm-non_rootunder the same tags. - Last named-safe OSS:
1.106.2. All prior versions: vendor says safe. - Not affected (vendor): LiteLLM enterprise private registry; customers pulling the embargoed private build. GitHub.com source tag
v1.107.0. - Exposure condition: the wheel or image was installed or started in the window, on a host that could reach the network. Wiz’s 49,000 figure is downloads, not confirmed infections. LiteLLM: if 1.107.0 was installed on a host with outbound internet, assume the exfil succeeded. Absence of
/tmp/.pc_once_72129118c59f5636c0bbproves nothing — containers and tmp cleanup remove it. - Windows vs Unix:
sitecustomize.pyusesos.fork()and fails closed on Windows.cook.pthstill importscook.pyon Windows. The stealer is in. - OT / ICS: UNKNOWN for process impact. This is a Python LLM gateway and developer toolchain. Do not merge it with Siemens Software Center, a plant historian, or yesterday’s Collabora row.
Technical context
High-level, from LiteLLM, JFrog, Wiz, Socket, and Microsoft. This is not an exploit recipe and not a reconstruction of the encoded cook.py blob.
The lookalike repository Berri-AI/litellm (hyphen) received the payload commits on 21 October. Wiz recovered them through Git fork-object sharing after GitHub took the lookalike down. First commits added the three files; a later commit set data = "cook" in pyproject.toml so Maturin copied cook/ into the wheel’s purelib; a last unsigned commit added COPY cook ./cook to the Dockerfiles. Author email on the payload commits: ckdi82fkoos@proton.me.
On install, wheel purelib lands at site-packages root:
cook.pth→import cookcook.py→ Base64+zlib payload executed at import; JFrog names the decoded programprivcollect. Run-once marker/tmp/.pc_once_72129118c59f5636c0bbis written after a successful POST. Environment flagPRIVCOLLECT_ACTIVE=1.sitecustomize.py→ Unix background loader, lock file/tmp/remote_code_loader.lock. Pollsraw.githubusercontent.com/letters9813/pythonclient-test/main/domain.txthourly. If a domain appears, fetcheshttp://<domain>/codepyandexecs it. Wiz: the dead-drop was never populated beyond test values; GitHub took the repo down 21 October ~13:30 UTC. The loader was dormant in that window. Changingdomain.txtwould re-arm any copy still on disk.
Exfil: HTTP POST to www.litellm.dev/healthcheck. Domain registered 13 October 2026. Official project is litellm.ai. Hardcoded header X-Token: XWiA8gZNa7NcX2XRoAEb. Payload encrypted AES-256-CBC, AES key wrapped RSA-4096 — network capture without the attacker’s private key does not recover the secrets.
Wiz: payload shows marks of LLM-aided development (verbose names, generic try/except, Windows paths that do not exist). That is an observation about the code, not attribution.
Socket’s independent recovery of the 10 and 12 October cache.yml workflows is what closed the dwell argument. Subject line both times: ci: bump cache config. Branches ci-tmp-<hex>, created from a recent legitimate commit, pushed, run, deleted. 20 distinct secret names referenced across the two runs, including GH_TOKEN, a GitHub App private key, Linear, Together AI, Anthropic, OpenAI, Bedrock, GitGuardian, Playwright Linear session state, and Postgres / Redis. The two secret sets do not overlap. Socket’s reading, which LiteLLM’s RCA accepts: the first delivery worked.
ATT&CK (defensive mapping, not attribution): T1195.001 compromise software dependencies; T1546.018 Python startup hooks; T1552 unsecured credentials; T1078 valid accounts (stolen GitHub CLI / cloud roles); T1530 data from cloud storage / provider APIs. Actor identity: UNKNOWN. Nation-state: UNKNOWN. Ransomware: not shown.
RWP is not publishing the encoded cook.py blob, the workflow YAML beyond what Socket already printed, or a how-to for the Maturin data = injection.
Threat / exploitation status
| Claim | Status | Evidence |
|---|---|---|
litellm==1.107.0 wheels and named Docker tags carried cook.pth / cook.py / sitecustomize.py | CONFIRMED | LiteLLM incident post; JFrog; Wiz; issue 47826 |
Official Git tag v1.107.0 does not contain the three files | CONFIRMED | LiteLLM; JFrog |
| PyPI live 11:39–13:23 UTC 21 Oct; Docker Hub to 14:20 UTC | CONFIRMED | LiteLLM timeline |
| Enterprise private registry not affected | CONFIRMED | LiteLLM |
Last safe OSS named 1.106.2 | CONFIRMED | LiteLLM; Wiz |
| Access: unrevoked May Agent Platform master key → org-owner GitHub CLI token on 10 Oct | CONFIRMED | LiteLLM RCA 23 Oct |
| Initial 21 Oct hypothesis (tokens from LiteLLM deployment logs / Claude Code) is withdrawn | CONFIRMED | RCA: “some assumptions … inaccurate”; THN 24 Oct |
| Socket recovered 10 Oct and 12 Oct secret-stealing workflows; 11-day dwell | CONFIRMED | Socket 23 Oct; RCA corroborates |
| ~49,000 PyPI downloads of 1.107.0 | CONFIRMED (Wiz / ClickPy) | downloads, not infections |
| Stolen AWS instance-role credentials used 16:20–17:01 UTC 21 Oct from 168.100.8.57 and 165.227.201.8 | CONFIRMED (Wiz) | GetCallerIdentity then Bedrock list |
| Hourly remote loader active in the wild | CONFIRMED dormant in the observation window | Wiz; JFrog zero-byte domain.txt |
| Second LiteLLM compromise of 2026 (after 1.82.7 / 1.82.8) | CONFIRMED as a separate campaign | JFrog; no public link to TeamPCP |
| Actor, census of victim tenants, OT process impact | UNKNOWN | not in this retrieval |
LiteLLM CVE on CISA KEV catalog 2026.10.23 / 1756 | CONFIRMED absent | KEV JSON this run |
What defenders should do
- Inventory first, and do not start the dirty Python to do it. Search images, lockfiles, pip caches, and
site-packagesforlitellm-1.107.0,cook.pth,cook.py,sitecustomize.py. Docker: digestsha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e, tagsv1.107.0/latest/main-latest/main-stablepulled 11:46–14:20 UTC 21 October. CI runners and Kubernetes nodes are in scope, not only the proxy VM. - From the shell, delete the hooks.
cook.pth,cook.py,sitecustomize.py, matching__pycache__files,/tmp/.pc_once_72129118c59f5636c0bb,/tmp/remote_code_loader.lock. Kill any detached Python holding the lock. Then rebuild the venv or image. Do not runpipas step one. - Pin only after the rebuild.
litellm==1.106.2on a cleaned interpreter, or wait for a vendor release LiteLLM will only ship when it is confident in the pipeline. Unpinnedlitellmand mutablelatesttags are how this landed. - Assume exfil if the host had outbound internet. Rotate from a known-clean machine: instance / task IAM roles; AWS / Azure / GCP keys; every provider key in
LiteLLM_CredentialsTable; master and virtual LiteLLM keys;DATABASE_URLpassword; GitHub CLI and GitHub App credentials; npm / PyPI / other package tokens; AI-tool configs on that host. Wiz’s Bedrock listing is the reason the cloud trail check is not optional. - CloudTrail (or equivalent) around install time.
GetCallerIdentityfrom the instance role, then use of that role from outside your ASNs. Wiz’s abuse IPs: 168.100.8.57, 165.227.201.8. Initial-access IP on the vendor side: 69.48.229.143. Approval IP: 199.91.221.39. Absence of those four is not a miss on the class. - Network and host hunts that survive tmp cleanup. Egress to
litellm.dev/www.litellm.dev/healthcheck. HeaderX-Token: XWiA8gZNa7NcX2XRoAEb. webhook.site collectors65a42464-3cfa-4b02-a18c-17a6d7b86f37and4da03473-a3e7-4dc2-a087-9c564b4208e1. EnvPRIVCOLLECT_ACTIVE=1. Hashes Wiz published for the hooks (Linux/macOS):cook.pth7f191632e7320705d154b85204f1ae546b543acbd7e8861229b76f5042b92bdb,cook.py9dd0a3926f96a37eb86cf1ee2310d9be93139cc4ebef3ef9e2efe9531390be25,sitecustomize.py34bef04fe88ac9d0aae0475630b1738681d15b94f233ac8aa46eacf5968305fa. - GitHub org hygiene if you run a similar releaser. Owner-level GitHub CLI tokens in agent platforms; secrets reachable from
on: [push]on any branch; two-person release gates that are just two user tokens; mutablelatesttags. LiteLLM locked the org down to one owner during IR. Copy the lesson, not the incident. - Do not close adjacent tickets on this row. [Collabora 110447](/posts/2026-10-24-collabora-code-cve-2026-110447-kev/) and [Arista VCO 114625](/posts/2026-10-23-arista-velocloud-cve-2026-114625/) are still due Monday 26 October, forensic triage Yes. [PHP2Shell](/posts/2026-10-22-php2shell-cve-2026-109618-109619/) is still the interpreter. Node.js CVE-2026-111497 (Brotli / Node2Shell) is a separate decoder bug with no reported in-the-wild use in this retrieval.
RWP assessment
High confidence that litellm==1.107.0 wheels and the four named Docker tags carried Python startup-hook malware on 21 October, that the Git tag did not, that PyPI and the registries pulled the artifacts the same day, that enterprise private-registry customers were out of scope, and that 1.106.2 is the last safe OSS release LiteLLM has named.
High confidence in the 23 October RCA on initial access: unrevoked May Agent Platform master key, 10 October owner-token theft, 10–12 October workflow exfil, 21 October lookalike-repo release. Socket’s recovered cache.yml runs are independent corroboration of the 10 and 12 October steps.
High confidence that Wiz observed stolen AWS instance-role credentials used for GetCallerIdentity and Bedrock listing the same afternoon. That is not a count of how many of the 49,000 downloads ran the stealer.
Moderate confidence that the hourly sitecustomize.py loader stayed dormant (empty domain.txt, repo taken down). Any surviving copy can still be re-armed. Treat hosts as if the stealer ran; treat the loader as a persistence check, not as “no second stage.”
Low confidence in actor identity, whether the May key was found by opportunistic Git history scraping or directed collection, and whether March’s 1.82.x operator is involved. JFrog says treat them as separate; we do.
Confidence that this is not OT process impact: high. No evidence in this retrieval that a controller, safety system, or physical process was affected.
This outranked the rest of the window because it is a new product, never a Daily Top, with a vendor RCA in the last 24 hours that changed the access path, confirmed credential reuse in AWS, and a built-in false close (uninstall / pin without deleting cook.pth). Collabora and Arista are yesterday’s KEV rows; their Monday due date is a clock. Node2Shell’s blast radius grew to Bun / Python / Envoy on 24 October, but CISA’s 23 October note still recorded exploitation none. GitLab CVE-2026-110264 remains CVSS 10 without confirmed in-the-wild use. HPE ArubaOS HPESBNW05162 is still “no public exploit, no exploitation claimed.” PHP CVE-2026-115560 is still a narrow unpatched config path.
What we are watching
- LiteLLM’s promised process-improvement post, and the first OSS release they will actually sign after locking the org.
- Whether
domain.txtor a replacement dead-drop re-armssitecustomize.pyon hosts that only uninstalled the wheel. - A named victim census, or Bedrock / Anthropic / OpenAI abuse beyond Wiz’s two IPs.
- CISA KEV catalog still
2026.10.23/ 1756. Collabora 110447 and Arista 114625 due 26 October. PHP2Shell 109618 / 109619 still absent. - Node.js / Brotli CVE-2026-111497 (Node2Shell): Imperva demonstrated RCE on one Linux arm64 Node 24.22.0 build; Bun 1.4.4, Python Brotli 1.2.1, Envoy 1.40.0 / 1.39.3 and later are the named floors. No in-the-wild exploitation reported in this retrieval. CISA SSVC 23 October: exploitation none, not automatable.
- GitLab CVE-2026-110264 (unauthenticated regex-compiler RCE, CVSS 10, self-managed from 7.0). Floors 19.5.1 / 19.4.3 / 19.3.6 / 19.2.9 / 19.1.9 / 19.0.10 / 18.11.13. The 12 October regex builds do not close it. No confirmed ITW.
- HPE HPESBNW05162 (22 October): 72 ArubaOS issues, eight scored 10.0. HPE not aware of public exploit or exploitation as of the bulletin.
Sources
Primary: LiteLLM attack-chain RCA, 23 October 21:00 UTC; LiteLLM 21 October incident post (corrected by the RCA); LiteLLM GitHub issue 47826. Independent technical: Wiz 21 October (downloads, payload, AWS follow-on); JFrog 21 October (wheel vs sdist, privcollect); Socket 23 October (10 and 12 October workflows); Microsoft 23 October (startup-hook placement). Secondary: The Hacker News 24 October. Catalog check this run: CISA KEV JSON 2026.10.23 / 1756. Adjacent, not this story: Collabora Daily Top 24 October; Arista Daily Top 23 October; PHP2Shell Daily Top 22 October.
Sources
- LiteLLM — The attack chain behind the v1.107.0 LiteLLM release, 23 Oct 2026
- LiteLLM — Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised
- LiteLLM — GitHub issue 47826
- Wiz — LiteLLM Compromised, Steals Credentials, 21 Oct 2026
- JFrog Security Research — LiteLLM Hijacked Again, 21 Oct 2026
- Socket — New Evidence Shows LiteLLM Secret-Stealing Workflows Ran 11 Days Before Supply Chain Attack, 23 Oct 2026
- Microsoft — Inside the LiteLLM 1.107.0 supply chain compromise, 23 Oct 2026
- The Hacker News — Leaked LiteLLM Master Key Let Attackers Steal GitHub Tokens and Hijack Release Pipeline, 24 Oct 2026
- CISA — KEV JSON feed catalog 2026.10.23
- CISA — Adds One Known Exploited Vulnerability to Catalog, CVE-2026-110447, 23 Oct 2026
- RWP Ventures — Collabora CODE Daily Top 2026-10-24
- RWP Ventures — PHP2Shell Daily Top 2026-10-22
- RWP Ventures — Arista VeloCloud Daily Top 2026-10-23