The wrong question to ask this week is "did we pull a bad version of LiteLLM." The right one is "did any host of ours start Python after installing LiteLLM 1.107.0 on October 21." The compromised release of LiteLLM, the open-source gateway that routes applications to large language models, did not wait to be imported. It planted startup hooks that execute every time the Python interpreter launches on the machine, whether or not the running process ever touches LiteLLM.
That detail reshapes the whole response. According to Microsoft Threat Intelligence, the poisoned wheel placed three files, cook.pth, cook.py, and sitecustomize.py, outside the normal litellm application directory. Python reads .pth and sitecustomize files at interpreter startup as part of ordinary site initialization, so uninstalling the package or rolling an image tag back leaves them behind. Microsoft is explicit that pulling the package does not amount to recovery.
What was published, and how
On October 21, the LiteLLM release pipeline pushed a tampered v1.107.0 to the Python Package Index and to the project's official container images. As reported by Help Net Security, LiteLLM said the attacker took over three of its engineers' GitHub identities, apparently with stolen GitHub CLI OAuth tokens, then split the work across them: one changed the release pipeline, another signed off on it, and a third wiped the workflow records. The company said it believes initial access traced back to an exposed internal deployment.
Socket, whose scanner flagged the malware, reported the wheel carried a 16-module credential collector and a remote-code loader. The collector swept four buckets of secrets: cloud and orchestration access (the major cloud providers plus Kubernetes, Docker, Vault, and Terraform), developer identity (version-control and package-registry logins, SSH and GPG private keys, and .env files), tokens for AI developer tools, and saved browser credentials. It encrypted the haul and shipped it to a lookalike domain. A second-stage loader read a pointer file hosted on GitHub to fetch follow-on code; by early afternoon UTC that pointer returned an empty body, which blunted the remote-code step.
The window was short. On PyPI the bad wheel was reachable between 11:39 and 13:23 UTC, roughly an hour and three quarters, while the Docker images carried the tampered build until 14:20 UTC. LiteLLM reports roughly 23 million weekly downloads; researchers put the number of downloads of the bad build at about 49,000 before it was pulled. Short is not the same as harmless. A few hours is plenty for an unattended install in a continuous-integration job to hand over every secret that job could read.
Why rolling back is the easy half
Most of the coverage ends at "roll back to 1.106.2." That closes the delivery route and does nothing about the two problems that outlast it. First, the startup files sit outside the package, so a host can keep running the stealer after LiteLLM is gone. Second, any credential the compromised host could read in that window should be assumed captured, and a captured key stays valid until someone rotates it. The install is a delivery event; the credential loss is the incident.
This also widens the blast radius past the people who run the LiteLLM proxy. Because the hooks fire at interpreter startup, every Python process on an affected host is in scope, including cron jobs, build steps, and unrelated services that happen to share the environment. "We do not call LiteLLM from that service" is not a reason to exclude a box that had 1.107.0 installed.
The second poisoning of this pipeline in a year
This is the second time in 2026 that the LiteLLM distribution chain has shipped a poisoned release. In March, malicious LiteLLM packages were part of a wider operation we traced when we covered the TeamPCP campaign and again when CloudSEK mapped its reach across roughly 2,500 organizations. A separate authentication-bypass flaw in LiteLLM's Model Context Protocol support surfaced later. Three distinct security events on one project in a year is a pattern, not luck. An AI gateway sits at a credential chokepoint: it holds provider API keys, its own proxy key table, and usually the cloud identity of the server it runs on. That concentration is what makes it worth poisoning, and it is why this class of tool now deserves the same release-pipeline scrutiny teams already give their build runners.
What recovery actually requires
Treat any host that installed 1.107.0 as credential-compromised, not merely as running a bad package. The order of operations:
-
Pin LiteLLM to
1.106.2, the last release confirmed clean. 1.107.0 is the only poisoned version on PyPI and is now quarantined there; no clean release above it exists yet, so do not jump forward in search of a fix. -
Find and delete the dropped files, including any copies that landed in
site-packages, not just the application directory. The hunt list below has the paths and markers. -
Rotate every credential the affected host could reach: cloud access keys, Kubernetes and Vault tokens, SSH and GPG keys, API keys for AI providers, and the LiteLLM proxy key table itself. Aikido's researcher advised anyone who installed 1.107.0 to treat their credentials as compromised regardless.
-
Rebuild affected containers from a clean base image rather than re-pulling a tag.
# Files dropped by the poisoned wheel (check site-packages copies too) litellm-1.107.0.data/purelib/cook.pth litellm-1.107.0.data/purelib/cook.py litellm-1.107.0.data/purelib/sitecustomize.py # Host markers /tmp/.pc_once_72129118c59f5636c0bb /tmp/remote_code_loader.lock # Exfiltration endpoint and remote-code pointer (defanged) www[.]litellm[.]dev/healthcheck raw[.]githubusercontent[.]com/letters9813/pythonclient-test/main/domain.txt # ATT&CK: T1546.018 Python startup hooks, T1195.001 compromise software dependencies
Microsoft published detection signatures for the payload (Defender flags it under names including Trojan:PyPI/Stealer.SA and a LiteLlmSupplyChain family). Two behavioral observables are worth an alert whether or not you run LiteLLM: a Python process reading SSH keys or cloud instance metadata it has never read before, and an outbound POST from a build or proxy host to the lookalike exfiltration domain. Because the hooks run at any Python start, the detection logic does not depend on the package being present.
The durable lesson is the one the version bump hides. A dependency you trust can turn hostile for a few hours through nothing more than a stolen developer token, and the damage is measured in rotated keys, not in a changelog entry. Pin the things you install to hashes, give release pipelines the same account-takeover defenses you give production, and assume that "we rolled back" is the start of the response, not the end.