Safeguard
AI Security

When the Scanner Is the Backdoor: The LiteLLM Trivy Attack

On March 19, 2026, TeamPCP hijacked Trivy's GitHub Action to steal LiteLLM's PyPI token, then shipped a backdoored release, CVE-2026-33634, CVSS 9.4.

Safeguard Research Team
Research
6 min read

On March 19, 2026, a threat actor tracked as TeamPCP force-pushed malicious commits onto 76 of the 77 version tags in Aqua Security's trivy-action GitHub Action, plus all 7 tags in setup-trivy, and separately published a backdoored Trivy binary (v0.69.4) to GitHub Releases, Docker Hub, GHCR, and Amazon ECR. Five days later, on March 24, that compromised Action ran inside LiteLLM's own CI/CD pipeline, exfiltrated the project's PyPI publish token, and TeamPCP used it to push two backdoored releases — litellm 1.82.7 and 1.82.8 — directly to PyPI under the real project's name before the packages were quarantined. LiteLLM is one of the most widely used open-source LLM gateway and proxy libraries, deployed across AI-agent frameworks, MCP servers, and production inference stacks. The incident was assigned CVE-2026-33634 with a CVSS score of 9.4, added to CISA's Known Exploited Vulnerabilities catalog, and documented in LiteLLM's own bulletin at docs.litellm.ai as well as independent write-ups from Snyk, Trend Micro, Cycode, Kaspersky, and Datadog Security Labs. What makes this incident worth studying isn't just the blast radius — it's the mechanism. The tool LiteLLM trusted to catch vulnerabilities in its own build was the thing that got compromised first, and that compromise is what makes this a supply-chain lesson every AI infrastructure team needs, not just a LiteLLM problem.

What actually let attackers into LiteLLM's pipeline?

An unpinned reference to a third-party GitHub Action. LiteLLM's CI/CD invoked Trivy — the open-source vulnerability scanner maintained by Aqua Security — through the trivy-action wrapper, and like most projects consuming third-party Actions, it referenced a version tag rather than an immutable commit SHA. Git tags are mutable by design: anyone with push access to the upstream repository can force a tag like v0.69.4 to point at a different commit without anyone downstream seeing a version bump. TeamPCP obtained write access to aquasecurity/trivy-action and rewrote nearly every tag to a malicious commit, and separately shipped a trojanized Trivy binary through Trivy's normal distribution channels. Because LiteLLM's workflow file didn't change, the poisoned Action ran inside a CI job that already held a PYPI_PUBLISH secret — turning a routine scanning step into a credential-exfiltration step with no visible signal in the repository's history.

Why did a security tool make the attack easier, not harder?

Because scanners run with elevated trust and broad filesystem access by default, and that trust is rarely re-verified after initial adoption. Trivy's entire purpose is to read source code, dependency manifests, and build artifacts looking for vulnerabilities, so CI pipelines grant it exactly the access an attacker would want. The campaign wasn't limited to Trivy or LiteLLM either: researchers at Kaspersky and Arctic Wolf documented that TeamPCP simultaneously targeted Checkmarx's KICS-based extensions on Open VSX in the same window, indicating a deliberate strategy of compromising the tools defenders install specifically because those tools carry more implicit trust than an average dependency. A scanner whose whole job is "tell me what to trust" is a uniquely high-leverage target precisely because teams apply far less scrutiny to how it's sourced than they apply to their own application code.

How did stealing one token expose LiteLLM's entire user base?

Because a stolen PyPI publish token lets an attacker impersonate the legitimate maintainer with no further authentication step. Once TeamPCP had LiteLLM's token, they didn't need to compromise a maintainer's laptop or bypass two-factor authentication — they published a routine-looking release and PyPI accepted it. According to Datadog Security Labs' analysis, the malicious releases exfiltrated data to a domain, models.litellm.cloud, that was never an official BerriAI/LiteLLM property, while the underlying Trivy compromise separately sent stolen CI secrets to a typosquatted domain designed to look like Aqua Security's own infrastructure. Because LiteLLM sits as a dependency inside a large number of AI-agent frameworks and inference gateways, any environment that pulled a fresh install during the exposure window inherited the backdoor transitively, without ever directly depending on the compromised version by name.

What made this backdoor persist without an explicit import?

A .pth file — a legacy Python packaging mechanism most engineers never have reason to think about. Python's site module scans site-packages at interpreter startup and executes any line in a .pth file that begins with import. Version 1.82.8 shipped a file named litellm_init.pth exploiting exactly this behavior, so the backdoor's initialization code ran the moment any Python process started in an environment where the package was installed, whether or not the running script ever wrote import litellm itself. This defeats the common defensive assumption that "we don't call that code path, so we're not exposed" — with a .pth payload, installation alone is the trigger.

How should teams change how they trust CI/CD tooling after this?

Pin every third-party Action and scanner to an immutable commit SHA, not a mutable tag, and treat CI secrets as reachable by anything the pipeline executes, not just the step that appears to use them. uses: aquasecurity/trivy-action@v0.69.4 is not equivalent to pinning by a 40-character commit hash — the tag can move underneath you, the hash cannot. LiteLLM's own postmortem and the independent Cycode and Trend Micro write-ups converged on the same remediation checklist: pin Actions by SHA, scope publish credentials to the minimum required lifetime and permission (short-lived OIDC-based publishing instead of long-lived PyPI API tokens where possible), and separate the CI identity that runs a scan from the identity that holds publish rights. None of this is exotic — it's the least-privilege discipline security teams already preach for application code, applied to the pipeline that builds and ships it.

How does Safeguard help with this class of attack?

Safeguard doesn't replace the discipline of SHA-pinning your own CI dependencies — no vendor tool substitutes for that choice — but it addresses the layers around it. Eagle, Safeguard's malware classification model, scores every PyPI, npm, and container publish against behavioral indicators including install-time network activity and divergence from a package's own version history, and re-scores its full historical corpus whenever detection improves, which is the mechanism that would flag a freshly published, behaviorally anomalous release like litellm 1.82.8 even before a CVE existed for it. For the CI layer itself, sourcing base tools and images from Safeguard's Gold registry — which requires SLSA build provenance and a Sigstore signature on every artifact rather than whatever a mutable tag currently resolves to — removes the specific failure mode that let a rewritten Trivy tag run unnoticed inside a trusted pipeline. Neither control makes SHA-pinning optional; both are the backstop for the moment a tool you already trusted publishes something it shouldn't have.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.