When Your LLM Wrapper Steals Your Keys: The litellm 1.82.8 Supply Chain Attack
Source: simonwillison
Supply chain attacks against Python packages have been a slow-burn crisis for years, but when one lands in a library that sits directly in front of your API keys, the stakes are unusually high. That’s what happened with litellm 1.82.8, which shipped a malicious file called litellm_init.pth designed to harvest credentials from affected systems.
litellm is a widely used Python library that wraps dozens of LLM provider APIs behind a single interface. If you’re calling OpenAI, Anthropic, Cohere, Mistral, or almost anything else, litellm is a common abstraction layer. Its user base is exactly the kind of developer who has OPENAI_API_KEY, ANTHROPIC_API_KEY, and AWS credentials sitting in their environment. Targeting litellm is not a random choice.
What a .pth File Is and Why It’s Dangerous
The attack surface here deserves careful explanation, because .pth files are not widely understood outside the Python packaging ecosystem.
Python’s site module, which runs automatically on every interpreter startup, scans the site-packages directory for files ending in .pth. These are path configuration files: normally they contain directory paths, one per line, which get added to sys.path. That’s the intended use. But there’s a second behavior that makes them a potent attack vector: any line beginning with import is executed as a Python statement.
So a file like litellm_init.pth placed in site-packages containing:
import os; exec(__import__('base64').b64decode('...'))
runs that code on every Python invocation, regardless of what script you’re running or whether litellm is even imported. You could be running a completely unrelated script and still trigger the payload. The malicious code executes before your program starts.
This is a known but underappreciated attack vector. The Python security community has documented .pth abuse before, but it keeps appearing in new supply chain incidents because it’s effective and subtle. Unlike a trojanized __init__.py that only runs when you import the package, a .pth file runs unconditionally.
The Credential Theft Angle
Given litellm’s user base, the targeting logic is straightforward. Developers using litellm almost certainly have some combination of the following in their environment:
OPENAI_API_KEYANTHROPIC_API_KEYCOHERE_API_KEYGOOGLE_API_KEYor Vertex AI credentialsAWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY(for Bedrock)HUGGINGFACE_API_KEY
A credential stealer running at Python startup has access to all of os.environ, which means it can scoop up any API keys exported in the shell, loaded from .env files before the process started, or set by CI/CD systems. It can also look for credential files in predictable locations: ~/.aws/credentials, ~/.config/gcloud/, SSH keys, and git configuration.
The exfiltration mechanism in these attacks is typically a simple outbound HTTP request to an attacker-controlled server, sent in a background thread so it doesn’t block execution or produce visible errors. From the user’s perspective, Python starts normally. Nothing looks wrong.
How the Package Was Compromised
The specific mechanism by which litellm 1.82.8 was compromised matters for understanding the broader risk model. PyPI supply chain attacks generally happen through one of a few vectors: a maintainer’s account gets phished or credential-stuffed, a malicious pull request slips through code review and gets merged before the release, or a dependency of the target package is compromised first.
In the litellm case, the malicious .pth file ended up in the distributed wheel or sdist. That means it bypassed whatever review process exists and made it into an official release on PyPI. This is the most damaging scenario because users installing from PyPI with pip install litellm==1.82.8 would receive the compromised package without any obvious warning.
Because litellm is a large and actively developed project with many contributors and frequent releases, the attack surface for a compromised commit or release pipeline is real. A package that ships multiple versions per week is harder to audit thoroughly.
Why This Class of Attack Will Keep Happening
The incentive structure for targeting AI-adjacent Python packages is extremely favorable for attackers right now. The average litellm user has API credits worth real money. OpenAI API keys with high spending limits are valuable on underground markets. AWS keys can be used to provision infrastructure for cryptomining or further attacks. Anthropic and Cohere credits can be resold or used to run inference for others.
Compare this to, say, stealing credentials from a random web scraping library. The expected value per compromised developer is much higher with litellm. If an attacker compromises ten thousand developer machines through a poisoned release, even a modest fraction of those having API keys with meaningful spending limits represents substantial financial gain.
This is also happening against a backdrop where AI development moves fast and developers are often installing packages quickly, pinning versions loosely, or using pip install --upgrade reflexively to get new model support. Good hygiene around dependency pinning is in tension with the rapid pace of litellm releases.
What Detection Looks Like
Detecting a malicious .pth file after the fact is straightforward if you know to look. The site-packages directory for your Python environment should contain only files installed by packages you explicitly installed. Auditing it is simple:
find $(python -c "import site; print(site.getsitepackages()[0])") -name "*.pth" | xargs grep -l "import "
That finds any .pth files containing executable import statements. In a clean environment, there should be few or none, and the ones that exist (like easy-install.pth or distutils-related files) should look innocuous.
For ongoing monitoring, tools like pip-audit check your installed packages against known vulnerability databases, but they won’t catch a brand-new malicious release before it’s been reported. The detection gap between a malicious package being published and it being pulled from PyPI is the window of risk.
If you installed litellm 1.82.8, the immediate remediation steps are:
- Rotate all API keys that could have been present in your environment during any Python execution after installing that version.
- Check your
site-packagesdirectory for thelitellm_init.pthfile and remove it. - Upgrade to a clean version of litellm.
- Review any outbound HTTP traffic from affected machines during the exposure window if you have logs.
Rotating the keys is non-negotiable. You cannot know what was captured and exfiltrated, so treating all keys as compromised is the only safe posture.
Pinning, Lockfiles, and the Broader Lesson
This incident reinforces an argument that keeps getting made and keeps getting ignored: pin your dependencies with hashes.
pip install litellm with no version constraint will install whatever the current latest version is. pip install litellm==1.82.8 pins the version but not the content: if the package at that version was swapped (which can happen if an attacker controls the release), you still get malicious code. Hash pinning is the only strong guarantee:
litellm==1.82.7 \
--hash=sha256:abc123...
With a hash pin, pip will refuse to install a package whose content doesn’t match the recorded hash, regardless of what PyPI serves. This is what pip-compile --generate-hashes from pip-tools or a properly configured Poetry lockfile gives you.
In practice, most AI projects don’t do this. The tooling friction is real, litellm releases constantly, and keeping hashes current requires running the lock update workflow on every dependency bump. But that friction is the cost of the guarantee. Without it, you’re trusting PyPI’s infrastructure and every maintainer’s account security every time you install.
Virtual environments with hash-pinned lockfiles committed to version control also make auditing straightforward. You have a record of exactly what was installed, and any deviation is detectable.
A Note on the Specific Target
It’s worth sitting with the specifics here. litellm is infrastructure for AI applications. The developers using it are often building products that themselves handle user data and make decisions with real consequences. A compromised developer machine with a stolen API key is bad. A compromised developer machine that’s also used to push code to production systems that other people’s applications depend on is worse.
The .pth persistence mechanism means this isn’t just a one-time credential grab. Every Python invocation on the compromised system re-runs the payload. If you’re running automated tests, CI pipelines, or any scheduled Python scripts, the credential exfiltration runs repeatedly. A key that gets rotated might get captured again when you set the new key in your environment, depending on when the malicious .pth file was removed.
The Python ecosystem’s openness is one of its genuine strengths. The ability to publish a package to PyPI with minimal friction has enabled enormous amounts of useful software. The cost of that openness is incidents like this one, and they’ll keep happening as long as the tools that developers trust have accounts that can be compromised and release pipelines that can be subverted.
The full analysis from Simon Willison is worth reading for the specifics of what the malicious code does. The broader takeaway is that any package handling API credentials is a high-value target, the .pth attack vector is real and underappreciated, and hash-pinned lockfiles are the most practical defense available right now.