Ad
Threats

Trivy scanner breached, 75 GitHub Actions tags poisoned, npm worm spawned in 24-day attack

Artem Safonov
By Artem Safonov , Threat Analyst
Trivy scanner breached, 75 GitHub Actions tags poisoned, npm worm spawned in 24-day attack
Cover © Anonhaven

TeamPCP breached Aqua Security's Trivy vulnerability scanner on March 19, 2026. The 24-day campaign hit 75 GitHub Actions tags, over 60 npm packages, and Docker Hub images. It started with an AI bot exploiting a misconfigured workflow and ended with a self-propagating npm worm, decentralized C2, and an Iran-targeted wiper.

An automated GitHub bot named hackerbot-claw opened PR #10252 against the Trivy repository on February 27. The PR was closed immediately, but closure did not matter. Trivy's "API Diff Check" workflow used the pull_request_target event trigger, which runs with the base repository's secrets while executing code from the attacker's fork. The bot exfiltrated an organization-scoped Personal Access Token (ORG_REPO_TOKEN) belonging to the aqua-bot service account to recv.hackmoltrepeat[.]com.

That single token was used across at least 33 Aqua workflows. By February 28, the attacker had privatized the Trivy repo and deleted all 178 releases. A malicious VS Code extension appeared on Open VSX. Aqua rotated credentials, but the process was not atomic.

This was a follow up from the recent incident (2026-03-01) which exfiltrated credentials. Our containment of the first incident was incomplete. We rotated secrets and tokens, but the process wasn't atomic and attackers may have been privy to refreshed tokens.

— Aqua Security, GitHub Discussion #10425

Twenty days later, TeamPCP returned. On March 19 at 17:43 UTC, the aqua-bot account pushed a malicious v0.69.4 tag to Trivy, triggering the automated release pipeline. The poisoned binary shipped through GitHub Releases, GHCR, Docker Hub, ECR Public, deb/rpm repositories, and get.trivy.dev before removal around 21:36 UTC, a roughly three-hour window.

The technique was forensically precise. Commit 1885610c swapped the actions/checkout reference to an imposter commit (70379aad) containing a composite action. It downloaded malicious Go source files from a typosquatted domain, scan.aquasecurtiy[.]org (transposed letters in "security"), resolving to IP 45.148.10.212. The attacker added --skip=validate to goreleaser to bypass binary validation. Users saw normal Trivy scan output while the payload ran alongside it.

Tag poisoning delivered the wider blast radius. Within hours, aqua-bot force-pushed 75 of 76 trivy-action tags and all seven setup-trivy tags to malicious commits. Over 10,000 GitHub workflows referenced trivy-action. Any workflow pinned to a version tag silently resolved to the attacker's code without any file change on the consumer's side.

The imposter commits cloned original author metadata, timestamps, and messages. Socket researchers flagged three detection signals. GPG signatures were missing on commits originally web-signed. Chronology was impossible (commits dated 2021 with parents from March 2026). Single-file changes appeared where originals touched multiple files. The attacker spoofed "rauchg" (Guillermo Rauch, Vercel CEO) for the checkout injection and "DmitriyLewen" (a real Aqua maintainer) for the Trivy commit.

StepSecurity confirmed the poisoned Actions tags were live for roughly 12 hours. Their Harden-Runner tool flagged outbound C2 connections across 12,000+ public repositories and notified affected projects directly. A scan of 767 repositories confirmed 45 had executed compromised versions during the window.

Python comment in the malicious entrypoint.sh identifying the payload as "TeamPCP Cloud stealer.

The malicious entrypoint.sh contained 204 lines self-labeled "TeamPCP Cloud stealer." On GitHub-hosted runners, it escalated to root via passwordless sudo, located Runner.Worker processes, and scraped their memory through /proc/<pid>/mem. It searched for the JSON pattern {"value":"<secret>","isSecret":true}, the structure where GitHub Actions stores masked secrets at runtime. It also swept 50+ filesystem paths for SSH keys, AWS/GCP/Azure credentials, Kubernetes tokens, Docker configs, Solana validator keypairs, and cryptocurrency wallets.

Stolen secrets were encrypted with AES-256-CBC and RSA-4096 hybrid encryption, bundled as tpcp.tar.gz. Primary exfiltration went via HTTPS POST to the typosquatted domain. If a GitHub PAT was available, the malware created a public tpcp-docs repository on the victim's account and uploaded the bundle as a release asset. As of March 23, repositories named tpcp-docs belonging to kaufmann-digital, BEUMERGroupBot, cloud-team-si-it, and dhoppe (all created March 22) confirm successful theft from real CI/CD environments.

Developer machines got a different implant. The poisoned Trivy binary installed a persistent backdoor as a systemd user service at ~/.config/systemd/user/sysmon.py. It polled an ICP canister for arbitrary payloads every five minutes.

Stolen npm tokens from CI runners were weaponized less than 24 hours later. On March 20 at 20:45 UTC, TeamPCP deployed what Aikido Security called CanisterWorm. It harvested npm tokens from ~/.npmrc, project configs, and environment variables. It authenticated, enumerated publishable packages, bumped patch versions, and published with --access public. Twenty-eight @emilgroup packages fell in under 60 seconds. The worm spread to @opengov (16 packages), @teale.io/eslint-config, @airtm/uuid-base32, and @pypestream/floating-ui-dom.

By March 23, 141 malicious artifacts across 66+ packages had been identified.

This is the point where the attack goes from 'compromised account publishes malware' to 'malware compromises more accounts and publishes itself.' Every developer or CI pipeline that installs this package and has an npm token accessible becomes an unwitting propagation vector.

— Charlie Eriksen, security researcher, Aikido Security

Each infected package ran a three-stage chain. A Node.js postinstall loader decoded a base64 Python script, wrote it to ~/.local/share/pgmon/service.py, and installed it as a systemd user service masquerading as PostgreSQL monitoring. The Python backdoor slept five minutes (sandbox evasion), then polled ICP canister tdtqy-oyaaa-aaaae-af2dq-cai every 50 minutes for a URL pointing to the current payload. A kill switch existed: if the URL contained "youtube.com", the implant went dormant.

On March 22, TeamPCP rotated through six payload versions in roughly five hours via the ICP canister. Version 1 (around 11:45 UTC) was a monolithic Kubernetes DaemonSet with host escape, systemd persistence, and an Iran-targeted wiper. The wiper detected Asia/Tehran timezone or fa_IR locale and executed rm -rf / --no-preserve-root.

This is the point where the attack goes from compromised account publishes malware to malware compromises more accounts and publishes itself. Every developer or CI pipeline that installs this package and has an npm token accessible becomes an unwitting propagation vector

— Charlie Eriksen, Aikido Security.

Subsequent versions went modular. A pure SSH/Docker network worm scanned local /24 for ports 22 and 2375. One variant used WAV steganography, hiding payloads inside audio files. By 16:00 UTC, TeamPCP pushed malicious Docker Hub images (v0.69.5, v0.69.6) directly, bypassing GitHub entirely.

At 20:31 UTC, 44 internal Aqua Security repositories in the aquasec-com organization were defaced. The Argon-DevOps-Mgt service account, a single bot bridging two orgs with a long-lived PAT, was the entry point. Repositories were renamed with a tpcp-docs- prefix and descriptions changed to "TeamPCP Owns Aqua Security." Wiz confirmed the attacker had also exfiltrated GPG keys and credentials for Docker Hub, Twitter, and Slack to a Cloudflare Tunnel C2 at plug-tab-protective-relay.trycloudflare[.]com.

The ICP canister was denylisted at 21:31 UTC on March 22.

TeamPCP is also tracked as DeadCatx3, PCPcat, ShellForce, and CipherForce. Socket noted the self-labeling makes attribution straightforward, though it could be a false flag. The Iran-targeted wiper does not match the group's known financial motives. Whether it reflects TeamPCP's own agenda, a paid job, or a planted distraction is unknown.

Trivy has over 145,000 GitHub stars. The trivy-action was referenced by over 10,000 workflows. Git tags are mutable references that any user with write access can force-push. The attacker exploited this by publishing immutable releases from poisoned tags, locking the malicious state and blocking maintainers from restoring originals.

In March 2025, the tj-actions/changed-files compromise (CVE-2025-30066) exposed secrets from 218 repositories via identical tag poisoning. The Trivy incident is larger. It combines binary tampering, Actions tag hijacking, a cross-ecosystem worm, decentralized C2, rapid payload rotation, and a geopolitically targeted wiper.

If you suspect you were running a compromised version, treat all pipeline secrets as compromised and rotate immediately.

— Itay Shakury, VP of Open Source, Aqua Security

Trivy v0.69.3 or earlier is safe. For Actions, use trivy-action v0.35.0 (the only tag not force-pushed) or setup-trivy v0.2.6 (published during response by maintainer simar7 at 21:43 UTC March 19). Aqua's advisory is at Discussion #10425. GHSA-69fq-xp46-6x23 was published. No CVE has been assigned as of March 23.

Organizations that used Trivy, trivy-action, or setup-trivy between March 19 and 22, 2026, should assume all accessible secrets have been stolen. Rotate GitHub tokens, cloud provider credentials (AWS/GCP/Azure), SSH keys, Docker registry tokens, Kubernetes service account tokens, and npm publish tokens. Search your GitHub organizations for repositories named tpcp-docs. Their existence confirms successful exfiltration.

Wiz researcher Rami McCarthy recommended pinning all GitHub Actions to full 40-character commit SHAs, not version tags. The syntax uses: aquasecurity/trivy-action@<sha> is immutable. The syntax uses: aquasecurity/trivy-action@0.34.0 is not. One misconfigured pull_request_target workflow launched a chain across Trivy's binary pipeline, 75 Actions tags, 66+ npm packages, Docker Hub, and 44 internal repos. TeamPCP iterated six payloads in five hours, faster than most teams can start an incident response call.

Have a story? Become a contributor.

We work with independent researchers and cybersecurity professionals. Send us a tip or submit your article for editorial review.

Questions on the topic

Was Trivy vulnerability scanner compromised in March 2026?
Yes. TeamPCP compromised Trivy between March 19-22, 2026, poisoning 75 GitHub Actions tags and distributing a credential stealer through the official release pipeline. Over 10,000 workflows were affected. Pin Actions to full commit SHAs and rotate all CI/CD secrets.