The Scanner That Became the Threat: Trivy Compromised in Supply Chain Attack, CanisterWorm Follows

Published: March 22, 2026 Reading time: 14 minutes 🚨 BREAKING — ACTIVE INCIDENT

📢 Affiliate Disclosure: This site contains affiliate links to Amazon. We earn a commission when you purchase through our links at no additional cost to you.

On March 21, 2026, Trivy — the most widely deployed open-source vulnerability scanner in the container and Kubernetes ecosystem — became the vulnerability. A threat actor known as TeamPCP compromised Aqua Security's build pipeline, backdoored Trivy v0.69.4, hijacked 75 of 76 GitHub Actions tags in the trivy-action repository, and used the resulting access to push credential-stealing malware to every CI/CD pipeline running Trivy. The malicious release was live for approximately three hours. Affected GitHub Actions tags remained active for up to twelve hours. Any organization that ran Trivy during that window should assume their environment is fully compromised.

The attack is a masterclass in supply chain exploitation: target the tool that everyone trusts to find supply chain threats, compromise it upstream, let the world's security pipelines deliver your malware for you. It's also the second breach of Aqua Security's Trivy infrastructure in March — a March 1 incident exfiltrated credentials that were not fully contained, and those same credentials enabled this follow-on attack.

This post covers the full attack chain, what the infostealer collected, how the follow-up CanisterWorm campaign works (including its novel blockchain C2 via Internet Computer canisters), what detection and remediation look like, and — separately — the first in-the-wild Chrome Application-Bound Encryption bypass using hardware debugger breakpoints, disclosed today by Gen Digital's threat research team.

1. What Happened: The Trivy Supply Chain Compromise

Trivy, developed by Aqua Security, is the de facto standard container and Kubernetes security scanner. It detects CVEs in container images, misconfigurations in infrastructure-as-code, exposed secrets in code repositories, and compliance violations across cloud environments. According to Aqua Security's own data, Trivy is downloaded more than 20 million times per month. It runs in the CI/CD pipelines of companies across the Fortune 500.

That ubiquity is exactly why it was targeted.

On March 21, 2026, the threat actor TeamPCP (also tracked as DeadCatx3, PCPcat, and ShellForce) used credentials stolen in an earlier March 1 breach to access Aqua Security's GitHub build infrastructure. They made two primary modifications:

  1. Trojanized the Trivy v0.69.4 release binary — replacing the official build with a version that included an infostealer payload
  2. Force-pushed malicious commits to 75 of 76 version tags in the aquasecurity/trivy-action GitHub repository, replacing the legitimate entrypoint.sh with a malicious version

The trivy-action repository is the official GitHub Action that organizations use to integrate Trivy into their GitHub Actions workflows. A typical workflow looks like this:

- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@v0.24.0  # ← this tag was compromised
  with:
    image-ref: 'docker.io/library/nginx:latest'
    format: 'sarif'

Because GitHub Actions workflows pin to version tags (not commit hashes), and because TeamPCP force-pushed malicious commits to 75 tags, any workflow using a tag other than the single unaffected one automatically ran the malicious entrypoint during the exposure window. The malicious code executed before running the legitimate Trivy scan — so the pipeline completed normally, produced real Trivy results, and gave no obvious indication that anything had gone wrong.

Aqua Security confirmed the incident and the March 1 link:

"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 Discussions #10425

The malicious release was live for approximately three hours. The compromised GitHub Actions tags remained active for up to twelve hours before Aqua Security completed remediation.

2. The Attack Chain: From Credential Breach to Mass Exfiltration

The full attack chain across both incidents:

March 1: Initial Breach — Credential Exfiltration

TeamPCP (a documented cloud-native threat actor known for targeting misconfigured Docker APIs, Kubernetes dashboards, and Redis servers) gained access to Trivy's build environment and exfiltrated credentials. Aqua Security detected this incident and initiated a credential rotation process. The rotation was incomplete — likely because Aqua Security didn't know the full scope of what was exfiltrated, so not all secrets were invalidated.

March 21: Follow-On Attack — Build Pipeline Compromise

Using credentials from the March 1 exfiltration (either non-rotated secrets or newly issued tokens captured before rotation completed), TeamPCP pushed to the Trivy GitHub repository with write access. Their modifications targeted two surfaces:

Surface 1: The Official Release Binary (v0.69.4)

The attacker replaced the legitimate Trivy binary with a trojanized version that included the infostealer payload. The payload was uploaded to VirusTotal (hash: 18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a). On developer machines that downloaded and ran v0.69.4, the binary performed credential harvesting in addition to its normal scanning function.

Surface 2: GitHub Actions Tags

In the aquasecurity/trivy-action repository, the attacker force-pushed malicious commits to 75 of 76 version tags. The legitimate entrypoint.sh was replaced with a version that:

  1. Ran credential harvesting against the CI/CD runner's environment
  2. Then executed the real Trivy scanner (to avoid detection)

This is a critical design choice: the malware intentionally allows the scan to complete normally. The pipeline green-lights, Trivy produces real output, and the secret exfiltration happens silently in the background.

Exfiltration Infrastructure

Collected data was encrypted and stored as tpcp.tar.gz, then exfiltrated to a typosquatted command-and-control server: scan.aquasecurtiy[.]org (note the deliberate misspelling of "security" as "securtiy"). This domain mimics Aqua Security's legitimate domain aquasecurity.github.io, making it plausible in DNS logs for defenders who aren't looking carefully.

If exfiltration to the C2 failed, the malware fell back to creating a public repository named tpcp-docs in the victim's own GitHub account and pushing the stolen data there. This is an elegant fallback: if egress to external C2 is blocked, the attacker's data goes to the victim's own GitHub, where it's accessible publicly and doesn't require any outbound connection that could be blocked.

Persistence

On developer machines, the trojanized binary established persistence by dropping a Python payload to ~/.config/systemd/user/sysmon.py and registering it as a systemd user service. The service checks a remote server for additional payloads — giving TeamPCP ongoing access to any developer machine that ran v0.69.4, even after the malicious binary was removed from GitHub.

3. What the Infostealer Collected

Analysis by Socket and Wiz identified the full scope of credential and data types targeted. The list is comprehensive and illustrates exactly why CI/CD environments are high-value targets:

Category Specific Targets
Reconnaissance hostname, whoami, uname, network config, environment variables
SSH Private and public keys, SSH config files
Cloud / Infrastructure AWS, GCP, Azure credentials; Kubernetes kubeconfig; Docker credentials
Environment files .env and variants — where most application secrets live
Database credentials PostgreSQL, MySQL/MariaDB, MongoDB, Redis config files
CI/CD configs Terraform state, Jenkins credentials, GitLab CI tokens
TLS/Crypto TLS private keys, VPN configs, cryptocurrency wallets
Communication tokens Slack and Discord webhooks and bot tokens
System files /etc/passwd, /etc/shadow, auth logs, shell history
GitHub Actions secrets Memory scan of Runner.Worker process for "isSecret":true JSON strings

That last entry is particularly notable: the malware actively scanned the GitHub Actions Runner.Worker process memory for the specific JSON structure that GitHub uses to pass secrets to workflow steps. This means even secrets that were never written to disk and were only passed as environment variables could be captured. If your pipeline injected AWS credentials, Docker registry tokens, or signing keys as GitHub Actions secrets, those may have been captured directly from runner memory.

For CI/CD security fundamentals — including how to minimize secret exposure in automated pipelines — Hacking the Cloud covers the practical attack surface across AWS, GCP, and Azure environments that attackers target after gaining CI/CD credentials like these.

4. CanisterWorm: The Blockchain-C2 Follow-Up Campaign

Researchers at Aikido Security linked the same TeamPCP threat actor to a follow-up campaign that began using stolen npm tokens from the Trivy compromise to deploy a novel self-propagating worm in the npm ecosystem.

CanisterWorm is technically interesting in two ways: its propagation mechanism and its C2 infrastructure.

Propagation: Self-Replicating via Stolen npm Tokens

Once the worm obtains an npm authentication token from a compromised developer environment, it runs a deploy.js payload that:

  1. Uses the stolen token to authenticate with the npm registry as the victim user
  2. Enumerates all packages the victim account has publish rights to
  3. Bumps the patch version of each package
  4. Publishes malicious versions of all enumerated packages to the npm registry

Aikido observed this process deploying across 28 packages in under 60 seconds. Because these packages are published under legitimate maintainer accounts with real histories and real users, downstream projects that automatically update dependencies will receive the malicious versions without any obvious warning signs. The worm then repeats the cycle when those downstream developers' machines are compromised — propagating through the npm dependency graph.

C2 Infrastructure: Internet Computer (ICP) Canisters

This is the technically novel part. Rather than using a traditional HTTP C2 server (which can be taken down via domain seizure or IP blocking), CanisterWorm uses the Internet Computer Protocol (ICP) blockchain's "canister" smart contracts as its command-and-control mechanism.

ICP canisters are WebAssembly programs that run on the Internet Computer blockchain. They're persistent, decentralized, and — critically — can only be shut down by their own controller. Law enforcement cannot seize the domain because there is no domain. There is no IP to block. The C2 "server" is a smart contract running on a decentralized network of nodes.

The canister acts as a dead-drop resolver: the malware calls the canister, which returns URLs pointing to additional payloads. Even if those payload URLs are taken down, the attacker can update the canister to point to new URLs. The only way to truly shut down the C2 is to compromise the canister's controller key.

The malware also establishes persistence via the same systemd user service pattern as the Trivy trojan: ~/.config/systemd/user/sysmon.py checks the ICP canister for instructions at regular intervals.

This represents a meaningful escalation in attacker C2 infrastructure sophistication. Security teams that rely on domain-blocklists and IP-based threat intelligence will find that traditional C2 detection approaches are ineffective against blockchain-based command-and-control.

5. Am I Affected? Detection and Indicators

Were You Running Trivy During the Window?

The exposure window was approximately:

  • Trojanized binary (v0.69.4): Available for ~3 hours on March 21, 2026
  • Compromised GitHub Actions tags: Active for up to ~12 hours on March 21, 2026

If any of your CI/CD pipelines or development machines ran Trivy during this window — whether via the GitHub Action, via direct binary download, or via container image — treat the environment as compromised.

Key Indicators of Compromise

File system indicators:

  • ~/.config/systemd/user/sysmon.py — persistence payload
  • tpcp.tar.gz in any temp or home directory — staged exfiltration archive
  • Unexpected public repository named tpcp-docs in your GitHub account

Network indicators:

  • DNS queries or HTTP requests to scan.aquasecurtiy[.]org (note misspelling)
  • Outbound connections to ICP canister endpoints (Internet Computer blockchain)
  • Unexpected npm publish activity from your account

GitHub indicators:

  • Review your GitHub Actions workflow runs for March 21 — look for unusual file operations or network activity in the pre-scan steps
  • Check your GitHub account's repository list for any unexpected public repositories
  • Review your npm publish history at npmjs.com/~youruser for unauthorized publications

Process indicators:

# Check for the persistence service
systemctl --user list-units | grep sysmon
ls -la ~/.config/systemd/user/sysmon.py

# Check for unexpected npm packages published by your account
npm whoami && npm search --maintainer $(npm whoami) --json | jq '.[].date'

Trivy Hash Verification

If you downloaded Trivy recently, verify you're running a clean build. The malicious binary was v0.69.4. Check:

trivy --version
# Output should show v0.69.3 or v0.69.5+

# If you have v0.69.4, verify against official checksums at:
# https://github.com/aquasecurity/trivy/releases/tag/v0.69.5
# (Aqua Security pulled v0.69.4 and published v0.69.5 as clean replacement)

6. Remediation: What to Do Now

If you ran Trivy during the exposure window or cannot rule it out, follow this remediation checklist. The scope of what the infostealer collected means you should treat this as a broad credential compromise, not a targeted one.

Immediate Actions (Do These First)

  1. Rotate all cloud provider credentials — AWS access keys, GCP service account keys, Azure service principals. Revoke the old credentials before or simultaneously with creating new ones. Check CloudTrail / GCP Audit Logs / Azure Activity Logs for unauthorized API calls using the potentially stolen credentials.
  2. Rotate all GitHub tokens, npm tokens, and container registry credentials. Revoke all personal access tokens (PATs) associated with accounts that ran affected workflows. Check your GitHub account's "Authorized OAuth Apps" and "Personal access tokens" pages.
  3. Rotate SSH keys. The infostealer specifically targeted ~/.ssh/. Generate new keys, update authorized_keys on all servers, and revoke the old keys.
  4. Audit and rotate all secrets in .env files and secret managers. HashiCorp Vault, AWS Secrets Manager, GitHub Actions secrets — rotate everything that was accessible in the build environment.
  5. Remove the persistence payload. Check for ~/.config/systemd/user/sysmon.py on all affected machines and CI/CD runners. Disable and remove the service if present:
    systemctl --user stop sysmon 2>/dev/null
    systemctl --user disable sysmon 2>/dev/null
    rm -f ~/.config/systemd/user/sysmon.py
    rm -f ~/.config/systemd/user/sysmon.service

CI/CD Hardening (Prevent Recurrence)

This attack succeeded in part because GitHub Actions workflows pinned to tag names rather than commit hashes. Tag names are mutable — they can be force-pushed to point to different commits. Commit hashes are immutable.

The hardened pattern:

# Vulnerable — tag can be force-pushed to malicious commit:
uses: aquasecurity/trivy-action@v0.24.0

# Hardened — commit hash cannot be changed:
uses: aquasecurity/trivy-action@a20de5420d57c4102486cdd9349b532415f5d7b7

GitHub's own documentation recommends pinning to full commit SHAs for third-party actions. Tools like StepSecurity Harden-Runner and actionlint can enforce this across your workflow files.

Additional CI/CD hardening measures:

  • Minimal permissions: GitHub Actions tokens should use permissions: blocks to restrict access to only what each workflow requires. A scanner workflow does not need id-token: write or packages: write.
  • OIDC for cloud credentials: Use GitHub's OIDC provider to assume cloud IAM roles rather than storing long-lived credentials as secrets. A stolen OIDC token is scoped to a specific workflow run — it cannot be replayed indefinitely.
  • Egress filtering for CI runners: Self-hosted runners should have egress filtered to known-good endpoints. The C2 domain scan.aquasecurtiy[.]org would have been blocked by any reasonably configured egress filter.

For a comprehensive treatment of how attackers move from CI/CD compromise to cloud account takeover — and how to architect pipelines that minimize blast radius — Hacking the Cloud and Container Security are the reference texts used by red teams doing this work professionally.

Long-Term: Supply Chain Verification

Aqua Security has committed to signing all future Trivy releases with Sigstore's cosign tooling, enabling cryptographic verification of release provenance. Once implemented, you can verify:

cosign verify-blob trivy \
  --certificate-identity="https://github.com/aquasecurity/trivy/.github/workflows/release.yaml@refs/tags/v0.69.5" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  --bundle trivy.bundle

This approach — Software Bill of Materials (SBOM) + provenance attestation via Sigstore — is now the recommended standard for high-assurance binary distribution in the supply chain security community.

7. Bonus: VoidStealer — The First Chrome ABE Bypass in the Wild

Breaking independently today from Gen Digital (Avast/AVG/Norton parent company): VoidStealer, a Malware-as-a-Service infostealer first advertised on dark web forums in December 2025, has become the first infostealer observed in the wild to successfully bypass Chrome's Application-Bound Encryption (ABE) using hardware debugger breakpoints.

What Is Application-Bound Encryption?

Google introduced ABE in Chrome 127 (June 2024) as a direct response to the infostealer epidemic targeting browser-stored credentials and cookies. Before ABE, Chrome stored sensitive data encrypted with keys that any process running as the logged-in user could access — enabling the wave of "cookie theft" malware that bypassed MFA by stealing authenticated session cookies.

ABE addresses this by requiring Google Chrome's Elevation Service (which runs as SYSTEM) to participate in decrypting the master key. User-level processes cannot decrypt ABE-protected data directly — they need the Elevation Service to validate and perform the decryption on their behalf. For malware running at user privilege level, this appeared to close the cookie theft pathway.

How VoidStealer's Debugger Bypass Works

VoidStealer's breakthrough is a debugger-based attack that exploits a brief window when Chrome's master key exists in plaintext in process memory:

  1. Spawn a suspended, hidden browser process — VoidStealer launches a new Chrome process in suspended state, making it invisible to the user
  2. Attach as a debugger — The malware attaches to the new process with debugging privileges (no admin required — any process can debug its own children, or processes it spawns)
  3. Wait for the target DLL — Monitor for chrome.dll to load in the new process
  4. Scan DLL for the target instruction — Locate a specific string and the LEA (Load Effective Address) instruction that references it
  5. Set hardware breakpoints — Rather than injecting code (which AV/EDR detects), set hardware breakpoints on the instruction's address across all browser threads using the CPU's debug registers
  6. Wait for breakpoint trigger — During browser startup, Chrome decrypts ABE-protected cookies using the v20_master_key. When the breakpointed instruction executes during this decryption, execution pauses at the hardware breakpoint
  7. Read the master key — Use ReadProcessMemory to read the register pointing to the plaintext master key at the moment of breakpoint trigger

The key insight: Chrome briefly holds the master key in memory in plaintext during startup decryption. The hardware breakpoint catches Chrome in that exact moment, before the key is re-encrypted or cleared. The approach was pioneered in the open-source ChromeKatz project (ElevationKatz component), which demonstrated the weakness over a year ago. VoidStealer v2.0 is the first malware observed deploying it operationally.

Why This Matters More Than Previous ABE Bypasses

Previous ABE bypasses required either privilege escalation or code injection — both of which are reliably detected by modern EDR tools. VoidStealer's hardware breakpoint approach requires neither:

  • No privilege escalation — Debugging a child process is permitted for any user-level process
  • No code injection — Hardware breakpoints are set via debug APIs, not by writing shellcode into the target process
  • No interaction with the Elevation Service — The key is extracted from memory rather than requesting decryption through the protected service pathway

EDR products that rely on detecting privilege escalation attempts or code injection patterns will not trigger on VoidStealer's technique. Detection requires monitoring for unusual use of Windows debug APIs (DebugActiveProcess, hardware breakpoint configuration via SetThreadContext) against browser processes.

Google has been contacted by BleepingComputer for comment and had not responded by the time of publication.

What Bug Hunters Should Know

The VoidStealer technique opens a new research area: using hardware breakpoints as a non-injecting, non-privileged method for extracting in-memory secrets from protected processes. If you're working on browser security research or red team engagements where browser credential extraction is in scope, the ChromeKatz/ElevationKatz open-source tooling is worth studying as a foundation — it documents the specific Chrome code paths and data structures involved. The Web Application Hacker's Handbook provides the conceptual grounding for browser security models that makes this class of attack legible in context.

Key Takeaways

  • Trivy v0.69.4 was backdoored (March 21, 2026). If you ran Trivy or the trivy-action GitHub Action on March 21, treat your environment as compromised. Rotate all cloud credentials, SSH keys, GitHub/npm tokens, and secrets manager contents immediately.
  • The attack succeeded because of an incomplete credential rotation from a March 1 breach. "We rotated secrets" is not the same as "we contained the breach." Verify full rotation, revoke all tokens (not just the ones you know about), and audit for persistence before declaring an incident closed.
  • GitHub Actions tag pinning is mutable and exploitable. Pin workflows to full commit SHAs, not tag names. Tools like actionlint can enforce this automatically.
  • CanisterWorm uses ICP blockchain canisters as C2. Traditional domain/IP blocklists and domain seizure orders are ineffective. Detection requires monitoring for ICP endpoint communication and anomalous npm publish activity.
  • VoidStealer bypasses Chrome's ABE without privilege escalation or code injection — using hardware debugger breakpoints to extract the master key during the brief startup window when it exists in plaintext. This is the first in-the-wild exploitation of the technique. Google has no public patch.

Advertisement