LiteLLM 1.107.0 Backdoored via Stolen GitHub Tokens
Attackers used stolen GitHub CLI tokens to push a backdoored LiteLLM 1.107.0 to PyPI and Docker — the AI gateway's second supply chain breach in a year.

LiteLLM got popped again. On October 21, attackers pushed a trojanised release — version 1.107.0 — to PyPI, Docker Hub and GHCR, bundling a credential stealer and an hourly-beaconing remote access trojan into one of the most widely deployed AI gateways on the planet. The malicious PyPI package lived for roughly two hours before quarantine; the poisoned Docker tags (1.107.0, latest, main-latest, main-stable) a bit longer. If you pulled an unpinned build in that window, assume your secrets are gone and act accordingly.
This is LiteLLM's second supply chain compromise in a single year. After the March 2026 incident — which CloudSEK estimated potentially exposed more than 2,500 organisations and 434,000 CI/CD pipelines — the project hardened its release pipeline: private release repo, immutable Docker tags, signed images. None of it stopped this one, because the attacker walked in through the front door with valid credentials. That's the story here, and it's uglier than a typo-squat ever could be.
What's confirmed, what's claimed, and what nobody knows yet
Let's split the evidence pile, because the gaps matter for how you respond.
- Confirmed (multiple independent researchers): the poisoned files, the auto-execution mechanism, the exfiltration endpoint, and observed abuse of stolen AWS credentials within hours, documented by Aikido, Wiz and Socket.
- Claimed (by LiteLLM): the intrusion path — Claude Code embedding GitHub CLI OAuth tokens in response bodies that ended up in LiteLLM logs, exfiltrated from an internet-facing internal deployment.
- Unknown: how that internal deployment became publicly reachable, how the attacker got in, the total number of victims, and whether the March attackers are back.
- Disputed: attribution. The .pth technique mirrors the March attack attributed to TeamPCP, but Socket says the evidence doesn't connect them.
The chain: one leaked token to a signed, poisoned release
According to The Hacker News, the sequence started on October 19, when LiteLLM temporarily widened release permissions so two internal engineers could push daily bugfix backports for customers. The attacker, holding three sets of developer GitHub CLI credentials — including one that could run workflows — used a token belonging to a GitHub organisation owner to disable branch protections and force-push malicious changes.
Then the elegant part: they ran the legitimate release workflow but pointed it at a lookalike repository, Berri-AI/litellm instead of BerriAI/litellm. One hyphen. Because the pipeline itself was genuine, the poisoned artifacts came out the other end signed and published through the normal channels. This is the part that should bother you if you run any CI/CD of your own: signature verification proves the artifact came from the pipeline, not that the pipeline was pointed at the right source.
"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," LiteLLM said in its advisory.
The maintainers' working theory is that the attacker found an "unexpected, innocuously named user account" in an internal LiteLLM deployment exposed to the public internet, pulled those OAuth tokens from its logs, and used them against the release workflow. How that deployment ended up internet-facing is unclear — LiteLLM theorises a recently patched vulnerability. File that under unproven until demonstrated.
Inside the payload: cook.pth, cook.py and a hourly C2 loop
The malware itself is a tidy little chain, and worth understanding because the primitives generalise. Aikido researcher Ilyas Makari walked through it, per The Register and The Hacker News:
cook.pth— a Python path file. Any line starting withimportin a .pth executes at every interpreter startup. This one importscook. No user action required, no import of LiteLLM needed; just running Python triggers it.cook.py— deobfuscates itself and runs a stealer routine calledprivcollect, sweeping browser, developer, cloud and AI-tool secrets, then POSTing the loot tolitellm[.]dev/healthcheck.sitecustomize.py— auto-executed on Linux and macOS at interpreter start. Fetchesdomain.txtfrom a now-removed GitHub dead drop (github[.]com/letters9813) and, on a valid response, pulls a second stage from<domain>/codepy.
Makari described the persistence mechanism: the daemon takes a lock at /tmp/remote_code_loader.lock to avoid duplicate instances, then loops forever — every hour fetching the current C2 domain from a raw GitHub file, then downloading and executing a Python payload from it. Wiz researchers Benjamin Read and Rami McCarthy noted the dual .pth plus sitecustomize.py approach appears to be the first documented case of both techniques used together in the wild. Wiz also noted the dead-drop text file contained only test values before GitHub killed it around 1:30 p.m. UTC — a small mercy, but the design means the C2 is trivially re-pointable.

The stealer's scope is what turns this from a package incident into a cloud incident. Per The Register, Wiz says the infostealer runs 16 modules covering browsers (Chrome, Firefox, Edge, Safari credentials and cookies), AWS material from ~/.aws/, environment variables, and EC2/ECS instance metadata. Aikido adds Kubernetes service account tokens and CI/CD secrets from GitLab and Jenkins, according to CSO Online.
The follow-on: LLMjacking, in near real time
Wiz observed abuse of stolen credentials starting about 4.5 hours after exfiltration. Between 4:20 and 5:01 p.m. UTC, the attacker validated AWS instance role credentials harvested from compromised CI runners and Kubernetes nodes from two IPs — 168.100.8[.]57 and 165.227.201[.]8.
"Each credential was checked with sts:GetCallerIdentity, followed by bedrock:ListFoundationModels and a ListModels call, consistent with LLMjacking," Wiz said. "In a small number of environments, the attacker also enumerated IAM (ListRoles, ListUsers, ListAccessKeys) and performed general discovery (ListAccounts, DescribeTrails, etc.)."
That's a fast, deliberate monetisation loop: steal keys, validate them, check what Bedrock models they can burn. If you've watched API-key abuse cases like the one where an OpenAI model spent a stranger's test ETH, you know how cheap autonomous key-misuse has become to run.
Same attacker as March? The evidence says "unproven"
Tempting as it is to pin this on TeamPCP — the .pth loader mirrors the March 2026 tradecraft attributed to them — Socket explicitly declined to connect the dots, telling The Hacker News the two campaigns use different embedded RSA public keys, different exfiltration implementations and different backdoor behaviour. Whether credentials stolen in March played any role is unverified. Same-operator-new-tooling remains possible; it's just not established.
So is trusting an AI gateway still worth it?
Here's the uncomfortable calculus. LiteLLM pulls around 23 million weekly downloads and sits, by design, next to every model API key, cloud identity and database connection string your organisation owns. IDC's Sakshi Grover told CSO Online that "an AI gateway is privileged enterprise infrastructure" — centralising model access improves governance but "concentrates trust." IDC's July survey found 95% of enterprises already run at least one company-funded agent-enabled workflow in production. That's a lot of blast radius riding on one PyPI project.
JFrog researcher Guy Korolevski made a related point in The Hacker News: one poisoned install is enough to let an attacker push malicious code downstream, and a clean-looking source repository tells you little about the artifact you actually install — so verify the exact wheel and vet versions before they reach a build. My take, as someone who's spent too many CTF nights abusing exactly this kind of implicit trust: pinning alone wouldn't have saved you (the tags themselves were poisoned), but pinning plus hash verification plus a quarantine period for fresh releases would have. Vendoring or self-proxying LLM traffic is no longer paranoia — it's a reasonable control for infrastructure this privileged. The engineering overhead is real; so is rebuilding every CI runner in your estate because a .pth file fired.
Detection and response: what to do today
If you installed 1.107.0 between 11:39 a.m. and 1:23 p.m. UTC via pip, or pulled the affected Docker/GHCR tags between 11:46 a.m. and 2:20 p.m. UTC, you're in scope. Makari's quick triage via The Register:
# Exfil already fired if this exists:
ls /tmp/.pc_once_*
# C2 daemon ran (or is still running) if this exists:
ls /tmp/remote_code_loader.lock
# Malicious artifacts:
find / -name 'cook.pth' -o -name 'cook.py' 2>/dev/null
find / -name 'sitecustomize.py' -path '*litellm*' 2>/dev/null
- Roll back to v1.106.2 — the most recent safe version, per The Register — and isolate any host that ran 1.107.0, including proxy containers and CI runners (Socket's advice).
- Block
litellm[.]dev/healthcheckand theraw.githubusercontent.com/letters9813dead drop at egress; alert on any process fetching/codepy. - Rotate every credential reachable from affected hosts: cloud keys, browser-stored secrets, K8s service account tokens, GitLab/Jenkins secrets, model provider keys. LiteLLM's own advice: treat the environment as breached; removing the package isn't enough, since the RAT may still be resident.
- Audit CloudTrail for
sts:GetCallerIdentityfollowed bybedrock:ListFoundationModelsfrom unexpected principals, and check the two Wiz IPs against your logs. - Going forward: pin exact versions, verify wheel hashes against known-good artifacts, and give fresh releases a soak period before they hit builds.
LiteLLM says the incident is contained and enterprise private-registry customers were unaffected; co-founder Krrish acknowledged publicly that the attack was novel and asked for feedback. Fair enough. But twice in one year, through two different doors, the lesson holds: when one package holds every key to your AI estate, its release pipeline is your attack surface whether you signed up for that or not.
Sources
This article draws on reporting and advisories from:


