Contents
ToggleExecutive Summary
Between March 19 threat actor TeamPCP compromised Aqua Security’s and March 23 2026 (disclosed by Paul McCarty). The threat actor TeamPCP executed the most consequential CI/CD supply chain attack documented to date. Starting from a single incompletely-rotated GitHub personal access token, the campaign cascaded across two major open-source security vendors (Aqua Security and Checkmarx), four GitHub Actions repositories, two OpenVSX extensions, container registries (Docker Hub, GHCR, ECR), and over 66 npm packages.
We did cover the attacks in previous articles, as the situation is evolving. The first article covered the initial attack, a second one was a deep dive into the Trivy compromise, and this one covers more techniques and the new Kicks attack.
The attacker force-pushed 110+ version tags across aquasecurity/trivy-action, aquasecurity/setup-trivy, Checkmarx/kics-github-action, and Checkmarx/ast-github-action, replacing them with malicious commits containing a three-stage credential stealer self-identified as the “TeamPCP Cloud stealer.” The stealer dumps Runner.Worker process memory, sweeps 50+ credential paths on self-hosted runners, encrypts harvested data with AES-256-CBC and RSA-4096, and exfiltrates via vendor-specific typosquat domains designed to blend into CI/CD log output.
Stolen npm tokens then fuelled CanisterWorm, the first publicly documented npm malware to use an Internet Computer Protocol (ICP) blockchain canister as a command-and-control dead-drop, making conventional domain takedown impossible. A destructive variant specifically targeting Iranian Kubernetes clusters was also detected.
The campaign had no CVE, no CVSS score, and no NVD entry. Traditional scanner-based vulnerability management workflows would not have caught it.

TL;DR for Engineering Teams
What it is: Multi-vendor supply chain compromise of Trivy and Checkmarx GitHub Actions, OpenVSX extensions, container images, and 66+ npm packages, injecting a three-stage credential stealer attributed to TeamPCP. No CVE assigned for the March 2026 wave.
Where it bites: Any CI/CD pipeline referencing aquasecurity/trivy-action (75 of 76 tags), aquasecurity/setup-trivy (7 tags), Checkmarx/kics-github-action (all 35 tags), Checkmarx/ast-github-action (confirmed: v2.3.28), Trivy binary v0.69.4–0.69.6, OpenVSX extensions ast-results v2.53.0 and cx-dev-assist v1.7.0, or any CanisterWorm-affected npm package.
Why it matters: Payload executes before the legitimate scan, so pipelines appear normal. The stealer dumps Runner.Worker process memory, harvests SSH keys, cloud credentials, K8s tokens, crypto wallets, and npm tokens. Stolen npm tokens enabled a self-propagating worm using blockchain-based C2 that cannot be taken down via conventional means. 10,000+ workflows affected across both vendors.
Patch status: Trivy: malicious artifacts removed, tags restored with v-prefix, immutable releases enabled. Safe versions: trivy v0.69.3, trivy-action v0.35.0 (SHA 57a97c7e), setup-trivy v0.2.6. Checkmarx: kics-github-action repository reinstated and declared resolved. OpenVSX: extensions reported for removal.
Immediate action: Pin all affected actions to verified SHA commits. Rotate every secret accessible to CI runners during the exposure windows (Trivy: March 19 17:43–March 20 05:40 UTC; KICS: March 23 12:58–16:50 UTC). Block scan.aquasecurtiy[.]org, checkmarx[.]zone, 45.148.10.212, 83.142.209.11. Search GitHub orgs for tpcp-docs or docs-tpcp repositories. Audit npm packages for CanisterWorm IOCs.
Vulnerability Overview
| Field | Value |
| Vendors | Aqua Security, Checkmarx |
| Products | Trivy, trivy-action, setup-trivy, KICS, kics-github-action, ast-github-action, ast-results (OpenVSX), cx-dev-assist (OpenVSX) |
| Vulnerability Type | Supply chain compromise / credential theft / self-propagating worm |
| CWE | CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) |
| CVE | No CVE assigned for March 2026 wave; GHSA-69fq-xp46-6×23 covers Trivy ecosystem |
| Attack Vector | Network (CI/CD pipeline execution, IDE extension activation) |
| Active Exploitation | Confirmed — multi-vendor, multi-ecosystem |
| Attribution | TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce, CipherForce) |
| Trivy Exposure Window | ~12 hours (March 19 17:43 UTC to March 20 ~05:40 UTC) |
| KICS Exposure Window | ~4 hours (March 23 12:58 UTC to ~16:50 UTC) |
Technical Anatomy

Phase 1: Initial Access via GitHub Actions Misconfiguration (Late February 2026)
The campaign’s origins trace to late February 2026 when the hackerbot-claw account (created February 20) began scanning public repositories for exploitable GitHub Actions workflows. The account’s approach was methodical: it targeted repositories with pull_request_target triggers that checked out attacker-controlled code, a well-documented class of misconfiguration known as a “PWN request.”
The attacker exploited multiple techniques across different targets. Against one Microsoft repository, the payload was embedded in the Git branch name itself, using shell expansion ($(…)) to achieve command injection when the branch name was echoed in a workflow step. Against a Datadog repository, the same shell expansion was delivered via a malicious markdown filename that was evaluated during a file-listing step. Against a repository running a Claude-based AI code review bot, the attacker attempted prompt injection to coerce the model into executing arbitrary code. Claude detected and refused both attempts, but the non-deterministic nature of LLM evaluation means repeated attempts could eventually succeed.
The attack that yielded the most impact targeted Aqua Security’s Trivy repository. The hackerbot-claw account forked the Trivy repo and opened a pull request that looked legitimate but included a curl command piped to bash, which ran within the GitHub Action runner’s context. This extracted a GitHub personal access token (PAT) bound to the entire aquasecurity GitHub organization, granting write access to all 33+ repositories.
With org-wide access, the attacker immediately defaced the Trivy repository README, deleted all releases, renamed the repo, and pushed a malicious VS Code extension to OpenVSX. Aqua Security disclosed the breach on March 1 and began rotating credentials. The rotation was not atomic. As tokens were being cycled, the attacker captured refreshed credentials before the old access was fully revoked, setting the stage for the March 19 attack.
Phase 2: The February VS Code Extension Attack — Weaponising Local Coding Agents
An overlooked element of the February breach was the malicious VS Code extension pushed to OpenVSX, which contained a payload specifically designed to exploit local AI coding assistants. The extension scanned for five installed coding agents (Claude Code, Google Gemini, GitHub Copilot, Codex, and others) and attempted to invoke them in their most permissive modes: –dangerously-skip-permissions for Claude, –yolo for Gemini/Copilot, and –ask-for-approval never for Codex.
The embedded prompt, over 2,000 words long, impersonated a forensic analyst and instructed the agent to scan the local system for credentials, financial data, cloud tokens, and security configurations. The initial version attempted multi-channel exfiltration (email, encrypted portals, written reports). A second iteration specifically directed the agent to use the GitHub CLI or API to upload findings to a public repository. While no confirmed successful exfiltration was documented, the technique represents a new attack surface: weaponising developer productivity tools as credential harvesters.
Phase 3: The Trivy Tag-Poisoning Attack (March 19, 2026)
At 17:43 UTC on March 19, TeamPCP leveraged retained credentials to execute the main strike. Using the compromised aqua-bot service account, the attacker force-pushed 75 of 76 version tags in aquasecurity/trivy-action and all 7 tags in aquasecurity/setup-trivy to malicious imposter commits. Simultaneously, a backdoored Trivy binary was published as v0.69.4 to GitHub Releases, Docker Hub, GHCR, and Amazon ECR.
The tag-poisoning technique was technically elegant. Each forged commit cloned the original commit’s metadata (author, timestamp, commit message) while replacing entrypoint.sh with the stealer payload and setting the commit’s parent to master HEAD. This produced commits that looked nearly identical on GitHub’s release page, including the “Immutable” badge. The sole telltale indicator was the “0 commits to master since this release” counter, which displayed zero even for tags dating to 2020–2024 that should have shown hundreds of commits.
The single surviving clean tag was 0.35.0, protected because GitHub’s immutable releases feature had been enabled on March 4. The attacker likely skipped it because replacing it would have produced a commit whose parent was itself, creating a visibly anomalous release.
The Three-Stage Credential Stealer (entrypoint.sh, 204 lines)
The malicious entrypoint.sh (SHA-256: 18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a) was a 204-line bash script. Lines 4–105 contained the stealer. Lines 106–204 ran the legitimate Trivy scan, ensuring pipeline output looked clean and no build failures alerted operators.
Stage 1 — Collection: On GitHub-hosted Linux runners, the malware dumped Runner.Worker process memory via /proc/[pid]/mem, searching for the JSON pattern {“value”:”<secret>”,”isSecret”:true} stored in the .NET process heap. On self-hosted runners, a comprehensive Python credential harvester swept 50+ paths across SSH keys, Git tokens, AWS/GCP/Azure credentials (including EC2 IMDS at 169.254.169.254), Kubernetes configs and service account tokens, Docker registry creds, .env files, database credentials, CI/CD configs (Terraform state, Jenkinsfile, .gitlab-ci.yml), TLS private keys, WireGuard configs, shell history (which often contains exported secrets), and even cryptocurrency wallets (Bitcoin, Ethereum, Solana keypairs, Ledger files).
Stage 2 — Encryption: Harvested data was encrypted with AES-256-CBC (key derived via PBKDF2 from 32 random bytes), with the session key wrapped by a hardcoded RSA-4096 public key using OAEP padding. The bundle was packaged as tpcp.tar.gz. Only the RSA private key holder can decrypt.
Stage 3 — Exfiltration: Primary: HTTPS POST to scan.aquasecurtiy[.]org (45.148.10.212, Amsterdam, TECHOFF SRV LIMITED) with header X-Filename: tpcp.tar.gz. Fallback: if the C2 was unreachable and INPUT_GITHUB_PAT was available, the stealer created a public repository named tpcp-docs under the victim’s GitHub account and uploaded the bundle as a release asset. Data exfiltrated over GitHub’s own infrastructure is unlikely to be blocked by corporate firewalls.
Phase 4: The Checkmarx Wave (March 23, 2026)
Four days after the Trivy compromise, TeamPCP pivoted to Checkmarx’s ecosystem. Between 12:58 and 16:50 UTC on March 23, all 35 tags of Checkmarx/kics-github-action were hijacked using the compromised cx-plugins-releases service account (GitHub ID 225848595). The attacker staged malicious commits on a fork containing a setup.sh payload, then updated all tags to point to those commits.
The KICS payload shared the identical RSA public key as the Trivy stealer, providing high-confidence attribution. TeamPCP introduced a new C2 domain: checkmarx[.]zone (resolving to 83.142.209.11), replacing the Trivy-era typosquat. The fallback exfiltration repository was renamed from tpcp-docs to docs-tpcp. New capabilities included Kubernetes-focused persistence code not present in the Trivy variant.
At 22:25 UTC, Sysdig’s Threat Research Team reported that Checkmarx/ast-github-action version 2.3.28 was also compromised with the same payload. The process tree was functionally identical to the Trivy attack: Runner.Worker → bash → setup.sh → curl POST. Only the entry point script and exfiltration domain differed. The use of vendor-specific typosquat domains for each poisoned action is a deliberate deception: an analyst reviewing CI/CD logs would see curl traffic to what appears to be the action’s own vendor domain, reducing the likelihood of manual detection.
Phase 5: OpenVSX Extensions Deliver Persistent Backdoor (March 23, 2026)
Twelve seconds apart at 12:53 UTC on March 23, two malicious VS Code extensions were published to OpenVSX via the ast-phoenix account: ast-results v2.53.0 and cx-dev-assist v1.7.0. VS Code Marketplace versions appear unaffected.
On activation, a malicious environmentAuthChecker.js checked whether the victim had credentials for at least one cloud provider. If detected, a second-stage payload was fetched from checkmarx[.]zone/static/checkmarx-util-1.0.4.tgz and executed via npx, bunx, pnpx, or yarn dlx. On non-CI systems, the extension installed a systemd user service polling checkmarx[.]zone/raw every 50 minutes for additional payloads. The polling included a kill switch: if the response contained “youtube,” execution aborted. At the time of analysis, the kill-switch URL redirected to Queen’s “The Show Must Go On.”
Phase 6: CanisterWorm — Self-Propagating npm Worm with Blockchain C2
Using npm tokens harvested during the Trivy compromise, TeamPCP deployed CanisterWorm starting March 20 at 20:45 UTC. The worm was first detected by Aikido Security researcher Charlie Eriksen. It is the first publicly documented npm malware to use an ICP blockchain canister (tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io) as a C2 dead-drop, making conventional takedown impossible since the *.icp0.io gateway is shared across all ICP canisters.
The attack deployed in four escalating waves. Wave 1 hit 28 packages in the @emilgroup scope as a dry run. Wave 2 armed the ICP backdoor. Wave 3 introduced self-propagation via a findNpmTokens() function that scraped .npmrc files and environment variables, then used stolen tokens to bump patch versions and republish every package the victim maintained. Wave 4 combined self-propagation with the armed backdoor. The worm’s persistence mechanism disguised itself as a PostgreSQL monitoring service (pgmon.service), writing the backdoor to ~/.local/share/pgmon/service.py.
By March 22, the scale had expanded from the initial 47 packages (Aikido’s count) to 141 malicious artifacts across 66+ unique packages (Socket’s tracking). Compromised scopes included @emilgroup, @opengov, @teale.io, @airtm, and @pypestream.
Phase 7: The kamikaze.sh Script — Targeted Destruction and Subnet Scanning
On March 24, a new payload variant was detected: kamikaze.sh, a destructive script with geopolitical targeting. The script checks whether the runtime environment is Kubernetes and whether the system’s timezone is set to Asia/Tehran or the locale is Farsi. The logic branches four ways:
1. Kubernetes + Iran: wipe every node in the cluster. 2. Kubernetes + elsewhere: install the CanisterWorm backdoor on every node via a DaemonSet (host-provisioner-std). 3. Not Kubernetes + Iran: rm -rf / –no-preserve-root with sudo. 4. Not Kubernetes + not Iran: do nothing.
Once installed on a cluster, the worm drops a script that scans for SSH keys and Docker API tokens, then probes the local subnet for port 22 (SSH) and port 2375 (Docker API). If Docker API access is found, the worm creates a privileged container with the host root filesystem mounted, achieving full node compromise. This lateral movement capability transforms a single infected CI runner into a potential foothold across the entire internal network.
C2 Infrastructure: WAV Steganography and the Music Playlist
TeamPCP’s C2 infrastructure uses an ICP blockchain canister that points to either a binary payload URL or a YouTube link (the kill switch). The YouTube references evolved over the campaign: the initial C2 pointed to a Rick Roll, then shifted to Dido’s “Thank You,” then to King Gizzard and the Lizard Wizard. The Checkmarx wave’s kill switch pointed to Queen’s “The Show Must Go On.”
A later iteration embedded malware payloads inside WAV audio files using Base64 encoding within the file, which was decoded and executed on the target. This steganographic technique allows the C2 to serve what appears to be an audio file while delivering executable payloads, evading content-based filtering.
Affected Versions

Aqua Security / Trivy Ecosystem
| Component | Vulnerable | Safe Version | Notes |
| trivy-action | Tags 0.0.1–0.34.2 (75 of 76 tags) | v0.35.0 (SHA: 57a97c7e) | Force-pushed to malicious commits |
| setup-trivy | 7 tags compromised | v0.2.6 | Same stealer payload in action.yaml |
| trivy binary | v0.69.4, v0.69.5, v0.69.6 | v0.69.3 | Published to Docker Hub, GHCR, ECR |
| Homebrew | Picked up v0.69.4 | Rolled back to v0.69.3 | Built from source; binary OK after rollback |
| apt/rpm packages | Not affected | N/A | Not impacted |
Checkmarx Ecosystem
| Component | Vulnerable | Safe Version | Notes |
| kics-github-action | All 35 tags (v1 through v2.1.20) | Repository reinstated; verify SHA | Compromised via cx-plugins-releases service account |
| ast-github-action | v2.3.28 confirmed; all tags suspected | Verify SHA before use | Reported by Sysdig TRT |
| ast-results (OpenVSX) | v2.53.0 | Prior versions | Payload: environmentAuthChecker.js |
| cx-dev-assist (OpenVSX) | v1.7.0 | Prior versions | Published 12 seconds after ast-results |
npm / CanisterWorm
| Scope | Packages Affected | Status | Notes |
| @emilgroup | 28 packages | Wave 1 dry run; Wave 2+ armed | First scope targeted |
| @opengov | 16+ packages | Active | Includes form-renderer, other packages |
| @teale.io | eslint-config v1.8.11–1.8.12 | Self-propagating variant | First self-propagating payload |
| @airtm, @pypestream | Multiple packages | Active | Later wave expansion |
| Total tracked | 141 malicious artifacts / 66+ packages | Per Socket Security tracking | Aikido initial count: 47 |
Compromised kics-github-action Tags (All 35, source: Wiz)
| Tag | Commit SHA | Tag | Commit SHA |
| v1 | 0e22ec8d | v2.0.0 | 121c38fb |
| v1.0 | 45f37494 | v2.1.0 | 1e9eeaba |
| v1.1 | 8e20c7a6 | v2.1.1 | c5c07508 |
| v1.2 | 93de85c9 | v2.1.2 | c999dbb9 |
| v1.3 | 0e7343ba | v2.1.3 | 4ebf62dd |
| v1.4 | 2dc0fa61 | v2.1.4 | 3ae9f0d6 |
| v1.5 | f00191dd | v2.1.5 | 96a0e8eb |
| v1.6 | e0359b1a | v2.1.6 | 31fbf583 |
| v1.6.1 | dc6dbf35 | v2.1.7 | fca3a20a |
| v1.6.2 | 08b9ea97 | v2.1.8 | 0f81f132 |
| v1.6.3 | 005fb083 | v2.1.9 | c0e23718 |
| v1.7.0 | a5471d37 | v2.1.10 | d66f0657 |
| v2 | 3d49875e | v2.1.11–2.1.20 | See Wiz advisory |
Discovery and Disclosure Timeline

| Date | Event |
| October 2025 | Vulnerable pull_request_target workflow (apidiff.yaml) added to Trivy repo |
| November 29, 2025 | Boost Security’s poutine tool flags the workflow; no action taken |
| February 20, 2026 | hackerbot-claw GitHub account created |
| Late February 2026 | hackerbot-claw scans public repos, exploits multiple GitHub Actions (Microsoft, Datadog, Aqua Security); attempts prompt injection against Claude code review bot |
| February 28, 2026 | Trivy repository fully compromised: releases deleted, repo defaced, malicious VS Code extension pushed to OpenVSX targeting local AI coding agents |
| March 1, 2026 | Aqua Security discloses, begins credential rotation (non-atomic); releases v0.69.2 |
| March 3, 2026 | Trivy v0.69.3 released (current safe version) |
| March 4, 2026 | Immutable releases enabled on trivy-action (protects tag 0.35.0) |
| March 19, 17:43 UTC | TeamPCP pushes backdoored Trivy v0.69.4 to GitHub, Docker Hub, GHCR, ECR |
| March 19, ~19:00 UTC | 75 trivy-action + 7 setup-trivy tags force-pushed; Socket generates 182 threat feed entries |
| March 19, ~19:15 UTC | First detection by Paul McCarty |
| March 19–20 | CrowdStrike Falcon detects malicious script execution on customer runners |
| March 20, ~05:40 UTC | Malicious Trivy artifacts removed by Aqua Security |
| March 20 | Tags restored with v-prefix convention; Docker images v0.69.5/v0.69.6 also found poisoned; 44 repos in aquasec-com org defaced |
| March 20, 20:45 UTC | CanisterWorm first detected by Aikido Security researcher Charlie Eriksen |
| March 20–22 | CanisterWorm scales from 47 to 141 artifacts across 66+ npm packages |
| March 23, 12:53 UTC | Malicious OpenVSX extensions ast-results v2.53.0 and cx-dev-assist v1.7.0 published via ast-phoenix account |
| March 23, 12:58–16:50 UTC | All 35 kics-github-action tags compromised via cx-plugins-releases service account |
| March 23, 16:50 UTC | KICS repository taken down after community GitHub issue filed |
| March 23, 19:24 UTC | KICS repository reinstated; Checkmarx states “The issue is resolved now” |
| March 23, 22:25 UTC | Sysdig TRT reports ast-github-action v2.3.28 also compromised |
| March 23, 22:35 UTC | Wiz confirms OpenVSX extension compromise; ReversingLabs concurrently reports same finding |
| March 24, 2026 | kamikaze.sh variant detected with Iran-targeting logic and Kubernetes cluster destruction; WAV steganography payload delivery observed |
Determine If You’re Affected
Exposure by Environment
| Environment | Risk Level | Reason |
| CI/CD pipelines referencing Trivy by tag | Critical | Direct credential theft from Runner.Worker memory; ~12-hour window |
| CI/CD pipelines referencing KICS/AST by tag | Critical | Same stealer payload; ~4-hour window; new C2 domain |
| Self-hosted runners (any affected action) | Critical | 50+ credential paths swept; SSH, cloud, K8s, crypto wallets; subnet scanning |
| Developers with compromised OpenVSX extensions | High | Persistent systemd backdoor polling C2 every 50 minutes |
| npm consumers of affected packages | High | CanisterWorm self-propagation; ICP blockchain C2 resistant to takedown |
| Docker builds using trivy:0.69.4–0.69.6 | High | Backdoored binary with ICP canister C2 polling |
| Developers who installed Feb 2026 VS Code ext | Medium | Malicious prompts targeting local coding agents (Claude, Gemini, Copilot) |
| Pipelines pinned to full commit SHA | Not affected | Immutable reference; not vulnerable to tag repointing |
Verification Steps
1. Search all workflow files for affected action references: grep -r “aquasecurity/trivy-action\|aquasecurity/setup-trivy\|Checkmarx/kics-github-action\|Checkmarx/ast-github-action” .github/workflows/
2. Determine whether any workflow ran a version-tag reference (not SHA) during either exposure window.
3. Check self-hosted runners for persistence indicators: ~/.config/sysmon.py, ~/.local/share/pgmon/service.py, pgmon.service, /tmp/pglog.
4. Search your GitHub organisation for repositories named tpcp-docs or docs-tpcp (indicates successful exfiltration).
5. Audit npm packages your organisation publishes for unexpected patch version bumps between March 20–24.
6. Check OpenVSX extension versions: ast-results should not be v2.53.0; cx-dev-assist should not be v1.7.0.
7. Run StepSecurity trivy-compromise-scanner for automated workflow log auditing.
8. Scan with Zizmor (open-source GitHub Actions security scanner) for pull_request_target misconfigurations in your own repositories.
Detection Guidance
Indicators of Compromise
| Type | Indicator | Context |
| Exfiltration domain | scan.aquasecurtiy[.]org | Trivy C2 (typosquat of aquasecurity) |
| Exfiltration IP | 45.148.10.212 | Amsterdam, TECHOFF SRV LIMITED |
| Exfiltration domain | checkmarx[.]zone | Checkmarx/KICS/OpenVSX C2 (typosquat of checkmarx.com) |
| Exfiltration IP | 83.142.209.11 | Checkmarx wave C2 |
| Cloudflare Tunnel | plug-tab-protective-relay.trycloudflare.com | Additional credential exfil channel |
| ICP Canister | tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io | Blockchain-based C2 dead-drop (CanisterWorm + Trivy binary) |
| Initial C2 | recv.hackmoltrepeat.com | hackerbot-claw initial breach (February) |
| Exfil header | X-Filename: tpcp.tar.gz | Present in all stealer curl POST requests |
| Fallback repos | tpcp-docs / docs-tpcp | Created in victim GitHub accounts if C2 unreachable |
| Persistence (Trivy) | ~/.config/sysmon.py | Trivy binary persistence script |
| Persistence (CanisterWorm) | ~/.local/share/pgmon/service.py + pgmon.service | systemd user service masquerading as PostgreSQL monitor |
| Payload file | /tmp/pglog | Downloaded binary payload from ICP canister |
| Malware self-ID | TeamPCP Cloud stealer | String in stealer output |
File Hashes (SHA-256)
| Hash | Artifact |
| 18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a | Malicious entrypoint.sh (Trivy stealer, 17,592 bytes) |
| 07500e81693c06ef7ac6bf210cff9c882bcc11db5f16b5bded161218353ba4da | Legitimate entrypoint.sh (2,855 bytes) |
| 65bd72fcddaf938cefdf55b3323ad29f649a65d4ddd6aea09afa974dfc7f105d | ast-results-2.53.0.vsix (malicious OpenVSX extension) |
| 744c9d61b66bcd2bb5474d9afeee6c00bb7e0cd32535781da188b80eb59383e0 | cx-dev-assist-1.7.0.vsix (malicious OpenVSX extension) |
| 0d66d8c7e02574ff0d3443de0585af19c903d12466d88573ed82ec788655975c | checkmarx-util-1.0.4.tgz (second-stage payload) |
| 527f795a201a6bc114394c4cfd1c74dce97381989f51a4661aafbc93a4439e90 | environmentAuthChecker.js (OpenVSX trigger script) |
| e9b1e069efc778c1e77fb3f5fcc3bd3580bbc810604cbf4347897ddb4b8c163b | CanisterWorm Wave 1 index.js (dry run) |
| 61ff00a81b19624adaad425b9129ba2f312f4ab76fb5ddc2c628a5037d31a4ba | CanisterWorm Wave 2 index.js (armed ICP backdoor) |
| 0c0d206d5e68c0cf64d57ffa8bc5b1dad54f2dda52f24e96e02e237498cb9c3a | CanisterWorm Wave 3 index.js (self-propagating) |
| c37c0ae9641d2e5329fcdee847a756bf1140fdb7f0b7c78a40fdc39055e7d926 | CanisterWorm Wave 4 index.js (final form) |
MITRE ATT&CK Mapping
| Technique | Description | Campaign Application |
| T1195.002 | Supply Chain Compromise: Software Supply Chain | Poisoned GitHub Actions, container images, npm packages, VS Code extensions |
| T1003 | OS Credential Dumping | /proc/[pid]/mem reading for Runner.Worker secrets |
| T1552.004 | Unsecured Credentials: Private Keys | SSH keys, TLS certs, cloud service principal files |
| T1552.005 | Cloud Instance Metadata API | IMDS queries at 169.254.169.254 for IAM credentials |
| T1543.002 | Create/Modify System Process: Systemd Service | pgmon.service persistence (CanisterWorm), host-provisioner-std DaemonSet |
| T1048.002 | Exfiltration Over Alternative Protocol | Encrypted bundle to typosquat domains; GitHub repo fallback |
| T1132 | Data Encoding | AES-256-CBC + RSA-4096 wrapping; Base64 in WAV steganography |
| T1036 | Masquerading | Imposter commits with cloned metadata; PG service name disguise |
| T1046 | Network Service Discovery | Subnet scanning for port 22 (SSH) and 2375 (Docker API) |
| T1021.004 | Remote Services: SSH | Using harvested SSH keys to propagate to local subnet hosts |
Runtime Detection Rules
Sysdig published Falco rules that detect the stealer’s core behaviours: “Contact EC2 Instance Metadata Service From Container” (catches IMDS credential theft), “Curl Exfiltrating File” (catches the encrypted bundle upload regardless of destination domain), “Exfiltration of AWS IMDS Credentials Using LOTL Binary” (correlates IMDS access with subsequent data upload — the highest-signal rule), and “Dump Memory using /proc/ Filesystem” (catches the Runner.Worker memory scanning technique).
StepSecurity Harden-Runner provides network anomaly detection that builds per-job baselines and alerts on deviation, egress domain allowlisting, and a Compromised Actions Run Policy that blocks known-malicious actions. StepSecurity also released trivy-compromise-scanner for automated log auditing across organisations.
Remediation and Temporary Protections
Immediate Actions
1. Pin trivy-action to SHA: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1
2. Pin setup-trivy to safe version v0.2.6.
3. Verify kics-github-action and ast-github-action references against known-good commit SHAs before re-enabling.
4. Rotate every pipeline secret (GitHub PATs, AWS/GCP/Azure credentials, SSH keys, npm tokens, Docker registry tokens, Kubernetes service account tokens) if any workflow ran a compromised tag during either exposure window.
5. Block the following at your network perimeter and DNS: scan.aquasecurtiy[.]org, checkmarx[.]zone, 45.148.10.212, 83.142.209.11, plug-tab-protective-relay.trycloudflare.com.
6. Search your GitHub organisation for repositories named tpcp-docs or docs-tpcp.
7. Audit self-hosted runners for persistence: ~/.config/sysmon.py, ~/.local/share/pgmon/, pgmon.service, /tmp/pglog.
8. Uninstall ast-results v2.53.0 and cx-dev-assist v1.7.0 from any developer machines using OpenVSX.
9. Check npm packages your organisation publishes for unexpected patch bumps between March 20–24.
Long-Term Hardening
Pin all GitHub Actions to full SHA commit hashes. Tools: Zizmor (open-source GitHub Actions scanner by Will Woodruff), pinact, StepSecurity secure-repo, Renovate with pinGitHubActionDigests. SHA pinning does not protect against transitive dependencies — if your action calls another action by tag, that second-degree dependency remains vulnerable.
Audit all pull_request_target workflows. If any step checks out the PR’s head code, it is exploitable via PWN request. Use poutine (Boost Security) for automated scanning.
Minimise token permissions. The Trivy breach was amplified because the PAT was scoped to the entire organisation. Prefer read-only tokens with the narrowest possible scope.
Enable immutable releases on repositories publishing GitHub Actions. This is the single control that saved trivy-action tag 0.35.0.
Require GPG-signed commits for release tags. The malicious commits were unsigned; signed-commit enforcement would have prevented the tag-poisoning.
Restrict IMDS access from CI runner containers using IMDSv2 with hop limits, or disable IMDS entirely where cloud credentials are not needed.
Monitor outbound network connections from CI runners for curl POST requests to domains that do not match expected artifact repositories.
Wait before adopting new package versions. Community detection of malicious code injections typically occurs within days. A one-week delay for non-critical updates provides a meaningful detection window.
How Threat Actors Could Use This Attack Pattern
The TeamPCP campaign is a masterclass in cascading supply chain exploitation, and the techniques are now publicly documented for any motivated threat actor to replicate.
GitHub Actions tag-poisoning is trivial once you have a PAT with write access to a repository. Tags are mutable by default in Git — most organisations protect branches but not tags. A single compromised token scoped to an organisation (rather than a repository) grants access to every repo under that org, as happened with Aqua Security.
The imposter commit technique — cloning the original commit’s metadata while replacing the payload — produces releases that look identical on GitHub’s UI. The only detectable anomalies (unsigned commits, impossible parent timestamps, “0 commits since release”) require manual inspection or purpose-built automation to catch.
The VS Code extension attack targeting local AI coding agents represents a new attack surface. As more developers adopt Claude Code, Copilot, and similar tools with broad filesystem access, malicious prompts embedded in extensions or dependencies can coerce these agents into harvesting credentials and exfiltrating them. The attack is especially effective in permissive modes (–dangerously-skip-permissions, –yolo) that developers use during active coding sessions.
The ICP blockchain C2 sets a precedent. Because ICP canisters run on decentralised infrastructure, no single domain registrar or hosting provider can issue a takedown. The *.icp0.io gateway is shared across all canisters, making IP-based blocking impractical without collateral damage. Future supply chain attackers will adopt blockchain-based C2 infrastructure as a matter of course.
The self-propagating npm worm pattern — harvest tokens, enumerate publishable packages, bump patch version, inject payload, republish — can be applied to any package ecosystem where tokens are stored in environment variables or local config files. PyPI, RubyGems, and Maven Central are all susceptible to the same technique if a maintainer’s token is compromised.
Phoenix Security Recommendations
The TeamPCP campaign demonstrates why vulnerability management cannot stop at scanning known CVEs. This attack had no CVE, no CVSS score, and no NVD entry. It targeted the security tooling itself. Traditional scanner-based workflows would not have flagged it.
Attack surface management: Phoenix identifies CI/CD pipelines running compromised components and maps whether references use mutable version tags or immutable SHA commits. For organisations running Trivy or Checkmarx actions across hundreds of repositories, Phoenix provides a single view of which pipelines referenced compromised tags during the exposure windows.
Contextual deduplication: Organisations using Trivy or KICS across dozens of repositories get a single prioritised backlog rather than duplicate alerts per repository. Phoenix correlates findings across scanners, environments, and teams to eliminate noise.
Reachability analysis: Phoenix maps which vulnerable component references actually executed compromised code versus those merely declared in workflow files. This distinguishes between repositories where the malicious action ran (and secrets were likely exfiltrated) versus those where the workflow was defined but did not execute during the exposure window.
Remediation campaigns: Create a campaign in Phoenix to track SHA migration across all affected actions, secret rotation completion, runner persistence cleanup, and npm package auditing. Assign owners per repository and track fix verification through to confirmed clean state.
Ownership attribution: Map each affected workflow to the owning team for coordinated response. When hundreds of repositories reference the same compromised action, knowing which team owns each workflow is the difference between a structured remediation and chaos.
Phoenix Security correlates vulnerable components with runtime workloads, identifies exposed pipelines via attack surface management, and assigns remediation ownership — turning a cross-ecosystem supply chain compromise into an owned, trackable remediation backlog.
References
1. Wiz Research — Trivy Compromised by TeamPCP
2. Wiz Research — KICS GitHub Action Compromised: TeamPCP Supply Chain Attack
3. CrowdStrike — From Scanner to Stealer: Inside the trivy-action Supply Chain Compromise
4. Socket Security — Trivy Under Attack Again: Widespread GitHub Actions Tag Compromise
5. Socket Security — CanisterWorm: npm Publisher Compromise Deploys Backdoor Across 29 Packages
6. Sysdig TRT — TeamPCP Stealer Detected in Checkmarx ast-github-action (sysdig.com)
7. StepSecurity — Trivy Compromised a Second Time: Malicious v0.69.4 Release
8. Aikido Security — TeamPCP Deploys CanisterWorm on NPM Following Trivy Compromise
9. Snyk — Trivy GitHub Actions Supply Chain Compromise
10. Boost Security Labs — 20 Days Later: Trivy Compromise, Act II
11. Rami McCarthy — Trivy Supply Chain Compromise: TeamPCP Attack Timeline & IOCs (ramimac.me/trivy-teampcp)
12. Aqua Security — Trivy Supply Chain Attack: What You Need to Know (aquasec.com)
13. Aqua Security GitHub Security Advisory GHSA-69fq-xp46-6×23
14. Mend — CanisterWorm: The Self-Spreading npm Attack That Uses a Decentralized Server to Stay Alive
15. Cloud Security Alliance — CSA Research Note: CanisterWorm Blockchain C2 in CI/CD Supply Chain Attack
16. ReversingLabs — Checkmarx OpenVSX Extension Compromise (concurrent report)
17. ARMO — The Trivy Supply Chain Attacks Explained
How Phoenix Security Fixes What Actually Matters
Your team doesn’t have a finding problem. They have a fixing problem.
Most platforms hand you a list. Phoenix hands you a plan — ranked by real risk, mapped to the team that owns it, with a clear path to close it.
What remediation looks like with Phoenix:
- One backlog, not five. Findings from SAST, SCA, containers, and cloud — deduplicated, correlated, and surfaced in a single prioritized queue. No more reconciling lists across tools.
- Ownership that sticks. Team attribution and inheritance mean the right ticket goes to the right engineer, first time. No routing, no guessing, no back-and-forth.
- Campaigns that move the needle. Group related findings into targeted remediation campaigns. Track progress, measure closure rates, and report real reduction — not raw counts.
- AI that does the legwork, not the deciding. Phoenix’s Remediator agent drafts fixes, creates tickets, and opens PRs. Your team reviews, approves, and merges. Every fix is traceable.
Phoenix Security changes the game.

The results are clear:
- Bazaarvoice saved $6.3M in developer time and for teams removed critical in the first weeks of adoption
- ClearBank cut critical container vulnerabilities by 96–99% and reclaimed 4 hours per engineer per week.
- IAS saved an equivalent of 1.5M in development hours and reduced SCA-to-container noise by 82.4%
- Optimizely has been able to act on vulnerabilities sitting on the backlog.
The pattern is the same every time: teams that move from find and report to analyze and fix close more risk with less effort.

The results are clear:
- Bazaarvoice saved $6.3M in developer time and for teams removed critical in the first weeks of adoption
- ClearBank cut critical container vulnerabilities by 96–99% and reclaimed 4 hours per engineer per week.
- IAS saved an equivalent of 1.5M in development hours and reduced SCA-to-container noise by 82.4%
- Optimizely has been able to act on vulnerabilities sitting on the backlog.
Or learn how Phoenix Security slashed millions in wasted dev time for fintech, retail, and adtech leaders.