2026 State of Agentic AI Security
How ready is your organization to secure AI agents?
3 quick questions. See how you compare to other security leaders.
On October 21, 2026, attackers published a poisoned LiteLLM release, version 1.107.0, to PyPI, GHCR and Docker Hub. For about two hours, anyone who ran an unpinned pip install litellm or pulled the latest image got a credential stealer and an hourly remote code loader. The malicious version was downloaded roughly 49,000 times before PyPI quarantined it.
This is the second LiteLLM compromise of 2026. In March, a group tracked as TeamPCP pushed versions 1.82.7 and 1.82.8 with a stolen PyPI token. LiteLLM hardened its release process after that. The October LiteLLM breach went through every one of those new controls, and the artifacts came out signed, with valid provenance.
Below is the timeline, how the payload worked, and how to check and clean up your environment. We also look at what the incident says about AI gateways as a target, and why the entry point is the most uncomfortable part of the story.
TL;DR
What:
litellm==1.107.0(seven wheels) and Docker tagsv1.107.0,latest,main-latest,main-stablecarried a stealer called privcollect plus a backdoor loader.When: October 21, 2026, 11:39 UTC to about 13:23 UTC on PyPI; images until 14:20 UTC.
How: Stolen GitHub CLI OAuth tokens from three engineers let the attacker rewrite the release workflow and build from a lookalike repo,
Berri-AI/litellm.Impact: Cloud keys, AI provider keys, SSH and GPG keys, browser data, AI coding tool tokens, and every upstream provider key stored in the LiteLLM proxy database.
Fix: Do not run pip first. Remove the payload files, kill the loader, rebuild on
1.106.2, and rotate everything the host could reach.
What happened: the LiteLLM breach timeline
The attacker went from a rejected workflow change to a published release in 91 minutes, then deleted the workflow logs 14 minutes later.

The attacker started testing stolen AWS roles two hours after the last poisoned image came down.
Time (UTC) | Event |
|---|---|
08:56 to 10:46 | Attacker pushes 10 commits to the lookalike repo |
09:48 | Commit adds |
10:08:29 | Branch protection rejects an unauthorized change to the private release workflow. |
10:08:34 | Five seconds later, an org owner's stolen token overrides the protection and force-pushes the change. |
10:46 | Modified workflow starts a staging run against |
11:11 | A second engineer's stolen credential approves the production gate. |
11:39 to 11:40 | Seven malicious |
11:46 | Malware scanners flag the release, about seven minutes after upload. Container images publish the same minute. |
11:53 | A third engineer's credentials delete the staging and production workflow records. |
13:23 | PyPI removes 1.107.0. |
13:30 | GitHub takes down the dead-drop repo. |
14:20 | Last poisoned Docker Hub tags removed or repointed. |
16:20 to 17:01 | Attacker tests stolen AWS instance role credentials, including Bedrock model listing. |
The v1.107.0 git tag in the real repo was clean. Anyone who audited the source, or even the tag, saw nothing wrong. The only trace in the sdist was the extra data = "cook" line in pyproject.toml, which the tag did not have.
How the LiteLLM compromise worked
The payload ran every time Python started in an infected environment, whether or not anything imported LiteLLM. Three files were dropped into site-packages:
cook.pthcontains one line,import cook. Python processes.pthfiles at interpreter startup, so the stealer runs before your code does. Runningpipto fix the problem triggers it again.cook.pyis the stealer, which researchers call privcollect after the label in its output. It runs 16 collection modules in a background thread.sitecustomize.pyforks a detached daemon on Linux and macOS. Every hour it readsdomain.txtfrom the GitHub repoletters9813/pythonclient-test, downloads Python fromhttp://<domain>/codepy, and runs it withexec. There is no signature check. The pointer only ever held test values, so no second stage was observed.

Pairing .pth with sitecustomize.py appears to be the first documented case of this technique in the wild. Both are standard Python startup hooks, so the attacker got two independent footholds. cook.pth fires on every interpreter start on any OS, while sitecustomize.py gives Linux and macOS hosts a backdoor that keeps polling for new code. Delete one file and the other still runs.
Researchers also found signs the code was written with LLM help:
Verbose names: variables like
encrypted_private_key_output.Generic error handling: overly defensive
try/exceptblocks that swallow failures instead of handling them.Dead code: a module called
exec_pythat is defined but never called.Untested edges: the run-once marker is written to
/tmp/, which does not exist on Windows, and the loader'sfcntlandforkcalls crash there.
That mix is typical of code built by iterative prompting: it works on the happy path and breaks on platforms nobody tested.
What privcollect steals
Category | Targets |
|---|---|
Cloud | AWS keys and instance or task role credentials, GCP service account tokens, Azure CLI cache and instance metadata, Kubernetes, Vault, Terraform |
Developer secrets | SSH, GPG and TLS private keys, |
AI coding tools |
|
Browsers | Chromium and Firefox logins, cookies and history |
Runtime | Every non-empty env var, and on Linux the environments of other readable processes |
LiteLLM proxy | If |
The loot was encrypted with AES-256-CBC, the key wrapped with RSA-4096, and POSTed to www.litellm[.]dev/healthcheck. The lookalike domain was registered about a week before the release.
About 4.5 hours after exfiltration, the attacker began testing stolen AWS instance role credentials from 168.100.8[.]57 and 165.227.201[.]8. The first calls werests:GetCallerIdentity, then Bedrock and model listing. That pattern points to LLMjacking: reselling or burning a victim's model quota.
Why an AI gateway breach is worse than a normal package breach
Most supply chain stealers grab whatever happens to be on the box. This one shipped a module written for the LiteLLM proxy database. That tells you the attacker understood what an AI gateway is for.
The gateway is a keyring
Teams adopt LiteLLM, so applications never hold provider keys directly. The proxy stores the OpenAI, Anthropic, Azure, Bedrock, and Vertex credentials, then hands out virtual keys. That design is good for governance. It also means one compromised gateway host gives up every upstream key at once, plus the master key that mints new ones. privcollect read LiteLLM_CredentialsTable directly through Prisma, so there was no need to search the disk.
Gateway hosts also tend to run with cloud roles that can call Bedrock or Vertex, and with open egress to many AI endpoints. The attacker's first move with stolen AWS roles was to list Bedrock models. A gateway is close to the ideal LLM-jacking target.
The AI toolchain was the entry point and the target
LiteLLM's own account of the root cause should worry every AI engineering team. Its post says Claude Code can include a user's GitHub CLI OAuth token in response bodies, and those bodies can end up in LiteLLM logs when response logging is on. An internal LiteLLM deployment was reachable from the internet. The team suspects the attacker got in through a recently patched flaw, read the logs, and collected three engineers' GitHub tokens. The root cause is still unconfirmed.
If that holds, the chain runs like this. Tokens from an AI coding agent leak into AI gateway logs. Those tokens are used to poison the AI gateway. The poisoned gateway then steals AI coding agent credentials from every machine that installs it, ready to start the loop again somewhere else.

In many companies, prompt and response logs have become a store of secrets that nobody classifies. They hold whatever agents read, wrote, or echoed: tokens, .env contents, customer data, database URLs. Few teams scan them, restrict who can read them, or keep them off the internet. Treat LLM request and response logs as sensitive API payloads, because that is what they are.
Signing proved the wrong thing
After March, LiteLLM moved releases to a private repo, adopted PyPI Trusted Publishing, made Docker tags immutable, and started signing images. None of it helped. The attacker did not steal a publishing token this time. They edited the workflow that does the publishing, so the legitimate pipeline built, signed, and shipped the malware with valid provenance.
The provenance did record the truth. It said the source owner was Berri-AI, one hyphen away from BerriAI. Nobody checked. A signature tells you which pipeline built an artifact. It does not tell you that the pipeline was building from the right place. The weak points were controls one person could get around: branch protection that an owner token overrode in five seconds, and an approval gate that one stolen credential satisfied.
Repeat targets get hit again
LiteLLM had more than 100 million PyPI downloads in the last month and over 2,000 dependent packages. It sits in front of the most valuable keys in a modern stack. Researchers have not linked October to TeamPCP, but both attacks used the same .pth startup trick. Expect AI gateways, MCP servers, and agent frameworks to keep drawing this kind of attention. A clean history does not make the next release safe.
Who is affected by the LiteLLM incident?
You are exposed if any of these were installed or ran during the window. Enterprise customers on LiteLLM's private registry were not affected, per the official post.
Channel | Affected artifact | Window (UTC, Oct 21) |
|---|---|---|
PyPI | Unpinned | 11:39 to 13:23 |
GHCR |
| 11:46 to about 13:51 |
Docker Hub |
| 11:46 to 14:20 |
The exposure is wider than people who use LiteLLM directly. Any CI job, notebook or container that pulled LiteLLM as a transitive dependency during the window counts. So does any cache, mirror or base image built then.
How to check
Search lockfiles, SBOMs, and CI logs for
litellm==1.107.0orlitellm-1.107.0.dist-info.Check running and cached images for index digest
sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e.Look for
cook.pth,cook.pyand an unexpectedsitecustomize.pyin everysite-packages. Compare hashes, becausesitecustomize.pycan be legitimate.Look for
/tmp/.pc_once_72129118c59f5636c0bb(exfil succeeded) and/tmp/remote_code_loader.lock(loader running). A missing marker proves nothing in containers.Search egress logs for
litellm[.]dev,/healthcheck,/codepy, the headerX-Token: XWiA8gZNa7NcX2XRoAEb, and the stringPRIVCOLLECT_ACTIVE.In CloudTrail, look for
GetCallerIdentity, and Bedrock calls from 168.100.8[.]57 or 165.227.201[.]8, or any instance role used from outside your network.
Do the inspection from a shell. Do not run python or pip on a suspect host, because that re-runs the payload.
What to do now
If 1.107.0 ran on a host with outbound internet access, assume the data left. Work through these in order.
Isolate and preserve. Cut egress for the host or pod, snapshot it, and save logs before you change anything.
Stop the loader. Kill the detached Python process holding
/tmp/remote_code_loader.lock. Uninstalling the package does not stop it or remove the files.Remove the payload from a shell. Delete
cook.pth,cook.py, the malicioussitecustomize.py, and matching__pycache__files from everysite-packages.Rebuild, don't repair. Recreate venvs and images from scratch, pinned to
litellm==1.106.2by hash or digest.Rotate from a clean machine. Cover everything the host could reach:
Cloud instance and task roles, access keys, service account tokens
Every provider key stored in the LiteLLM proxy database, plus master and virtual keys
The
DATABASE_URLpasswordSSH, GPG and TLS keys, git, npm, PyPI and registry tokens
AI coding tool sessions (Claude Code, Codex, Gemini, Cursor, Copilot and others)
Browser sessions on any developer laptop that installed it
Hunt for follow-on use. Review CloudTrail, provider usage dashboards, and billing for new regions, model calls or IAM changes since October 21.
Check your own logs for tokens. If you log LLM response bodies, search them for GitHub, cloud, and provider tokens, and rotate what you find.
Lessons for AI supply chain and AI security
Cleanup closes this incident. These changes reduce the damage from the next one.
Treat AI gateways as tier-0 infrastructure. Give them the controls you give a secrets manager: no public exposure, strict egress allowlists, short-lived cloud roles scoped to the models they serve, and alerts on any new outbound destination. The exfil here went over plain HTTP to a domain a week old. An allowlist would have blocked it.
Keep long-lived keys out of the gateway when you can. Use workload identity for Bedrock, Vertex, and Azure OpenAI instead of static keys in the proxy database, so a database dump yields less.
Classify LLM logs as sensitive data. Turn off response body logging where you don't need it. Where you do, redact tokens before storage, restrict access, and scan the logs for secrets the same way you scan code.
Inventory every AI endpoint you run. The breach likely started at an internal LiteLLM deployment that nobody expected to be on the internet. Continuous discovery of AI gateways, MCP servers, and agent endpoints, with authentication and exposure testing, catches that before an attacker does.
Verify provenance, not just signatures. Check that the source repo in the attestation matches the one you expect. A one-character difference in the owner name was the whole attack.
Protect the workflow definition. Require two people to change release workflows. Use rules that org owners cannot bypass alone, and prefer short-lived, device-bound tokens over GitHub CLI OAuth tokens that do not expire on their own.
Scan the wheel, not the repo. The source and tag were clean. Scanners that inspected the built artifact flagged it within minutes. Add artifact scanning and a cooldown period on new versions of critical dependencies.
Pin by hash. Unpinned installs and floating tags like
latestare how this release reached victims.
Secure your LiteLLM deployment
If you run LiteLLM in production, these two resources help you lock it down:
LiteLLM Security Checklist: a practical list of controls to harden your LiteLLM proxy and the keys it holds.
Akto + LiteLLM Solution Brief: how Akto adds a chained guardrail inside the LiteLLM proxy to catch prompt injection, semantic data leaks, and intent-level policy violations, with the YAML config to turn it on.
Experience enterprise-grade Agentic Security solution


