One pip install and it was over.

SSH keys. AWS, GCP, and Azure credentials. Kubernetes configs. AI keys. Database passwords. Environment variables. Bash history. CI/CD secrets.

Everything.

At 10:39 UTC on March 24, 2026, someone published litellm 1.82.7 to PyPI. Thirteen minutes later, 1.82.8. Neither version existed in the GitHub repository. Neither had a tag. Neither came from the legitimate build pipeline.

They were injected into PyPI with stolen credentials.

Three million daily downloads. Two hours of exposure.

Do the math.


But the malware did not need you to import it.

A .pth file, litellm_init.pth, executed automatically in every Python process that started on the machine where it was installed. No import litellm was required. No direct invocation was required. Merely being present in the environment was enough.

Every script. Every notebook. Every cron job. Every child process.

They all triggered the payload.


The attack operated in three layers, documented by Sonatype:

Layer 1 - Harvesting. A Python script swept the machine: SSH keys, .env files, cloud credentials, Kubernetes configs, database passwords, .gitconfig, shell history, crypto wallets, and anything that smelled like a secret.

Layer 2 - Exfiltration. Everything collected was encrypted with AES-256-CBC, wrapped with a random session key, and that key was encrypted with a hardcoded 4096-bit RSA public key in the payload. The resulting archive, tpcp.tar.gz, was POSTed to models.litellm.cloud. A domain registered on March 23, one day before the attack.

Layer 3 - Lateral movement. If a Kubernetes service account token was present, the malware read every secret in every namespace. It then tried to create a privileged alpine:latest pod on each kube-system node, mount the host filesystem, and install a persistent backdoor at /root/.config/sysmon/sysmon.py with a systemd service.

This was not theft. It was an operation.


The Chain Nobody Saw

This attack did not start in PyPI.

It started with a vulnerability scanner.

March 19. TeamPCP rewrites Git tags in aquasecurity/trivy-action, Aqua Security's open-source vulnerability scanner. Version v0.69.4 now points to a malicious release. Thousands of CI/CD pipelines run it without noticing. CVE-2026-33634, CVSS 9.4.

The tool that scans your code for vulnerabilities is now the vulnerability.

March 23. Every release tag in Checkmarx/kics-github-action is compromised. KICS, Checkmarx's IaC scanner. The master branch was clean. But every workflow referencing the Action by version tag ran attacker code. The exfiltration domain: checkmarx[.]zone. Same payload. Same tpcp.tar.gz. Same encryption scheme.

Checkmarx took the repo down. Then put it back up. Then said it had "no knowledge of impact to customer data or production environments."

Corporate optimism is a fascinating thing.

March 24. LiteLLM's CI/CD pipeline runs Trivy as part of its build process. Not pinned to a version. The compromised Action exfiltrates the PYPI_PUBLISH token from the runner environment. With that credential, the attackers publish the poisoned releases directly to PyPI.

The compromised account belonged to LiteLLM co-founder and CEO Krish Dholakia.


The Loop

Wiz called it a loop. It is a supply chain worm.

Compromised Trivy
  -> stolen CI/CD credentials
    -> compromised Checkmarx
      -> more stolen credentials
        -> compromised LiteLLM
          -> API keys for every AI vendor in your org
            -> ???

Each link in the chain feeds the next one. Credentials stolen in one incident unlock the next. Sysdig confirmed it: identical payload, identical encryption scheme, identical tpcp.tar.gz naming convention. The same actor expanding its reach.

And there is one detail that should keep you awake.

LiteLLM exists for one purpose: to be the unified front door to every major AI vendor. OpenAI, Anthropic, Google, Bedrock, Vertex AI, and hundreds more. By design, the environment where it runs concentrates all of your organization's AI API keys in one place.

The attacker did not choose a random package.

They chose the one package that, by definition, has access to every vendor key.

Precision targeting.


The Bug That Saved Them

The world did not find out because of an audit. Not because of a scanner. Not because security teams did their job.

It was a bug in the malware.

The .pth file launched a Python child process with subprocess.Popen. But because .pth files execute on every interpreter startup, the child re-triggered the same .pth. Then the child of the child. And so on.

An exponential fork bomb. The machine froze.

Developers noticed their machines dying on pip install. They started asking questions. Someone opened a GitHub issue. FutureSearch spotted it because a Cursor MCP plugin pulled it in as a transitive dependency.

The issue was closed as "not planned" by the owner. Hundreds of bots spammed the thread to dilute the discussion. The litellm author was very likely fully compromised.

Sonatype detected it within seconds with automated tooling. PyPI quarantined the project. The malicious versions were yanked a few hours later.

But the malware's persistence code still referenced checkmarx[.]zone, the domain from the previous operation. They did not bother to clean it up.

Vibe coding saves the world. This time.


TeamPCP

Also known as PCPcat, Persy_PCP, ShellForce, DeadCatx3. Possibly linked to LAPSUS$, though attribution is still under investigation.

Their Telegram message:

"These companies were made to protect your supply chains and can't even protect their own."

They are not wrong.

Trivy is a security scanner. Checkmarx sells supply chain security. LiteLLM handles every AI key in your organization. The three tools built specifically to protect you were the attack vector.

The fox did not break into the henhouse. The hens opened the door, handed over the keys, and went back to sleep.


What Nobody Says

Classic software engineering teaches that dependencies are good. That we are building pyramids out of bricks. That every pip install, every npm install, every GitHub Action pinned by tag, is a trusted brick someone already validated.

Every package you install trusts every dependency in the entire tree. Any of them can be poisoned.

This is not paranoia. It just happened.

Microsoft published detection guidance. CrowdStrike published IOCs. Sysdig published Falco rules. Snyk published analysis. The entire security industry mobilized.

After the fact.

The advice is the usual advice: pin Actions to full SHAs, not tags. Monitor outbound connections from CI runners. Restrict IMDS. Rotate credentials.

Things everyone knows. Things almost nobody does.


The next attack will not have a bug that crashes the machine to warn you.

The next tpcp.tar.gz will be quiet, encrypted, and not fork-bombing anything. It will live in your cluster for three months before someone asks the right question.

By then, the credentials will already be on another chain, opening another door, publishing another package.

The loop did not close.

It expanded.