DEV Community

Teja Kummarikuntla
Teja Kummarikuntla

Posted on

⚠️ AI Gateway Attack: LiteLLM is Compromised

On March 24, 2026, attackers published backdoored versions of LiteLLM to PyPI. The malware harvested cloud credentials, SSH keys, Kubernetes tokens, and pretty much everything else it could find on the host. This is the full breakdown of how it happened, step by step.


The Scale of Impact

LiteLLM gets roughly 3.4 million downloads per day. The compromised versions (1.82.7 and 1.82.8) were live on PyPI before being detected.

Any organization that:

  • Installed litellm for the first time during the window
  • Updated litellm to the latest version during the window
  • Had a CI/CD pipeline that pulled litellm without pinning to a specific version

...should assume that every credential accessible from that environment has been stolen.

The remediation guidance is severe: rotate every secret that was present on any machine where the compromised version was installed. SSH keys, cloud credentials, database passwords, API keys, Kubernetes tokens. All of it.


What is LiteLLM?

LiteLLM is an open-source Python library that acts as a unified proxy for large language models. It lets developers write one API call and route it to OpenAI, Anthropic, Google, Mistral, Cohere, or any other LLM provider without rewriting code for each provider's SDK.

It's massively popular. Around 95 million monthly downloads on PyPI. Thousands of companies use it as the backbone of their AI stack because it makes switching between LLM providers trivially easy.

You install it with pip install litellm, import it in your Python code, and it handles the rest. That "installed directly into your application runtime" part is important. Keep it in mind.


Who is TeamPCP?

TeamPCP is the threat actor behind this attack. They're also responsible for compromising Aqua Security's Trivy vulnerability scanner on March 19, 2026 and Checkmarx's KICS GitHub Action on March 23. The LiteLLM attack on March 24 was the third stage of a broader campaign.


The Attack Chain, Step by Step

This wasn't someone finding a bug in LiteLLM's code. It was a multi-stage supply chain attack that moved laterally across three different open-source projects before reaching LiteLLM's users.

Stage 1: Compromising Trivy (March 19)

Trivy is a popular open-source vulnerability scanner made by Aqua Security. Millions of developers and CI/CD pipelines use it to scan container images, code repos, and infrastructure for security issues.

How it was compromised:

TeamPCP found a misconfigured GitHub Actions workflow in Trivy's repository. Specifically, a pull_request_target trigger.

Here's why that matters. In GitHub Actions, there are two ways to run workflows on pull requests:

  • pull_request runs the workflow using the code from the pull request itself, but with limited permissions. Safe.
  • pull_request_target runs the workflow using the code from the base branch (the main repo), but it gets triggered by an external pull request. The dangerous part is that it gives the workflow access to the repository's secrets (tokens, credentials, etc.) because it's running "trusted" base branch code.

The problem is that if the workflow does anything with the PR's code (checks it out, runs it, uses it as input), an attacker can submit a malicious PR that tricks the workflow into executing attacker-controlled code with full access to the repo's secrets.

TeamPCP used an account called "MegaGame10418" to exploit this. They submitted a pull request that triggered the vulnerable workflow, which gave them access to Aqua Security's aqua-bot credentials. These were privileged credentials that could push code to Trivy's repositories.

What they did with access:

With the stolen credentials, TeamPCP force-pushed malicious commits to 75 out of 77 git tags in two Trivy GitHub repositories. This means when anyone pulled Trivy at almost any version tag, they got the poisoned version.

They also published a malicious Trivy binary as version v0.69.4.

The malicious Trivy contained Python infostealers designed to harvest environment variables, SSH keys, and cloud tokens from CI runners and local systems.

Aqua Security's incomplete response:

Aqua Security detected the breach and disclosed it. They rotated credentials. But the rotation was incomplete. Some access paths remained open, which allowed TeamPCP to continue their campaign.

Stage 2: Pivoting to LiteLLM (March 24)

Here's where it gets really interesting.

LiteLLM's CI/CD pipeline used Trivy as part of its build process. This is normal. Lots of projects run security scanners in their pipelines. The problem was how LiteLLM referenced Trivy.

LiteLLM did not pin Trivy to a specific, verified version.

When LiteLLM's GitHub Actions workflow kicked off a build, it pulled whatever version of Trivy the tag pointed to. Since TeamPCP had rewritten Trivy's tags to point to malicious code, LiteLLM's pipeline pulled the compromised Trivy and ran it.

What the compromised Trivy did inside LiteLLM's build:

It extracted LiteLLM's PYPI_PUBLISH token from the GitHub Actions environment variables. This is the token that authorizes publishing new package versions to PyPI (the Python Package Index, where everyone downloads Python packages from).

With this token, TeamPCP could publish any code they wanted to PyPI under the legitimate LiteLLM package name.

Stage 3: Publishing Poisoned LiteLLM Packages

Using the stolen PYPI_PUBLISH token, TeamPCP published two backdoored versions of LiteLLM to PyPI:

Version 1.82.7 had the malicious payload embedded directly in litellm/proxy/proxy_server.py. This is a core module that gets imported when you use LiteLLM's proxy functionality. The payload was base64-encoded to make it harder to spot in a casual code review.

Version 1.82.8 escalated the approach. Instead of hiding code in a Python module, they included a file called litellm_init.pth at the root of the package.

The .pth Mechanism (This Is the Clever Part)

Python has a feature where files ending in .pth that are placed in the site-packages directory get processed automatically by site.py during interpreter startup.

Normally, .pth files are used for path configuration. They tell Python where to find additional packages. But if a .pth file contains a line starting with import, Python will execute that import statement automatically.

This means the malicious code in litellm_init.pth ran every single time the Python interpreter started. Not when you imported litellm. Not when you used litellm. Just by having litellm installed in your environment.

Think about what that means:

  • Running python manage.py runserver for your Django app? The malware runs.
  • Running a Jupyter notebook? The malware runs.
  • Running any Python script, even one completely unrelated to LLM work? The malware runs.
  • A CI/CD pipeline step that uses Python for anything? The malware runs.

The .pth launcher in version 1.82.8 spawned a detached child process via subprocess, so the payload ran silently in the background without the user noticing any delay or output.

Stage 4: What the Malware Actually Does

The payload operates in three stages:

Credential Harvesting

The script systematically sweeps the host for sensitive data:

  • Environment variables: All of them. This catches anything stored as env vars, which is how most applications store API keys (OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, DATABASE_URL, etc.)
  • SSH keys: Private keys from ~/.ssh/, authorized_keys files
  • Cloud provider credentials:
    • AWS: access keys, session tokens, credential files
    • GCP: application default credentials, service account keys
    • Azure: tokens, credential files
  • Kubernetes configs: kubeconfig files, service account tokens mounted in pods
  • Docker authentication: Docker registry credentials
  • Shell history files: bash_history, zsh_history (which often contain accidentally pasted credentials)
  • Cryptocurrency wallet directories
  • Database credentials and connection strings
  • CI/CD configuration files that often contain tokens and secrets
  • .env files wherever they exist
  • System information: hostname, username, network configuration

If a Kubernetes service account token exists in the environment, the harvester goes further. It enumerates cluster nodes and deploys privileged pods to each one, effectively spreading to the entire Kubernetes cluster.

Encrypted Exfiltration

The collected data goes through this process:

  1. Written to a temporary file
  2. A random AES-256 session key is generated
  3. All data is encrypted using AES-256-CBC with the session key
  4. The session key itself is encrypted with a hardcoded 4096-bit RSA public key (so only the attacker with the private key can decrypt it)
  5. Both the encrypted data and encrypted session key are packaged into a tar.gz archive called tpcp.tar.gz
  6. The archive is uploaded via HTTPS POST to https://models.litellm.cloud/

That domain, models.litellm.cloud, is attacker-controlled but designed to look like legitimate LiteLLM infrastructure. If someone was monitoring network traffic, this request might not immediately raise alarms.

The double encryption (AES for data, RSA for the AES key) means even if someone intercepts the exfiltrated data, they can't decrypt it without the attacker's private RSA key.

Persistence and Remote Access

After exfiltrating credentials, the malware installs persistence:

  • A systemd user service called sysmon.service is created (named to look like a legitimate system monitoring service)
  • This service polls checkmarx.zone/raw every 50 minutes for additional payloads to execute
  • This gives the attacker ongoing remote command execution on the compromised host
  • The backdoor includes a kill switch: if the polled URL returns content containing "youtube.com", the backdoor aborts (likely a safety mechanism for the attacker during testing)

If Kubernetes access is available, the malware deploys privileged pods across cluster nodes, giving the attacker persistent access to the entire cluster infrastructure even if the original compromised pod is removed.


Timeline

Date Event
Late Feb - March 1 Initial exploitation of Trivy workflows, bot steals aqua-bot credentials
March 19 TeamPCP force-pushes malicious code to 75+ Trivy git tags, publishes malicious Trivy v0.69.4
March 19-22 Aqua Security discloses, rotates credentials (incompletely)
March 23 TeamPCP compromises Checkmarx's KICS GitHub Action using similar technique
March 24 Compromised Trivy runs in LiteLLM's CI/CD, steals PYPI_PUBLISH token
March 24 LiteLLM versions 1.82.7 and 1.82.8 published to PyPI with malware
March 24 Community detects malicious .pth file, GitHub Issue #24512 filed
March 24-25 Remediation guidance published: rotate all credentials

Takeaways

  1. The root cause was not a code vulnerability. It was a CI/CD supply chain attack that moved across projects.

  2. The entry point into LiteLLM was an unpinned dependency (Trivy) in the build pipeline, combined with a publishing token that was accessible to the build step.

  3. The .pth mechanism meant the malware ran on every Python startup, not just when litellm was imported. This is a broader execution scope than most supply chain attacks achieve.

  4. The malware harvested everything, not just LLM API keys. It got cloud credentials, SSH keys, K8s tokens, database passwords. This happened because LiteLLM runs inside the application's Python process and inherits all of that process's access.

  5. Architectural isolation matters. If your LLM credentials live in a separate gateway process with its own container and its own secrets, a compromised application dependency can't reach them. The blast radius stays contained.

  6. Even with isolation, supply chain attacks can still hit your gateway. But the damage is scoped to what that gateway has access to, not everything in your entire environment.

Top comments (1)

Collapse
 
dorthyjooper profile image
Dorthy Cooper

For exact affected versions and fix: github.com/BerriAI/litellm/issues/...