LiteLLM 1.107.0 Supply Chain Compromise (No CVE): Dual Python Startup-Hook Stealer Shipped Through a Hijacked Release Pipeline

LiteLLM 1.107.0 supply chain compromise: a GitHub token in AI gateway logs leads to a hijacked release pipeline and a backdoored Python wheel carrying cook.pth, cook.py and sitecustomize.py.

Executive Summary

On October 21, 2026 at 11:39 UTC, a malicious litellm==1.107.0 was uploaded to PyPI across all seven platform wheels. The same payload shipped in the official GHCR and Docker Hub images under v1.107.0, latest, main-latest and main-stable. PyPI removed the release by 13:23 UTC and BerriAI finished repointing Docker Hub tags at 14:20 UTC. Wheels installed three files into the site-packages root: cook.pth, cook.py and sitecustomize.py. Python executes both hook types at interpreter start, so the stealer ran on any Python process in the environment, pip included, whether or not the code ever imported LiteLLM.

The payload, self-named privcollect, runs 16 collection modules covering browsers, SSH and GPG keys, cloud credentials and instance metadata, CI secrets, every readable process environment, more than 20 AI coding tools, and the LiteLLM proxy’s own LiteLLM_CredentialsTable. A second hook forks a detached daemon that polls a GitHub dead drop hourly for a second-stage host. Wiz Research observed the stolen AWS role credentials being validated about 4.5 hours after exfiltration, followed by Bedrock model enumeration consistent with LLMjacking.

This is the second LiteLLM compromise this year. In March, TeamPCP pushed 1.82.7 and 1.82.8 with a PyPI token harvested by a poisoned Trivy action. The October entry point is different and more uncomfortable for anyone running an AI gateway: BerriAI states the attacker obtained GitHub CLI OAuth tokens from the logs of an exposed internal LiteLLM deployment, then used them to override branch protection, trigger the release workflow, approve the production gate, and delete the workflow records afterward. The gateway that brokers your model keys was also logging your developers’ GitHub tokens. No CVE has been assigned and none is expected for the package compromise itself.

TL;DR for Engineering Teams

What it is: Malicious litellm==1.107.0 (all 7 wheels) and LiteLLM Docker images carrying a .pth plus sitecustomize.py credential stealer and hourly remote code loader. No CVE. Attribution unconfirmed.

Where it bites: Any environment that installed 1.107.0 between 11:39 and 13:23 UTC on October 21, or ran ghcr.io/berriai/litellm* / docker.io/litellm/litellm from v1.107.0, latest, main-latest or main-stable between 11:46 and 14:20 UTC. Unpinned litellm>= constraints and floating image tags are the main exposure paths.

Why it matters: Execution needs no import. Proxy hosts with DATABASE_URL set lose every upstream provider key stored in the proxy DB. Stolen AWS role credentials were validated within ~4.5 hours (Wiz).

Patch status: Malicious artifacts removed from PyPI, GHCR and Docker Hub. Last clean release: 1.106.2. Release candidates 1.107.0rc1 and rc2 are clean.

Immediate action: Do not run pip first. Delete the three payload files, kill the process holding /tmp/remote_code_loader.lock, rebuild on litellm==1.106.2 (images pinned by digest), rotate every credential the host could reach, block litellm[.]dev.

Vulnerability Overview

FieldValue
VendorBerriAI (LiteLLM)
Productlitellm (PyPI), LiteLLM Proxy images (GHCR, Docker Hub)
Vulnerability TypeSupply chain compromise: malicious release via hijacked CI/CD release workflow
CWECWE-506 (Embedded Malicious Code); CWE-532 (Sensitive Information in Log File) for the upstream token exposure
CVSS ScoreNot scored (no CVE)
CVENot assigned
Patch AvailableYes: pin 1.106.2; 1.107.0 removed
Active ExploitationConfirmed (credential validation and AWS discovery observed by Wiz)
AttributionUnattributed (see lineage section)
Exposure WindowPyPI 11:39 to 13:23 UTC; Docker 11:46 to 14:20 UTC, October 21, 2026
Exfiltrationhxxp://www.litellm[.]dev/healthcheck (plain HTTP POST)

Technical Anatomy

Root Cause

Two failures stacked. Upstream, GitHub CLI OAuth tokens belonging to LiteLLM engineers ended up in request/response logs of an internal LiteLLM deployment that was reachable from the internet. In its incident report, BerriAI states it observed specific situations in which Claude Code embeds a user’s GitHub CLI OAuth token into a response body. Where response bodies are logged, those tokens can land in LiteLLM logs. BerriAI also found an unexpected, innocuously named user account in that deployment and suspects the attacker got in through a recently patched vulnerability but has not confirmed it.

Downstream, the release pipeline trusted those tokens fully. One account could override branch protection on the private release workflow and point the build source at an arbitrary repository; a second account could satisfy the production approval gate; a third could delete the run records. Two-person approval was in place and still failed, because both persons were the attacker.

Exploit Path

  1. Entry point: The attacker harvests three engineers’ GitHub CLI OAuth tokens from the exposed internal LiteLLM deployment’s logs, registers litellm[.]dev on October 13, and stages ten commits on a hyphenated lookalike repository, Berri-AI/litellm, on October 21 between 08:56 and 10:46 UTC.
  2. Vulnerability trigger: At 10:08 UTC branch protection rejects a change to the release workflow; five seconds later a privileged developer credential overrides it. The modified workflow builds from Berri-AI/litellm@20535d8f, a second account approves production at 11:11 UTC, and maturin’s data = “cook” directive copies the payload into litellm-1.107.0.data/purelib/ of every wheel. A final commit adds COPY cook ./cook to the Dockerfiles.
  3. Execution impact: pip installs the .data/purelib contents into the site-packages root. On the next interpreter start, cook.pth (12 bytes, import cook) loads the stealer and sitecustomize.py forks the loader daemon. Collected secrets are encrypted and posted to www.litellm[.]dev/healthcheck.

The v1.107.0 git tag (commit 66c8f5b1a9) is clean. Source review and tag-based provenance checks would not have caught this; SLSA provenance on the images did record the hyphenated source repository, which is how Lidor Machluf traced it in BerriAI/litellm#47826.

Payload Flow

pip install litellm==1.107.0  |  docker run litellm:latest (Oct 21)
          |
          v
site-packages/ { cook.pth, cook.py, sitecustomize.py }
          |
   [any python start]
     |                              |
  cook.pth -> import cook        sitecustomize.py (Linux/macOS)
     |                              |
  privcollect (daemon thread)    flock /tmp/remote_code_loader.lock
  PRIVCOLLECT_ACTIVE=1           fork + setsid, cwd=/
  16 modules incl. db_litellm       |
     |                           every 3600s: GET raw.githubusercontent.com/
  AES-256-CBC + RSA-4096 OAEP      letters9813/pythonclient-test/main/domain.txt
     |                              |
  POST hxxp://www.litellm[.]dev/  if host: GET hxxp://<host>/codepy -> exec()
       healthcheck (X-Token)
     |
  HTTP 200 -> /tmp/.pc_once_72129118c59f5636c0bb

What privcollect Takes

Module groupTargets
BrowsersChromium-family login, cookie and web-data DBs (passwords decrypted in place on Linux/Windows); Firefox logins.json and key4.db; top 300 history URLs per profile
Keys~/.ssh/*, ~/.gnupg/ private keys, .pem/.key/.pfx/.p12/.pk8/.ppk under home, TLS keys from /etc/ssl/private, nginx, Apache, Let’s Encrypt
CloudAWS, Azure, gcloud credential files; AWS IMDSv2 (v1 fallback), ECS task creds, GCP SA tokens, Azure IMDS; AWS creds pre-validated with sts:GetCallerIdentity
Developer and registry~/.kube/config, Docker config, gh CLI tokens, .git-credentials, .netrc, .npmrc, .pypirc, Maven, Gradle, Composer, RubyGems, Cargo, .env files two levels deep
CI and infraGitLab Runner config, Jenkins master key, Vault tokens, Terraform/Packer creds, K8s SA tokens, /run/secrets
Process environmentsCurrent env plus /proc/<pid>/environ for every readable process
AI toolingClaude Code, Claude Desktop, Cursor, Windsurf, Kiro, Zed, Copilot, VS Code MCP settings, Cline, Roo, Cody, Gemini CLI, Codex auth.json, Continue, Aider, Goose, Ollama, Amazon Q, Hugging Face token and others
LiteLLM proxy DBVia the proxy’s own Prisma client when DATABASE_URL is set: LiteLLM_CredentialsTable and LiteLLM_ProxyModelTable (litellm_params holds upstream API keys)

The /proc/<pid>/environ sweep is what makes CI runner and gateway hosts worse than laptops. Secrets injected into sibling processes are captured, not just the environment of the Python step that triggered the hook.

The run-once marker is only written after the exfil server returns HTTP 200. While exfiltration fails (egress blocked, DNS sinkholed), the stealer re-runs on every Python start. On Windows the /tmp marker path usually does not resolve, so it likely re-runs there unconditionally, and the loader fails silently because fcntl and os.fork are absent.

Phoenix Blue Detection for LiteLLM

LiteLLM 1.107.0 has no CVE, and none is coming. Any tool that waits for an NVD entry shows nothing for this package.

Phoenix Blue works differently. It is Phoenix Security’s free threat intelligence feed for malicious and compromised open source packages, and it scores package versions on behavior, not on advisories. This payload is a textbook case of what it looks for: new .pth and sitecustomize.py files that run on every Python start, credentials sent over plain HTTP to a lookalike domain, and a background process that fetches remote code on a schedule.

Every Phoenix Blue verdict has a public page you can look up by package URL (purl), share with your team, and use as evidence in an incident ticket.

Phoenix Blue is free. Look up any package, follow new verdicts as they land, and get the signal before the advisory exists: phxintel.security

For Phoenix Security customers, the same verdicts feed the platform directly. A verdict becomes a finding on every repository, image and workload that carries the package, with an owner attached.

Protect yourself with the latest threat intelligence, get free access to Phoenix Blue Today

The Loader and the Orphaned Dead Drop

The dead drop history shows operator testing: localhost:18909 on October 18, test.test.com:18909 on October 19, emptied on October 20. domain.txt was empty at release time and the /codepy stage was never observed live. GitHub has since removed letters9813 and its repository. That does not neutralize infected hosts. The daemon keeps polling a URL tied to a username, and anyone who re-registers letters9813 and recreates pythonclient-test/main/domain.txt gets code execution on every host where the daemon still runs. Killing the daemon is a recovery step, not housekeeping.

Campaign Phases

PhaseWindow (UTC)Activity
Phase 1: Initial footholdBefore Oct 13 to Oct 21Access to exposed internal LiteLLM deployment; GitHub CLI OAuth tokens scraped from logs; litellm[.]dev registered Oct 13 00:45; letters9813 created Oct 13 06:43
Phase 2: ExpansionOct 21 08:56 to 11:54Ten commits on Berri-AI/litellm; branch protection overridden 10:08:34; staging run 10:46; prod approval 11:11; wheels to PyPI 11:39; images 11:46; workflow records deleted 11:53
Phase 3: Ecosystem targetingOct 21 11:39 to 14:20Wheels on PyPI (~1h44m); floating Docker tags on GHCR (to 13:51) and Docker Hub (to 14:20); mirrors kept serving cached wheels after PyPI removal (StepSecurity)
Phase 4: Persistence and useOct 21 16:20 onwardStolen AWS role creds validated from 168[.]100[.]8[.]57 and 165[.]227[.]201[.]8; bedrock:ListFoundationModels; IAM and CloudTrail discovery in some environments; hourly loader polling continues on uncleaned hosts

Lineage: March (TeamPCP) vs October

DimensionMarch 2026 (1.82.7 / 1.82.8)October 2026 (1.107.0)
AttributionTeamPCP, high confidenceUnattributed
Initial accessPyPI token stolen by poisoned Trivy action in LiteLLM CIGitHub CLI OAuth tokens from exposed internal LiteLLM deployment logs (per BerriAI)
Publish pathDirect PyPI upload, bypassing GitHubBerriAI’s own release workflow, sourced from lookalike repo
Docker imagesNot affected (pinned)Affected, including latest
Startup hooklitellm_init.pth (1.82.8)cook.pth and sitecustomize.py together
Exfil domainmodels.litellm[.]cloudwww.litellm[.]dev
CryptoAES-256-CBC, RSA-4096 OAEP key wrap, tar.gzAES-256-CBC, RSA-4096 OAEP-SHA256 key wrap, tar.gz
Lateral movementPrivileged pods on every K8s node, systemd persistenceNone observed; hourly loader daemon
Distinctive targetAWS Secrets Manager and SSM via SigV4LiteLLM proxy DB, 20+ AI coding tools

The overlap (LiteLLM lookalike domain, .pth startup execution, AES-CBC with RSA-4096 key wrap) is real but commodity-grade, and the March tradecraft was documented in detail publicly. The access path, the K8s behavior and the operator’s dead-drop design all differ. Treat a TeamPCP link as unproven until someone matches the RSA public key or infrastructure. Wiz notes strong signs of LLM-aided development in the payload: verbose naming, blanket try/except, untested Windows paths, and a dead exec_py module. The .pth plus sitecustomize.py combination is, per Wiz, the first documented in-the-wild use of both together.

Mis-citation risks: some March-era tooling tags 1.82.7/1.82.8 as CVE-2026-33634, which is a Trivy-related identifier and not a LiteLLM package CVE. CVE-2026-59822 (LiteLLM auth bypass before 1.84.0, exploited in Wiz honeypots in September) and CVE-2026-40217 are LiteLLM product vulnerabilities; neither has been confirmed as the access vector for this incident. Do not cite either as “the LiteLLM 1.107.0 CVE”.

Affected Versions

PackageVulnerable VersionsFixed VersionNotes
litellm (PyPI wheels)1.107.0 (all 7: macOS x86_64/arm64, manylinux aarch64/x86_64, musllinux aarch64/x86_64, win_amd64)1.106.2Payload in litellm-1.107.0.data/purelib/
litellm (PyPI sdist)1.107.01.106.2data = “cook” present, cook/ absent: tampered config, no payload
ghcr.io/berriai/litellmv1.107.0, latest, main-latest, main-stable (11:46 to 13:51 UTC)v1.106.2 by digestIndex sha256:1bbcb1443bc9…
ghcr.io/berriai/litellm-databaseSame tags and windowv1.106.2 by digestIndex sha256:c00f9d92c427…; db_litellm fires on first start
ghcr.io/berriai/litellm-non_rootSame tags and windowv1.106.2 by digestIndex sha256:7b00e91cd2fe…
docker.io/litellm/litellmv1.107.0, latest, main-latest, main-stable (11:46 to 14:20 UTC)v1.106.2 by digestSame index as GHCR
litellm 1.107.0rc1, rc2, 1.103.x to 1.106.xCleann/a1.107.0.dev1 to dev3 not inspected (StepSecurity)
litellm-proxy-extras, litellm-enterpriseCleann/aReported clean in original disclosure
LiteLLM private enterprise registryNot affectedn/aPer BerriAI

Exposure Analysis

EnvironmentRisk LevelReason
Internet-exposed LiteLLM proxiesCriticalFloating latest/main-stable tags pulled on restart; litellm-database image dumps provider keys on first start; gateway master and salt keys in env
CI/CD pipelinesCriticalFresh resolves of litellm>= during the window; /proc/*/environ sweep captures secrets of sibling steps; AWS role creds observed abused from runners
Cloud workloads and Kubernetes nodesHighIMDS role theft confirmed and validated; Bedrock enumeration consistent with LLMjacking; IAM discovery in some environments
Developer environmentsHighBrowser passwords decrypted locally, SSH/GPG keys, gh tokens, 20+ AI coding tool configs; loader daemon persists on macOS/Linux

Scale: LiteLLM shipped 12 releases across four minor lines between October 17 and 21 (StepSecurity), and maturin platform wheels mean essentially every 1.107.0 install got the payload. Chainguard flagged 1.107.0 sixteen minutes after upload. No public download count for the 104-minute PyPI window has been released.

Real-World Impact

Post-exploitation is confirmed. Wiz observed the attacker validating AWS instance role credentials harvested from compromised CI runners and Kubernetes nodes between 16:20 and 17:01 UTC, roughly 4.5 hours after exfiltration, from two IPs. Each credential got sts:GetCallerIdentity, then bedrock:ListFoundationModels and a ListModels call. That sequence is the standard LLMjacking qualification step: confirm the identity, confirm the model access, sell or burn the quota. In a smaller set of environments the attacker moved to IAM enumeration (ListRoles, ListUsers, ListAccessKeys) and DescribeTrails.

The realistic scenarios for a team that ran 1.107.0:

  1. Model-key theft at the gateway: every upstream key in LiteLLM_CredentialsTable gone, plus the master key that mints virtual keys. Expect fraudulent spend against OpenAI, Anthropic, Azure OpenAI and Bedrock accounts.
  2. Cloud foothold via instance role: IMDS credentials from runners and nodes, already validated and used for discovery.
  3. Release pipeline repeat: gh CLI tokens, .pypirc and .npmrc from developer and CI hosts are the same class of credential that produced this incident. If you publish packages, your pipeline is now the next candidate.
  4. Latent RCE: uncleaned hosts with the loader daemon running are one username registration away from arbitrary code execution.

The broader lesson for AI platform owners is about logging. Prompt and response bodies are routinely logged for spend tracking, evals and debugging. When coding agents pass tool output through the gateway, those bodies can carry tokens. The log store becomes a credential store with weaker access control than your secrets manager.

Detection Guidance

Log and Network Indicators

IndicatorWhere to lookMeaning
DNS/HTTP to litellm[.]dev, www.litellm[.]devDNS resolver, egress proxy, Harden-Runner baselinesStealer ran; exfil attempted
POST /healthcheck with X-Token: XWiA8gZNa7NcX2XRoAEb and UA Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0)Proxy logs, NDRExfil payload (AES/RSA, contents unrecoverable)
GET raw.githubusercontent.com/letters9813/pythonclient-test/main/domain.txt hourlyEgress proxyLoader daemon alive
Any request path ending /codepyProxy logsSecond stage executed: incident scope expands
sts:GetCallerIdentity then bedrock:ListFoundationModels from 168[.]100[.]8[.]57 or 165[.]227[.]201[.]8CloudTrailStolen role creds in use
Instance-role API calls from outside your IP space after Oct 21 11:39 UTCCloudTrail, GuardDuty InstanceCredentialExfiltrationCredential replay
GitHub audit log: branch protection bypass, workflow run deletion, OAuth app token use from new IPsGitHub org audit logSame access pattern against your org

Host Indicators

IndicatorType
cook.pth, cook.py, sitecustomize.py in site-packages root (or /app/.venv/lib/python3.13/site-packages/ in images)File
litellm-1.107.0.dist-infoFile
/tmp/.pc_once_72129118c59f5636c0bbMarker (exfil succeeded)
/tmp/remote_code_loader.lock held by a process with PPID 1, cwd /Process
.pc_sq_*, .pc_e_* in temp dirFile
PRIVCOLLECT_ACTIVE=1 in process environmentEnv
data = “cook” under [tool.maturin]Build config
__pycache__/cook*.pyc, sitecustomize*.pycFile

File Hashes (SHA-256)

ArtifactLinux/macOSWindows
cook.pth7f191632e7320705d154b85204f1ae546b543acbd7e8861229b76f5042b92bdb84dc8c82c0a4a4a62552ef826b49fcc86d1a5c4a5d0f2c06665723f93d129d7d
cook.py9dd0a3926f96a37eb86cf1ee2310d9be93139cc4ebef3ef9e2efe9531390be2503ac2a8b752605d1d7710979c17ff18b03dd978203ba23ba97fd8ce3d6f897ea
sitecustomize.py34bef04fe88ac9d0aae0475630b1738681d15b94f233ac8aa46eacf5968305fab6122440d0d9cb1eb1ade9933f49214da8c31288de7710c076c2680dfa528465
Wheel / ImageSHA-256
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_aarch64.whl668169d35b87bf51324080390b1b1c5ab12bd335cfe5f1a99e3689abd5e02ae6
litellm-1.107.0-cp310-abi3-manylinux_2_28_x86_64.whl462d0c9f67ca522d9e0e4acd245e90b36ce0d5353604a66bb7c43a1ec4a6fcf7
litellm-1.107.0-cp310-abi3-musllinux_1_2_aarch64.whlc2e32d0ce2548aa662c18bf5001911a79fe5b30cf8357132275add02fdcde054
litellm-1.107.0-cp310-abi3-musllinux_1_2_x86_64.whl1bbce8caa94f01c0aaa0b7ddae51553a8a32e1ce53d96d44fe583272aff7ad00
litellm-1.107.0-cp310-abi3-win_amd64.whl12d4ccc5c6f8d35cf4f2cf233b7f3e418e863e657d81ff5f5fc9e210005b9484
litellm-1.107.0.tar.gz (sdist)2befe2a79fb4fbbd9a543b5b40b530c8cffa8771bab22b8fb22b2e4ae3b1f819
ghcr.io/berriai/litellm:v1.107.0 amd64 / docker.io/litellm/litellm amd64bd1aeee419ea36da9aa2b657288da4101887be468f020ac63d22ba6ed84a3ea5
ghcr.io/berriai/litellm:v1.107.0 arm64 / docker.io/litellm/litellm arm6456b74573eed6e4aafe4b50603dbe7853e0345f188c8e44a09f1098caf1f4bcdf
ghcr.io/berriai/litellm-database:v1.107.0 amd6424581ddc30767f16ecb69d2b0517556f2cd7dea56971adb42a3d1beb97d69523
ghcr.io/berriai/litellm-database:v1.107.0 arm64827dd893c74cf621a860a66a3a447a5c732533f087589ce905573e285c0a239c
ghcr.io/berriai/litellm-non_root:v1.107.0 amd64645a67d810628252d63df85dcc8824c58bcc77cf8383f4d3e6321f0ccc3ce9e4
ghcr.io/berriai/litellm-non_root:v1.107.0 arm64f75c8edae59b72b1ebfd617295833f465c85bb2fab9c1d60f21b496db86cb8da
Image index (litellm, Docker Hub)1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e

Scanner References

Phoenix Security ingests SCA, container and SBOM findings and matches litellm@1.107.0 and the image digests above across repositories, registries and running workloads through the campaign config published with this article. GitHub dependency graph and Dependabot will surface the version in committed lockfiles, but not floating Docker tags or unpinned CI installs. Container scanners need the digest list, not the tag, because the tags have been repointed to clean images. SBOMs generated from images built on October 21 are the fastest way to find rebuilt-in-house images that inherited the bad base.

Verification Steps for Teams

  1. Search lockfiles and manifests: grep -rniE “litellm[=<>~! ]*1\.107\.0([^0-9rc]|$)” . –include=requirements*.txt –include=*.lock –include=pyproject.toml –include=Pipfile*
  2. List every project with an open-ended litellm>= constraint and every Dockerfile, Compose file or Helm value using a floating LiteLLM tag; pull CI logs for installs between 11:39 and 13:23 UTC.
  3. Check what actually ran: kubectl get pods -A -o jsonpath='{range .items[*]}{.status.containerStatuses[*].imageID}{“\n”}{end}’ | grep -i litellm and compare against the digests above.
  4. On hosts: find / -name cook.pth -path “*site-packages*” 2>/dev/null and lsof /tmp/remote_code_loader.lock.
  5. Query internal PyPI mirrors and pull-through registry caches for the wheel and image hashes; some mirrors served cached 1.107.0 after PyPI removed it.
  6. Review CloudTrail for GetCallerIdentity from instance roles near install time and any later use from external IPs.

Remediation Guidance

Immediate Actions

  1. Do not start with pip install or pip uninstall. Every Python start re-runs both hooks. From the shell, delete cook.pth, cook.py, sitecustomize.py and matching __pycache__ entries from every affected site-packages.
  2. Kill the daemon holding /tmp/remote_code_loader.lock (lsof or fuser; PPID 1, cwd /).
  3. Rebuild virtualenvs and images from scratch on litellm==1.106.2; for containers, pin v1.106.2 by digest and replace, do not clean in place.
  4. Purge 1.107.0 from internal PyPI mirrors, Artifactory/Nexus caches and container registries by hash.
  5. Rotate everything the host could reach: instance and task IAM roles, cloud access keys, SSH and GPG keys, saved browser passwords and session cookies, GitHub (including gh OAuth tokens), npm, PyPI and other registry tokens, Vault and Kubernetes tokens, CI secrets, and AI tool tokens.
  6. On proxy hosts: rotate every provider key in LiteLLM_CredentialsTable and model litellm_params, the LiteLLM master key, salt key, all virtual keys, and the DATABASE_URL password.
  7. Block litellm[.]dev at DNS and egress; alert on raw.githubusercontent.com/letters9813/ and any /codepy path.
  8. Set provider-side spend alerts and Bedrock model-invocation logging to catch LLMjacking on keys you have not rotated yet.

Temporary Mitigations and Hardening

  1. Pin LiteLLM exactly and with hashes (pip install –require-hashes), and pin images by digest. Hash pinning would not have stopped a fresh resolve in March or October, but it stops silent upgrades.
  2. Add a package cooldown (reject versions younger than 72 hours) on your internal PyPI proxy. Both LiteLLM incidents were cleaned up in under four hours.
  3. Alert on any new .pth file or non-distribution sitecustomize.py in site-packages; both LiteLLM waves executed through them.
  4. Scrub request/response bodies in gateway logging. Redact token patterns (gho_, ghp_, github_pat_, sk-, AKIA) before persistence, restrict log access, and keep internal gateways off the public internet.
  5. Treat release workflows as Tier 0: require branch protection with no admin bypass, restrict workflow source inputs to the canonical repository, short-lived gh tokens, and alert on workflow run deletion.
  6. Restrict egress from CI runners and gateway hosts to an allowlist; the stealer re-runs while exfil fails, but nothing leaves.

–ignore-scripts-style controls are irrelevant here: there is no install script. Execution happens at interpreter start through standard CPython features.

MITRE ATT&CK Mapping

TechniqueIDObserved behavior
Supply Chain Compromise: Software Supply ChainT1195.002Malicious 1.107.0 via release workflow
Valid AccountsT1078Three engineers’ GitHub accounts via OAuth tokens
Unsecured Credentials: Credentials in FilesT1552.001Tokens recovered from gateway logs; dotfiles and .env sweep
Event Triggered Execution: Python startup hooksT1546.018cook.pth, sitecustomize.py
Credentials from Web BrowsersT1555.003Chromium/Firefox stores
Unsecured Credentials: Cloud Instance Metadata APIT1552.005IMDSv2, ECS, GCP, Azure metadata
Steal Application Access TokenT1528gh, AI tool OAuth tokens
Web Service: Dead Drop ResolverT1102.001letters9813/pythonclient-test/domain.txt
Exfiltration Over Unencrypted Non-C2 ProtocolT1048.003HTTP POST to /healthcheck
Indicator RemovalT1070Release workflow run records deleted
Acquire Infrastructure: DomainsT1583.001litellm[.]dev registered Oct 13
Cloud Infrastructure Discovery / Account DiscoveryT1580 / T1087.004Bedrock, IAM, CloudTrail enumeration

Phoenix Security Recommendations

This incident has no CVE, so any program that waits for an NVD entry to open a ticket has no ticket. Phoenix Security’s campaign for this wave matches on package coordinates and image digests directly, so the backlog exists the same hour the IOCs do.

Contextual deduplication. The same litellm@1.107.0 shows up as an SCA finding in a repository, a container finding in the registry, and an SBOM component on a running pod. Phoenix collapses those into one finding per affected asset, with the owning team attached, so a 40-repo exposure reads as 40 owned items rather than 300 scanner rows.

Reachability analysis. Reachability normally answers “is the vulnerable function called”. Here the answer is always yes, because the payload runs at interpreter start. What matters is runtime presence: Phoenix correlates the vulnerable component with deployed workloads and flags which images actually ran on October 21, which hold DATABASE_URL, and which sit behind internet-facing endpoints, so rotation starts with the proxies holding provider keys.

Remediation campaigns. Import the attached campaign config, then: create the campaign, let ownership attribution assign each affected repository and workload, track rebuild-and-pin to 1.106.2 per asset, and verify runtime exposure is zero once the old digests stop appearing in cluster inventory. Credential rotation is tracked as a campaign task alongside the package fix, not as a separate spreadsheet.

Attack surface management. Internet-exposed LiteLLM proxies were both the target of this payload and, per BerriAI, the starting point of the attack. Phoenix ASM inventories exposed gateway endpoints so teams can find the internal deployment that should never have been public before someone else does.

Behavioral detection. The structural gap is the same one we wrote about in March: CVE-driven SCA has no detection surface during the exposure window. Blue Shield CI/CD Firewall and PHX-Neural flag the behavior (a new .pth or sitecustomize.py landing in site-packages, a build directive that pulls an undeclared directory into a wheel, CI egress to a never-seen lookalike domain) instead of waiting for a version to land on a blocklist.

Phoenix Security correlates vulnerable components with runtime workloads, identifies exposed services via attack surface management, and assigns remediation ownership: the blast radius becomes an owned, trackable remediation backlog.

External References

  1. LiteLLM, Contained: Oct 21, 2026 LiteLLM release v1.107.0 Compromised
  2. BerriAI/litellm issue #47826 (initial report, Lidor Machluf)
  3. Attacker commit range via fork object sharing: https://github.com/BerriAI/litellm/compare/a8113b0d…20535d8f
  4. Malicious head commit 20535d8f
  5. Clean v1.107.0 tag commit 66c8f5b1a9
  6. Wiz Research, LiteLLM Compromised, Steals Credentials (Benjamin Read, Rami McCarthy), October 21, 2026
  7. StepSecurity, litellm 1.107.0 Compromised on PyPI
  8. Chainguard, LiteLLM attacked again
  9. OX Security, LiteLLM 1.107.0 Compromised With A RAT InfoStealer
  10. PyPI project page (1.107.0 quarantined)
  11. Python docs, site module (.pth and sitecustomize)
  12. Phoenix Security, TeamPCP Backdoors LiteLLM: Your AI Gateway Just Became the Attack Vector, (March 2026)
Francesco is an internationally renowned public speaker, with multiple interviews in high-profile publications (eg. Forbes), and an author of numerous books and articles, who utilises his platform to evangelize the importance of Cloud security and cutting-edge technologies on a global scale.

Discuss this blog with our community on Slack

Join our AppSec Phoenix community on Slack to discuss this blog and other news with our professional security team

From our Blog

Two critical remote code execution vulnerabilities in PHP core, CVE-2026-109618 and CVE-2026-109619, each rated CVSS v4.0 9.2 by the PHP security team. Leaked community reports circulated a higher CVSS 10.0 figure; the official rating is 9.2.
Marcus Webb
Phoenix Security’s latest platform release tackles the three points where AI security tools break down: unverified findings, firewalls nobody can live with, and remediation that stops at a ticket. Grounded verification, real exception workflows, and PR-first remediation replace guesswork with proof, and tickets with merged code.
Alex Evangelou
Anthropic’s models found 29,439 candidate vulnerabilities in open source. Its dashboard shows 516 patched upstream. That 1.75% is the real story. Vulnerability management now depends on prioritisation, ownership and how fast you consume upstream fixes, because exploits arrive in minutes and median KEV remediation sits at 43 days.
Francesco Cipollone
Phoenix Security now ships two open-source clients — a full v1.27 CLI and a Model Context Protocol server — so security teams can query risk posture from the terminal or from Claude, Cursor, and ChatGPT. Same data, same risk math as the dashboard, now reachable from wherever the question gets asked.
Jorge Vico Castillo
Derek

Derek Fisher

Head of product security at a global fintech

Derek Fisher – Head of product security at a global fintech. Speaker, instructor, and author in application security.

Derek is an award winning author of a children’s book series in cybersecurity as well as the author of “The Application Security Handbook.” He is a university instructor at Temple University where he teaches software development security to undergraduate and graduate students. He is a speaker on topics in the cybersecurity space and has led teams, large and small, at organizations in the healthcare and financial industries. He has built and matured information security teams as well as implemented organizational information security strategies to reduce the organizations risk.

Derek got his start in the hardware engineering space where he learned about designing circuits and building assemblies for commercial and military applications. He later pursued a computer science degree in order to advance a career in software development. This is where Derek was introduced to cybersecurity and soon caught the bug. He found a mentor to help him grow in cybersecurity and then pursued a graduate degree in the subject.

Since then Derek has worked in the product security space as an architect and leader. He has led teams to deliver more secure software in organizations from multiple industries. His focus has been to raise the security awareness of the engineering organization while maintaining a practice of secure code development, delivery, and operations.

In his role, Jeevan handles a range of tasks, from architecting security solutions to collaborating with Engineering Leadership to address security vulnerabilities at scale and embed security into the fabric of the organization.

Jeevan Singh

Jeevan Singh

Founder of Manicode Security

Jeevan Singh is the Director of Security Engineering at Rippling, with a background spanning various Engineering and Security leadership roles over the course of his career. He’s dedicated to the integration of security practices into software development, working to create a security-aware culture within organizations and imparting security best practices to the team.
In his role, Jeevan handles a range of tasks, from architecting security solutions to collaborating with Engineering Leadership to address security vulnerabilities at scale and embed security into the fabric of the organization.

James

James Berthoty

Founder of Latio Tech

James Berthoty has over ten years of experience across product and security domains. He founded Latio Tech to help companies find the right security tools for their needs without vendor bias.

christophe

Christophe Parisel

Senior Cloud Security Architect

Senior Cloud Security Architect

Chris

Chris Romeo

Co-Founder
Security Journey

Chris Romeo is a leading voice and thinker in application security, threat modeling, and security champions and the CEO of Devici and General Partner at Kerr Ventures. Chris hosts the award-winning “Application Security Podcast,” “The Security Table,” and “The Threat Modeling Podcast” and is a highly rated industry speaker and trainer, featured at the RSA Conference, the AppSec Village @ DefCon, OWASP Global AppSec, ISC2 Security Congress, InfoSec World and All Day DevOps. Chris founded Security Journey, a security education company, leading to an exit in 2022. Chris was the Chief Security Advocate at Cisco, spreading security knowledge through education and champion programs. Chris has twenty-six years of security experience, holding positions across the gamut, including application security, security engineering, incident response, and various Executive roles. Chris holds the CISSP and CSSLP certifications.

jim

Jim Manico

Founder of Manicode Security

Jim Manico is the founder of Manicode Security, where he trains software developers on secure coding and security engineering. Jim is also the founder of Brakeman Security, Inc. and an investor/advisor for Signal Sciences. He is the author of Iron-Clad Java: Building Secure Web Applications (McGraw-Hill), a frequent speaker on secure software practices, and a member of the JavaOne Rockstar speaker community. Jim is also a volunteer for and former board member of the OWASP foundation.

Join our Mailing list!

Get all the latest news, exclusive deals, and feature updates.

The IKIGAI concept
Protected By
Shield Security PRO