analysis

46 Minutes: How a Poisoned Python Package Reached 47,000 AI Environments

A threat group called TeamPCP injected credential-stealing malware into LiteLLM versions 1.82.7 and 1.82.8 on PyPI. Nearly 47,000 downloads happened in 46 minutes. Here is what the attack did, how it started with a compromised security scanner, and what enterprises running AI agents need to check now.

ST
SuperAlign Team
Security Research
Mar 26, 202613 min read
46 Minutes: How a Poisoned Python Package Reached 47,000 AI Environments

Somewhere between 10:39 and 11:25 on March 24th, 2026, a lot of developers had a very bad morning without knowing it yet. In those 46 minutes, a poisoned version of LiteLLM was live on PyPI. It was downloaded nearly 47,000 times. The malware it carried swept each host for SSH keys, cloud credentials, Kubernetes secrets, AI service API keys, and cryptocurrency wallets, encrypted everything, and sent it home. Then it installed a backdoor and waited.

By the time most affected teams found out, their credentials were already gone. Kubernetes clusters got an extra step: the malware tried to deploy privileged pods to every node in the cluster.

LiteLLM is used in roughly 36% of cloud environments and downloaded around 95 million times per month. It sits directly between applications and their AI providers, which means a machine running LiteLLM typically holds API keys for multiple language model services alongside the cloud credentials needed to run them.

This was not a random target.


How supply chain attacks work, and why they keep succeeding

Software supply chains fail because of a structural problem in how modern software is built. Every project depends on packages. Those packages depend on more packages, maintained by developers you will never interact with, whose security practices you have no visibility into. You trust the entire chain whenever you run pip install.

The attack on a developer documented in an earlier LinkedIn post by Jonathan Vanderford followed exactly this pattern. A compromised package update in a routine npm install triggered TruffleHog on his machine, scraped his AWS credentials, and staged them in public GitHub repositories, all in the time it took him to read a changelog.

The same structural vulnerability enabled the LiteLLM attack, just at a different scale and with considerably more sophisticated execution.


It started with Trivy

The LiteLLM compromise did not begin with LiteLLM. It began in late February 2026, when an automated bot called hackerbot-claw exploited a misconfigured GitHub Actions workflow in Aqua Security's Trivy repository, a widely used open-source vulnerability scanner. The bot stole a privileged access token from the aqua-bot CI/CD account.

Aqua rotated their credentials on March 1, but the rotation was incomplete. A still-valid token gave TeamPCP access to newly rotated secrets, and they used that access to extend their reach. On March 19, malicious code was injected into Trivy's GitHub Action tags. On March 23, the Checkmarx KICS GitHub Action was compromised in the same campaign.

LiteLLM's CI/CD pipeline pulled Trivy from apt without a pinned version. When LiteLLM's build ran against the malicious Trivy action, the action exfiltrated LiteLLM's PyPI publishing token from the GitHub Actions runner environment. With that token, TeamPCP published directly to PyPI, bypassing GitHub entirely.

The compromised account belonged to Krrish Dholakia, CEO of BerriAI, which maintains LiteLLM. The account had two-factor authentication enabled. That did not matter. The attackers needed the PyPI API token, not the account password.

As Endor Labs put it in their analysis of the broader campaign: "Each compromised environment yields credentials that unlock the next target." In five days, TeamPCP crossed GitHub Actions, Docker Hub, npm, OpenVSX, and PyPI.


The two malicious versions and what made one far worse

TeamPCP published two distinct versions 13 minutes apart, with different infection mechanisms.

Version 1.82.7 (published at 10:39 UTC) injected 12 lines of malicious code directly into litellm/proxy/proxy_server.py inside the PyPI wheel. The GitHub source was clean. The malicious payload, 34,460 base64-encoded characters at line 130, only ran when litellm.proxy.proxy_server was imported. Most deployments trigger this import, but not all.

Version 1.82.8 (published 13 minutes later) included everything in 1.82.7 plus a file called litellm_init.pth. This is where the attack became categorically more dangerous.

Python automatically executes any .pth file in site-packages/ on every interpreter startup. Before imports. Before application code. Before anything the developer wrote. Installing version 1.82.8 was sufficient to trigger the malware, regardless of whether LiteLLM was ever explicitly used in the code.

FutureSearch engineer Callum McMahon discovered this firsthand when the package was pulled into his environment via a Cursor MCP plugin, which had an unpinned transitive dependency on LiteLLM. The .pth mechanism spawned a child Python process, which re-triggered the .pth, creating an exponential process fork. His machine, a 48GB Mac, hit 11,000 processes with CPU pegged at 100%.

As Andrej Karpathy noted after the incident: "If the attacker didn't vibe code this attack, it could have been undetected for many days or weeks." The malware's own coding error made it visible. A competent implementation of the same mechanism would have been much harder to detect.


What the malware actually did

Once triggered, the payload ran a 332-line Python credential harvester. The credential categories it targeted read like an inventory of everything a developer or AI deployment might have on a machine.

For cloud infrastructure: AWS credentials via direct file access and via the AWS metadata API (IMDS at 169.254.169.254), plus Secrets Manager and SSM parameters. GCP application default credentials. Azure directory trees. Kubernetes configs and service account tokens across all namespaces.

For developer tools: SSH private keys (id_rsa, id_ed25519, id_ecdsa, id_dsa), Git credentials, Docker configs, CI/CD secrets from Terraform state files, GitLab CI, Travis, Jenkins, Drone, and Ansible configurations. Database credentials for PostgreSQL, MySQL, Redis, MongoDB, and LDAP.

For communications: Slack and Discord webhook URLs.

For cryptocurrency: wallets spanning Bitcoin, Ethereum, Solana, Cardano, Litecoin, Dogecoin, Zcash, Dash, Ripple, and Monero.

The harvester searched .env files recursively up to six directories deep across /home, /root, /opt, /srv, /var/www, /app, /data, /var/lib, and /tmp.

After collecting, it encrypted everything with AES-256-CBC and wrapped the encryption key with a hardcoded 4096-bit RSA public key. This RSA key is technically identical to the one used in the Trivy and KICS payloads — the strongest technical link tying all three incidents to the same group. The encrypted bundle was sent to models[.]litellm[.]cloud or checkmarx[.]zone/raw depending on version.

It then installed a backdoor at ~/.config/sysmon/sysmon.py, registered as a systemd service named "System Telemetry Service," polling for next-stage payloads every 50 minutes. In Kubernetes environments, it tried to deploy privileged alpine:latest pods to every node in kube-system, each one mounting the entire host filesystem and installing the same persistent backdoor.

A full technical analysis is available from OX Security and Sonatype.


The scope: 47,000 downloads and 88% of dependents exposed

PyPI download data from BigQuery, analyzed by FutureSearch, showed 46,996 downloads of the two malicious versions during the 46-minute window before quarantine. Version 1.82.8 accounted for 32,464 of those downloads.

The breakdown by installer reveals something about why version 1.82.8 was downloaded six times more than any safe version in the same period. Malicious versions skewed heavily toward plain pip installs, where the resolver picks the latest available version. Safe versions skewed toward uv, where lock files pin to specific known versions.

The 23,142 pip installs of version 1.82.8 each represent an environment where the .pth file ran before any application code, because that is what .pth files do. Those are not just installations. They are executions.

Beyond the direct download count, FutureSearch's dependency analysis found that 88% of LiteLLM's 2,337 dependent packages on PyPI had version specifiers that would have resolved to a compromised version during the attack window. Twelve percent had no version constraint at all. Fifty-nine percent used only a lower bound. Only 283 packages were definitively safe through pinning.

This analysis covered only direct dependencies. Transitive exposure is larger.

Projects that filed issues or pull requests in response included DSPy, MLflow, OpenHands, CrewAI, and Arize Phoenix, among others. Lock files offered protection only to builds that predated the attack window and were not regenerated during those 46 minutes. As FutureSearch put it: "Lock files protect your builds. They don't protect your consumers."


The threat actor and the suppression campaign

TeamPCP (also known as PCPcat, Persy_PCP, ShellForce, and DeadCatx3) has been active since at least December 2025. They operate Telegram channels and an X account. Their payloads embed the string "TeamPCP Cloud stealer," and the LiteLLM compromise was internally labeled "Phase 09." Wiz's analysis noted that the group's infrastructure consistently uses the same registrar (Spaceship, Inc.) and hosting provider (DEMENIN B.V.) across all operations.

On Telegram, the group posted: "These companies were built to protect your supply chains yet they can't even protect their own, the state of modern security research is a joke."

When the community began reporting the compromise on GitHub issue #24512, TeamPCP launched an active suppression campaign. In a 102-second window, 88 bot comments from 73 unique accounts flooded the issue with AI-generated variations of positive feedback. Using the compromised maintainer account, they closed the issue as "not planned." Security researcher Rami McCarthy documented a 76% account overlap between these bots and the botnet that suppressed earlier Trivy vulnerability disclosures. The community reopened discussion in a parallel tracking issue.

Researchers from Aikido documented that hackerbot-claw used an AI agent called openclaw for automated attack targeting, which BleepingComputer reported as one of the first documented cases of an AI agent used operationally in a supply chain attack. And Security Affairs noted that their infrastructure employed the Internet Computer Protocol as a C2 channel — the first observed use of ICP in this context.


Why this attack is different for AI teams

LiteLLM is not a general-purpose Python library. It is a universal API gateway for over 100 language model providers. It sits between applications and their AI services, which means a machine running it holds API keys for multiple model providers simultaneously, alongside cloud credentials, database access, and whatever else the host environment contains.

Compromising LiteLLM at the package level gives an attacker access to a credential set that is qualitatively different from a typical application compromise. A developer running LiteLLM locally to build an AI product is not just a developer. They are a holder of production API keys, cloud credentials, and potentially model provider billing access.

The Cursor MCP connection that brought the attack into FutureSearch's environment is instructive. McMahon was running a Cursor MCP plugin that pulled in LiteLLM as a transitive dependency. When Cursor's IDE tried to autoload the MCP server, uvx silently downloaded the malicious version uploaded just minutes earlier. The seamless developer experience that makes MCP tools easy to use also removed any checkpoint where a human could have noticed the compromise.

Simon Willison has written about the "lethal trifecta" for AI agent attacks: the dangerous combination of access to private data, exposure to untrusted content, and the ability to communicate externally. For months, the AI security community has focused on prompt injection as the mechanism through which an attacker might exploit this trifecta, tricking an AI agent into accessing private data and exfiltrating it.

But as McMahon noted: "MCP servers got us via regular old supply chain attacks, no tricking of LLMs required."

The attack surface researchers identified as a future concern in AI agent systems was exploited through a dependency installed in 2016-era fashion. The tools changed. The underlying vulnerability did not.


What to check and what to do

Start with version verification. Run pip show litellm. Version 1.82.6 is the last confirmed clean release. Versions 1.82.7 and 1.82.8 are malicious. FutureSearch has also published a checker tool to help identify exposure.

If you installed either version, check for the persistence artifacts:

~/.config/sysmon/sysmon.py
~/.config/systemd/user/sysmon.service
find ~/.cache/uv -name "litellm_init.pth"
find ~/.cache/pip -name "litellm_init.pth"

For Kubernetes environments: kubectl get pods -n kube-system | grep node-setup

If any of these indicators are present, assume every credential the machine could reach has been compromised. The Python Packaging Authority's advisory PYSEC-2026-2 states it plainly: "Anyone who has installed and run the project should assume any credentials available to the LiteLLM environment may have been exposed, and revoke/rotate them accordingly."

Rotation should cover SSH keys, AWS/GCP/Azure tokens, Kubernetes configs, API keys in .env files, database passwords, cryptocurrency wallet keys, and any AI service API keys the machine held. Review logs for outbound connections to models[.]litellm[.]cloud or checkmarx[.]zone. In many cases, rebuilding from a known-clean image is the right call.

Going forward, PyPI's Trusted Publishers mechanism ties package uploads to specific CI workflows via GitHub's OIDC identity provider. Had LiteLLM used Trusted Publishers, the attacker would have needed to compromise the GitHub Actions workflow itself, not just an API token. That is a harder problem. It is also not currently the default.


What this means for enterprise AI security

A month-long campaign that began with a misconfigured GitHub Actions workflow at a security vendor ended with malware in a package present in more than a third of cloud environments. The attack crossed five ecosystems and, according to FBI Assistant Director Brett Leatherman, organizations should expect breach disclosures, follow-on intrusions, and extortion attempts in the weeks following the incident as stolen credentials are weaponized.

Three structural problems made the scale of this attack possible.

The first is that AI infrastructure packages occupy a privileged position in developer environments. LiteLLM does not just run in applications. It runs on developer laptops, in CI pipelines, inside MCP servers loaded automatically by IDEs. Each of those contexts has a different credential footprint, and none of them are treated as high-risk installation contexts by standard security tooling.

The second is that most organizations have no visibility into which AI packages are running in their environments, at what versions, with what credentials in scope. Traditional vulnerability management tools scan for known CVEs. They are generally not watching for credential-rich packages installed without pinning, or for .pth files that execute on interpreter startup.

The third is that the threat is not static. TeamPCP labeled this Phase 09. The group is active, their targeting is deliberate, and they are selecting packages specifically because of the credential access those packages provide in AI infrastructure environments.

This is the category of risk that SuperAlign was built to address. Enterprises deploying AI agents — whether for internal automation, customer-facing products, or developer tooling — need real-time visibility into the AI packages and tools running across their environments, what versions are installed, what credentials are in scope, and whether any of it has changed unexpectedly. SuperAlign provides that visibility: detection, monitoring, and policy enforcement across AI interactions and third-party AI tool usage, built for the organizations that cannot afford to find out about a compromise through a runaway process count.

The LiteLLM attack will not be the last time a widely used AI infrastructure package becomes the entry point for a credential theft campaign. The attack surface is large, the packages are credential-rich, and the tooling most organizations have in place was not designed for this environment.


Sources