# LiteLLM Got Owned, and Your Dependencies Might Be Next


Earlier today, someone published malicious versions of [litellm](https://pypi.org/project/litellm/) (versions 1.82.7 and 1.82.8) to PyPI. No corresponding release appeared on GitHub. The package was uploaded directly to PyPI, bypassing the normal release process, which points to a compromised maintainer account or token.

## What the malware did

The payload was a `.pth` file named `litellm_init.pth`. Python executes `.pth` files automatically on interpreter startup, so the malware ran without any user interaction once the package was installed.

It operated in three stages:

1. Collection: Harvested SSH keys, `.env` files, cloud credentials (AWS, GCP, Azure), Kubernetes configs, database passwords, `.gitconfig`, shell histories, and crypto wallet files. It also scraped environment variables and queried cloud metadata endpoints.

2. Exfiltration: Encrypted collected data with a hardcoded 4096-bit RSA public key and AES-256-CBC, then POSTed it to `models.litellm.cloud`, a domain unrelated to legitimate litellm infrastructure.

3. Lateral movement: If Kubernetes credentials were present, the malware read all cluster secrets and attempted to create privileged pods on every node in `kube-system`, installing persistent backdoors via systemd.

The malware had a bug: because `.pth` files trigger on every interpreter startup and the payload spawned child processes via `subprocess.Popen`, it created an exponential fork bomb that crashed machines.

The attack was [discovered](https://futuresearch.ai/blog/litellm-pypi-supply-chain-attack/) when the compromised package was pulled in as a transitive dependency by an MCP plugin running inside Cursor. A GitHub issue was filed and closed as "not planned," suggesting the maintainer account was still compromised at that point.

## This keeps happening

litellm gets [roughly 95 million downloads per month](https://pypistats.org/packages/litellm). It sits in the dependency tree of countless AI applications. If your CI pulled a fresh install between 10:52 UTC and whenever PyPI yanked the versions, you may have been affected.

This follows a pattern. In 2022, the [ctx package](https://blog.phylum.io/phylum-discovers-ctx-pypi-supply-chain-attack/) was compromised through an expired domain re-registration. In 2024, [Ultralytics](https://blog.yossarian.net/2024/12/06/zizmor-ultralytics-injection) (80 million monthly downloads) was hit via GitHub Actions script injection. Earlier this year, the [Shai-Hulud worm](https://www.reversinglabs.com/blog/shai-hulud-pypi-supply-chain-attack) demonstrated cross-ecosystem token exfiltration from GitHub secrets.

The attacks are getting more sophisticated. The defenses need to keep up.

## How to protect yourself

Bernát Gábor, who maintains virtualenv, tox, platformdirs, and filelock for PyPA, published [Securing the Python Supply Chain](https://bernat.tech/posts/securing-python-supply-chain/) earlier this month. It is the most practical guide available on this topic.

Gábor's core argument is defense in depth: no single measure stops every attack, but layering protections means each failure mode is covered by something else. He organizes the work into phases based on effort.

### Quick wins (a day or two)

Add Ruff security linting. Rules like `S105` (hardcoded secrets), `S301` (pickle deserialization), and `S608` (SQL injection) catch common vulnerabilities in your own code before they ship.

Pin dependencies with hashes. `uv pip compile --generate-hashes` produces a lockfile where every artifact is verified by its hash at install time. This stops tampering in transit, in caches, and on mirrors. It would not have caught today's litellm attack (the malicious version was a legitimate PyPI upload), but it narrows the attack surface.

### Foundation work (about a week)

Run pip-audit in CI. It checks your dependency tree against the [OSV database](https://osv.dev/) for known CVEs. Once litellm's malicious versions are added to the advisory databases, pip-audit would flag them.

Generate SBOMs. A CycloneDX SBOM gives you a machine-readable inventory of every dependency in your builds. When the next advisory drops, you can answer "are we affected?" in seconds instead of hours.

Switch to Trusted Publishing. [OIDC-based publishing](https://docs.pypi.org/trusted-publishers/) replaces long-lived PyPI API tokens with short-lived, scoped credentials tied to your CI provider. If litellm had used Trusted Publishing, a stolen token from a different context could not have been used to upload packages.

### Advanced measures

Delayed ingestion. If you run a corporate mirror, add a 7-day buffer before new versions become available internally. Most obvious malware gets caught by the community within days. Today's litellm attack was caught within hours.

Time-based installation constraints. [uv](https://pydevtools.com/handbook/reference/uv.md) supports `--exclude-newer` to limit installations to packages published before a specific date. pip v26+ adds `--uploaded-prior-to`. Both give you a buffer against fresh compromises.

### What would have helped today

Hash pinning alone would not have caught this, since the malicious package was a real PyPI upload with valid hashes. What would have helped:

- Lockfiles with pinned versions: If your lockfile pinned litellm to 1.82.6, you never pulled 1.82.7 or 1.82.8
- Delayed ingestion: A 7-day buffer would have kept the malicious versions off internal mirrors entirely
- `--exclude-newer`: Constraining to packages published before today would have skipped the compromised uploads
- Trusted Publishing on the litellm side: Would have prevented the unauthorized upload in the first place

Gábor's guide covers all of this with implementation details, CI integration examples, and clear explanations of what each layer does and does not protect against. Read it: [Securing the Python Supply Chain](https://bernat.tech/posts/securing-python-supply-chain/).
