Back to Blog

TeamPCP: The Hacker Group That Turned Developer Trust Into an Attack Surface

TeamPCP: The Hacker Group That Turned Developer Trust Into an Attack Surface

TeamPCP: The Hacker Group That Turned Developer Trust Into an Attack Surface

Last updated: June 2026

Two months ago, a code scanner ran in the European Commission's AWS build environment exactly as it was supposed to. Nine days later, ShinyHunters posted 340 GB of stolen Commission data on a dark-web leak site. The scanner did its job; that was the issue, apparently.

The version that ran that Thursday morning had been malicious for about three hours. It pulled an AWS secret out of the CI/CD environment. That secret turned out to carry management rights over other Commission AWS accounts. By the time the Commission's Cybersecurity Operations Centre flagged unusual API traffic on March 24, data was already being copied out of the cloud. CERT-EU later traced the entire incident, with high confidence, back to a single compromised release of a defensive tool.

The crew behind that release calls itself TeamPCP. The Commission breach is the first time their playbook hit a target big enough to make the front page.

How does a security scanner become an attack vector?

For most of the last decade, the supply-chain conversation has lived in the abstract: software bills of materials, vendor questionnaires, dependency inventories. TeamPCP is what that conversation looks like once it leaves the slide deck.

Stop thinking in perimeters for a second. A modern build environment is a graph of trust. A CI runner can read a secret, that secret can unlock a cloud account, a publishing token can write to a registry, and the registry can ship code to thousands of downstream installs. Security scanners sit near the middle of that graph because the job requires it. A tool that inspects container images, repositories, and cloud-native artifacts has to read everything those things contain. That visibility is the whole product. It is also exactly what an attacker needs.

The data has been pointing this way for years. The 2026 Verizon Data Breach Investigations Report says 31% of breaches now begin with software vulnerabilities, more than start with stolen passwords. ENISA's 2025 threat landscape analyzed 4,875 incidents across the EU and identified ransomware as the most impactful threat, with vulnerability exploitation as a leading intrusion path. OpenSSF expects more than 10 trillion open-source package downloads in 2026 across npm, PyPI, Maven Central, and the rest. NSA and CISA say it plainly in their CI/CD guidance: weak pipeline defense gives attackers a way around the security tools defending the perimeter.

TeamPCP appears to have read that map earlier than most defenders. Public reporting from S2W, Wiz, and Flare describes a financially motivated cloud-native cluster that surfaced in late 2025. The first visible campaigns targeted exposed Docker APIs, Kubernetes control planes, Redis instances, Ray dashboards, and Jenkins servers. The common thread across those targets was a working theory of access: find a place where one trusted system can issue commands to another, and the rest of the environment does the work. Trivy applied that same theory to software delivery instead of cloud infrastructure.

What happened to Trivy in February and March 2026?

The compromise predates March 19 by about three weeks. In late February 2026, attackers exploited a misconfiguration in Trivy's GitHub Actions environment and extracted a privileged access token. Aqua Security found the intrusion on March 1, disclosed it, and rotated credentials. The investigation worked. The rotation didn't catch everything. Some of the access stayed valid inside Trivy's release automation, and that surviving access is the reason the next three weeks unfolded the way they did.

On March 19, the attacker used that leftover access to do four things at once: force-push most aquasecurity/trivy-action version tags to malicious commits, replace every aquasecurity/setup-trivy tag with malicious commits, trigger release automation through a compromised service account to publish a malicious Trivy binary as version 0.69.4, and seed the same campaign across Trivy's Docker Hub and GitHub release paths.

The exposure windows were short. The malicious binary was live for about three hours. The trivy-action tags were redirected for roughly twelve hours, and setup-trivy for four hours. On March 22, malicious Docker Hub images tagged 0.69.5 and 0.69.6 went live for about ten hours before removal. In CI/CD, short windows are not safe windows. A team that runs Trivy on every pull request, every merge, and every nightly build can execute the compromised version dozens of times inside a four-hour gap.

The payload was a credential thief. It scraped secrets from the GitHub Actions runner environment and from the usual places where cloud, Kubernetes, registry, SSH, database, and wallet credentials are stored. The scan still produced plausible output, the job logs read like every other Trivy job, and most of the malicious version's behavior was indistinguishable from the legitimate one.

CVE-2026-33634 was published the same week, and on March 26 CISA added it to the Known Exploited Vulnerabilities catalog under the name "Aquasecurity Trivy Embedded Malicious Code Vulnerability." The KEV name does the work: CISA is treating this as malicious code shipped through a trusted distribution path, which puts the federal classification closer to a poisoned-package incident than to a typical CVE.

For the European Commission, the damage was already in motion by the time CISA caught up. Trivy ran inside the AWS environment behind the Europa.eu web hosting platform. The compromised version captured an AWS secret that turned out to carry management rights over other Commission AWS accounts. The attacker spent March 19 doing reconnaissance and looking for more secrets in those accounts. On March 24, the Commission's Cybersecurity Operations Centre opened its first alerts: possible Amazon API misuse, signs of account compromise, abnormal network traffic. The Commission secured the compromised secret on March 25, disabled the access keys the attacker had created, and disclosed the incident publicly on March 27. ShinyHunters posted the dataset on March 28.

CERT-EU's April 2 writeup details the impact. About 91.7 GB compressed (roughly 340 GB uncompressed) was exfiltrated from a cloud account hosting websites for up to 71 Europa clients: 42 internal Commission clients and at least 29 other Union entities. Confirmed exposed personal data included first names, last names, usernames, and email addresses. CERT-EU also confirmed 51,992 outbound email files totaling 2.22 GB, most of them automated notifications, with some bounce-backs that may have contained user-submitted text. There was no evidence of lateral movement into other Commission AWS accounts at the time of the writeup, although the secret had the rights to enable it.

A defensive scanner ran inside a build pipeline, exposed a cloud credential, and that credential opened public-sector data infrastructure. The original Aqua compromise lasted hours. The institutional consequence is still being measured.

How did the campaign spread beyond Trivy?

The Trivy compromise turned into a cascade because the secrets it harvested were reusable elsewhere. By March 31, Ireland's National Cyber Security Centre was describing the TeamPCP supply-chain attack as a multi-ecosystem event spanning GitHub repositories, container registries, Kubernetes clusters, and worm activity powered by stolen tokens. The affected products read like a tour of modern developer trust: aquasecurity/trivy-action, aquasecurity/setup-trivy, checkmarx/kics-github-action, checkmarx/ast-github-action, OpenVSX extensions, and LiteLLM on PyPI.

The next five days are worth listing in order. March 19: Trivy. March 20: CanisterWorm hits npm. March 22: malicious Trivy Docker Hub images. March 23: Checkmarx OpenVSX plugins and GitHub Actions go malicious. March 24: LiteLLM 1.82.7 and 1.82.8 land on PyPI carrying credential-harvesting code. Defenders had to coordinate across registries, maintainers, enterprise pipelines, developer laptops, and cloud tenants while the attacker was already trying the next set of stolen keys.

Checkmarx's own incident write-up gives the cleanest second-wave picture. On March 23 at roughly 02:53 UTC, two malicious OpenVSX plugins appeared (ast-results-2.53.0.vsix and cx-dev-assist-1.7.0.vsix) and stayed live until about 15:41 UTC. Later that day, between 12:58 and 16:50 UTC, malicious payloads showed up in checkmarx/ast-github-action and checkmarx/kics-github-action. KICS is an infrastructure-as-code scanner that reads Terraform, Kubernetes, Docker, CloudFormation, Ansible, and Helm definitions, which puts it in the same neighborhood as Trivy: close to deployment secrets. Checkmarx later said the March 23 incident likely came from the Trivy compromise, which had given the attacker access to its GitHub repositories. Data exfiltration followed on March 30. The stolen data appeared on a dark-web leak site on April 25, four weeks after the original Trivy compromise.

The LiteLLM compromise pulled AI infrastructure into the same chain. LiteLLM is a Python proxy library that many teams use to route requests across LLM providers through a single interface. The PyPA advisory for PYSEC-2026-2 says two LiteLLM versions reached PyPI carrying credential-harvesting malware that activated automatically, after an API token exposure from the exploited Trivy dependency. The malicious versions were uploaded directly by the attacker and bypassed LiteLLM's own GitHub CI/CD process. A developer running pip install saw a package name and version that looked official, while anyone comparing the source repository against the registry would have caught a release trail that didn't line up. That mismatch between source and registry became the signature of the campaign.

Microsoft's March 25 update summed up the broader objective in one line: harvest cloud credentials, Kubernetes secrets, database credentials, and CI/CD secrets, then exfiltrate them as encrypted archives. Microsoft also flagged the attacker's habit of naming domains after the legitimate tools they had compromised. The intent was to pass a quick visual check, so each phase looked close enough to the real project that a glance at a domain or commit message would not raise suspicion.

What makes CanisterWorm different?

CanisterWorm is the part of the campaign that runs without human attention. A single malicious package asks one question: who installs me? A worm asks a different one: who can I make publish me next?

Aikido detected the npm activity on March 20 at 20:45 UTC. Dozens of packages across multiple organizations had received unauthorized patch updates carrying the same hidden malicious code. The first wave hit scopes including @EmilGroup, @opengov, teale.io, @airtm, and @pypestream. Mend's later analysis put the count above fifty packages. The mechanism is npm lifecycle scripts. Install a package, the script runs. Most maintainers use those scripts legitimately. CanisterWorm used them to search for npm publishing credentials and then used whatever it found to publish malicious patch versions of every other package the token could reach.

The second twist was the command-and-control choice. Instead of phoning home to a single server that defenders could take down, the malware stored its next-stage location inside an Internet Computer Protocol canister, a piece of code running on a public blockchain. The implant asks the canister where to fetch the next instruction. Take down a domain and the campaign is still alive. Take down the canister and you are negotiating with a decentralized network instead of a hosting provider. Researchers describe the pattern as a dead drop. The takedown problem stops being a phone call to a registrar.

The worm rode the same rails as the legitimate ecosystem. Developers automate installs. Maintainers automate releases. CI/CD systems automate publishing. Registries automate distribution. CanisterWorm landed in the places where the system already trusted automation to act quickly, and it used valid publisher authority to do it. Once a stolen npm token could publish every package under a scope, the entire scope was a propagation point. The token was both authorization and distribution, which is the part most teams don't price into their threat model.

Why do supply chain attacks like this work?

The honest reason the TeamPCP campaign succeeded is that modern software delivery is built to let trusted systems speak quickly to other trusted systems, and most security programs are not built to defend the spaces between them.

Three trust assumptions broke at once during the Trivy incident. Version tags only mean what they say when the release system makes them hard to move, and mutable GitHub Action tags gave the attacker a way to redirect existing workflow references without changing a single line of YAML in a customer repo. Credential rotation only works when it closes the attacker's session across the entire trust path, and Aqua's first rotation was incomplete enough that the surviving access stayed productive for weeks. Security tools carry privileged trust because their value depends on it, and the same access that makes a scanner useful is what makes it dangerous once its release path is owned.

Attribution is the other place this story breaks the usual playbook. TeamPCP shows up in public reporting alongside aliases like PCPcat, ShellForce, DeadCatx3, CipherForce, and PersyPCP, plus CanisterWorm as a campaign or malware label. S2W's threat profile reads the cluster as a rebranded or reorganized identity tied to underground forums, Telegram channels, stolen data sales, and a ransomware-as-a-service operation under the CipherForce name. ShinyHunters, the crew that posted the Commission data, looks like a different operator working in the same cybercrime market. The leak site, the worm, the scanner compromise, and the cloud breach do not need the same hands. They need the same market.

That market is the adversary worth modeling. Initial access brokers sell footholds to ransomware groups, malicious packages hunt for secrets, stolen publisher tokens turn npm scopes into distribution surfaces, and compromised cloud keys open data-bearing infrastructure. Europol's 2025 Internet Organised Crime Threat Assessment frames it the same way, with stolen data and access treated as commodities moving through a criminal ecosystem that spans fraud, ransomware, and extortion. A single group name can attach itself to several of those roles without performing all of them, which is why public attribution stays messy and why behavior is more reliable evidence than branding.

The defender's job inside that market is to make trust relationships harder to convert into distribution. Naming every operator helps. It is not where the leverage lives.

What should security teams actually do?

The TeamPCP playbook still works because the controls that would break it cost something to deploy, and most organizations have not paid that cost yet. None of the work below is exotic.

Pin what executes. GitHub's own guidance is to pin Actions to full-length commit SHAs rather than version tags, because SHAs are immutable and tags can be moved. The Trivy attackers moved tags. SHAs would have turned a silent upstream change into a change-management problem, which is the right place for it to surface.

Shrink the blast radius of secrets. Long-lived publishing tokens, broad cloud keys, and reusable service credentials are the fuel that lets a single compromise spread for weeks. AWS's Well-Architected guidance favors temporary credentials, least privilege, network-source limits, and managed secret storage where long-lived credentials are unavoidable. npm's trusted publishing now supports OIDC-based publishing from CI/CD providers, which removes long-lived npm tokens from supported workflows and adds automatic provenance attestations. Those changes specifically attack the CanisterWorm loop.

Verify the release story, not just the artifact. The LiteLLM compromise was visible the moment anyone compared the registry version to the source repository's release process. They did not match. GitHub artifact attestations, SLSA-style provenance, and npm provenance give teams a verifiable link between what shipped and how it was built. The control to add is "does the registry version match the build it claims to come from."

Treat security tools as production software. Scanners, code-quality tools, IaC checkers, and AI gateway libraries usually run with broad read access because the alternative breaks them. That access is the threat model. A pull-request scan should not have production secrets sitting next to it. A scanner job that needs cloud access should use narrow roles, short sessions, and a separate account boundary where practical.

Watch behavior, not just artifacts. CERT-EU caught the European Commission incident through behavioral alerts: Amazon API misuse, abnormal traffic, unfamiliar access keys. Those signals fire even when the credential being used is technically valid. Pair them with release-side telemetry: patch versions outside normal release windows, tag movements, registry publishes from unexpected identities, and scanner jobs reaching unfamiliar infrastructure.

The TeamPCP story starts with a build doing exactly what it was told to do. The work ahead is making "exactly what it was told" something defenders can actually verify.