Trivy GitHub Actions Supply Chain Compromise
TL;DR
What: Attackers force-pushed malicious code to 75 of 76 version tags in
aquasecurity/trivy-action, plus compromisedsetup-trivyand the Trivy v0.69.4 binaryExposure window: \~12 hours (March 19, 17:43 UTC to March 20, \~05:40 UTC)
Impact: CI/CD secrets stolen via Runner process memory dumping; SSH keys, cloud credentials, crypto wallets harvested from self-hosted runners
Attribution: TeamPCP (DeadCatx3/PCPcat/ShellForce), a threat group targeting cloud-native infrastructure
Root cause: Credentials obtained during a prior compromise on March 1, which itself stemmed from a
pull_request_targetworkflow exploitSafe versions: trivy
v0.69.3, trivy-actionv0.35.0, setup-trivyv0.2.6CVE: Not yet assigned (community has requested one)
Action required: Pin to commit SHAs, rotate all pipeline secrets if you ran an affected version during the window
On March 19, 2026, attackers force-pushed malicious code to 75 of 76 version tags in aquasecurity/trivy-action, the official GitHub Action for one of the most widely-used open source vulnerability scanners in the container security ecosystem. The malicious payload silently stole CI/CD secrets before running the legitimate Trivy scan, so affected pipelines appeared to function normally.
This was the second compromise of Trivy infrastructure in under three weeks, with the attacker leveraging credentials obtained during the first incident. If your CI/CD pipeline uses Trivy via GitHub Actions, you may be affected. Here's what happened, how to check, and what to do about it.
What happened
The March 19 attack targeted three Aqua Security repositories simultaneously:
aquasecurity/trivy-action: 75 of 76 version tags rewritten to point to malicious commits containing a credential stealeraquasecurity/setup-trivy: Seven historical version tags deleted and recreated with malicious code; only v0.2.6 remains cleanTrivy binary v0.69.4: A malicious release published to the official repository, propagating to Docker Hub (
aquasec/trivy:0.69.4), GitHub Container Registry, and AWS ECR
The attacker rewrote existing version tags so they pointed to new commits containing a modified entrypoint.sh. When any CI/CD pipeline pulled trivy-action by tag (e.g., @v0.34.2, @v0.33.0, or any of dozens of older versions), the malicious entrypoint executed before the legitimate Trivy scan, silently harvesting credentials.
Only v0.35.0 was unaffected. It already pointed to the master HEAD commit used as the base for the malicious versions, so the attacker's automation skipped it.
Timeline of March 19 events (UTC) [^1]
Time | Event |
|---|---|
17:43 | Attacker pushes |
17:51 |
|
\~18:05 | Homebrew automation picks up v0.69.4 and opens an update PR |
18:26 | Poisoned binaries published to GitHub Releases, Docker Hub, GHCR, and AWS ECR |
18:30 | Attacker opens PR #538 on trivy-action to "bump default Trivy version"; tag force-pushes happen in parallel |
\~19:15 | Socket.dev detection systems begin generating threat feed entries |
22:06-22:08 |
|
21:07 | Community response begins; Trivy maintainer starts deleting poisoned setup-trivy tags |
21:43 | Clean |
23:05 | Homebrew emergency downgrade PR filed |
23:13 | Malicious v0.69.4 tag deleted |
00:01-00:08 (Mar 20) | 47 bot accounts post spam comments in the incident discussion; two accounts reference "TeamPCP" by name |
\~05:40 (Mar 20) | All trivy-action tags restored to safe commits; exposure window closes |
How the attack chain started
This was the culmination of a multi-stage attack that began weeks earlier.
Stage 1: The pull_request_target exploit (late February)
A threat actor operating under the GitHub handle hackerbot-claw (account created February 20, 2026) ran an automated campaign scanning public repositories for misconfigured GitHub Actions workflows. The campaign specifically targeted workflows using the pull_request_target event trigger with excessive token permissions. Targets included Microsoft, DataDog, the CNCF, and Aqua Security.
The pull_request_target event is a known risk in GitHub Actions. Unlike pull_request, it runs in the context of the base repository, not the fork. This means workflows triggered by pull_request_target have access to the base repo's secrets. If such a workflow also checks out code from the pull request (the fork), an attacker can submit a PR containing malicious code that executes with access to those secrets.
We covered this exact attack pattern in our analysis of the Ultralytics AI supply chain attack in December 2024, where template injection via branch names allowed attackers to execute arbitrary code in privileged workflow contexts.
The Trivy repository had a vulnerable pull_request_target workflow that had been present since October 2025. Boost Security's poutine tool had flagged it on November 29, 2025, over three months before exploitation. On February 28, hackerbot-claw exploited this workflow to steal an org-scoped Personal Access Token (ORG_REPO_TOKEN) belonging to the aqua-bot service account. This PAT was used across at least 33 workflows in the Aqua Security GitHub organization.
Stage 2: First compromise (March 1)
Using the stolen PAT, the attacker:
Temporarily privatized the Trivy repository
Deleted GitHub releases between v0.27.0 and v0.69.1
Published a malicious Trivy VS Code extension via OpenVSX that attempted to abuse local AI coding agents
Exfiltrated additional credentials from the CI environment
Aqua Security disclosed the incident in GitHub discussion #10265 and began rotating secrets and tokens. As the Trivy team later noted, "the process wasn't atomic, and attackers may have been privy to refreshed tokens."
Stage 3: Second compromise (March 19)
Twenty days later, the attacker returned with a more complex payload: a multi-stage credential stealer attributed to TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce), a threat group known for targeting cloud-native infrastructure with a focus on cryptocurrency theft.
In an additional counter-incident-response tactic, the attacker used the compromised aqua-bot account to delete the original incident discussion (#10265), slowing down the community's ability to coordinate a response.
Inside the malicious payload
The injected entrypoint.sh replaced the legitimate Trivy scan entrypoint with a three-stage attack. The self-identified "TeamPCP Cloud stealer" embedded in the payload targeted different environments with different techniques.
On GitHub-hosted runners
The malware used passwordless sudo access (standard on GitHub-hosted runners) to:
Dump Runner.Worker processes memory by reading
/proc/<pid>/memto extract GitHub Actions secrets stored in the .NET runtime heapParse secrets from memory, specifically targeting JSON objects matching the pattern
{"value":"<secret>","isSecret":true}Scan
/proc/*/environto harvest environment variables from all running processes
This is the same memory-dumping technique documented in the tj-actions/changed-files compromise from March 2025. Secrets passed to GitHub Actions workflows are decrypted at runtime and held in the Runner process memory, making them readable by any code executing in the same runner environment.
The full stealer script was shared in the trivy-action issue thread and analyzed independently. Here's the core memory-extraction logic:
On self-hosted runners
Against self-hosted runners, the malware performed a comprehensive filesystem scan across 50+ sensitive paths, targeting:
SSH keys and Git credentials
Cloud provider credentials: AWS, GCP, Azure configurations
Kubernetes tokens and kubeconfig files
Docker credentials and registry authentication
Database connection strings
Terraform state files (which often contain plaintext secrets)
Cryptocurrency wallets: Bitcoin, Ethereum, Solana, Cardano keypairs
Environment files:
.envand variantsTLS certificates and private keys
Shell history files
VPN configurations
On developer machines
If the malware was detected, it was not running inside GitHub Actions (i.e., GITHUB_ACTIONS != "true"), it installed a persistence mechanism: a Python dropper written to ~/.config/systemd/user/sysmon.py with an accompanying systemd user unit. This dropper polled an attacker-controlled ICP (Internet Computer Protocol) endpoint for additional payloads.
Exfiltration
Collected data was encrypted using AES-256-CBC with PBKDF2 key derivation, wrapped with RSA-4096 OAEP padding, bundled as tpcp.tar.gz, and sent via HTTPS POST to the typosquat domain scan[.]aquasecurtiy[.]org (note the swapped i and t in "security"). The C2 server sat at IP 45.148.10.212, registered to TECHOFF SRV LIMITED in Amsterdam.
If the primary exfiltration channel failed, the malware had two fallback mechanisms:
Cloudflare Tunnel: Exfiltration via
plug-tab-protective-relay.trycloudflare.comGitHub as a dead drop: Using the victim's own
INPUT_GITHUB_PATtoken, the malware created a public repository namedtpcp-docson the victim's GitHub account and uploaded the encrypted stolen data as release assets. The attacker could then find these repositories by searching GitHub.
The full credential theft scope
The decoded Python stealer script from setup-trivy (recovered from a captured workflow run) reveals the exhaustive scope of the credential theft. The malware targeted over 50 filesystem paths, including:
The script also attempted to reach cloud metadata services (169.254.169.254, 169.254.170.2) for IAM credentials, ran kubectl get secrets --all-namespaces, and searched recursively for API keys, webhook URLs, and database connection strings.
The spoofed trivy binary (v0.69.4)
Beyond what was stolen from users of the action, the compromised release workflow also exposed Aqua Security's own high-value secrets: Docker Hub credentials, AWS ECR access keys, GPG signing keys (used to sign RPM packages), and the org-scoped GitHub PAT used across 33+ workflows.
The compromised Trivy binary took a different approach. It rewrote the release workflow to reference actions/checkout@70379aad1a8b40919ce8b382d3cd7d0315cde1d0, which looks like a legitimate SHA pin but actually pointed to a commit from a fork, not the real actions/checkout repository. This exploits a subtle GitHub platform behavior: a uses: owner/repo@<hash> reference doesn't verify that the hash belongs to the named repository. Due to Git's fork object sharing, any commit from any fork is reachable by SHA. The build fetched backdoor Go source files from the C2, added --skip=validate to GoReleaser to bypass build validation, and overrode .golangci.yaml to suppress linter warnings on the injected code. The resulting binaries ran the legitimate Trivy scanner in parallel with the credential stealer, so scan results looked normal.
Detection indicators
The malicious commits had several telltale signs that, in hindsight, made the forgery visible:
Unsigned commits: The original Trivy releases were GPG-signed via the GitHub web UI. The replacement commits were not.
Impossible timestamps: The forged commits cloned the original release dates as their timestamps, but their parent commits were from March 2026, creating impossible parent-date relationships.
"0 commits to master since this release": GitHub's release UI showed this counter for tags that should have been many versions behind master, indicating that each tag pointed to a commit whose parent was the current master HEAD.
Single-file modifications: Each malicious commit only modified
entrypoint.sh, unlike the original multi-file commits they impersonated.
StepSecurity's Harden-Runner detected outbound C2 connections across 12,000+ public repositories using its community tier, enabling direct notification to affected projects.
Indicators of compromise [^2]
Type | Value | Notes |
|---|---|---|
Domain (C2) |
| Typosquat: note swapped |
IP |
| TECHOFF SRV LIMITED, Amsterdam |
Cloudflare Tunnel |
| Fallback exfiltration |
ICP endpoint |
| Persistence dropper C2 |
Commit (trivy-action) |
| Malicious |
Commit (setup-trivy) |
| Malicious |
GitHub repo (exfil) |
| Created in victim's GitHub account |
Trivy binary (Linux 64-bit) |
| SHA-256, v0.69.4 |
Are you affected?
You may be impacted if:
Your workflows use
aquasecurity/trivy-actionreferenced by tag (e.g.,@v0.34.2,@v0.18.0, or any version tag other than@v0.35.0)Your workflows use
aquasecurity/setup-trivyat any version other than@v0.2.6You downloaded Trivy v0.69.4 via direct download, Docker (
aquasec/trivy:0.69.4), or GHCR between March 19-20, 2026Any of the above ran during the exposure window: March 19, 2026, 17:43 UTC through March 20, 2026, \~05:40 UTC
The confirmed exposure window was approximately 12 hours. If your trivy-action ran outside this window, you were not affected by the tag compromise (though you should still pin to SHAs going forward).
Note on @master references: When asked whether workflows pinned to aquasecurity/trivy-action@master were safe, Trivy maintainer DmitriyLewen responded: "We don't have information how the attacker created the orphan commit. So I can't tell you that aquasecurity/trivy-action@master was 100% safe."
Note on Homebrew: While Homebrew initially picked up v0.69.4, it builds from source tarballs. The compiled binary was not backdoored. An emergency downgrade PR was still filed (Homebrew/homebrew-core#273304).
How to check
Review your workflow files for trivy-action references:
Check your GitHub Actions run logs for the affected time window. Look for any runs after March 19, 2026, 17:43 UTC that used trivy-action.
Check for the exfiltration fallback:
Check network logs for connections to the C2 domain (scan[.]aquasecurtiy[.]org) or IP (45.148.10.212).
Immediate remediation steps
1. Stop using trivy-action by tag
Remove or update all workflow references immediately. Safe options:
For setup-trivy, the only safe version is v0.2.6.
2. Rotate all credentials
If any affected action ran in your pipeline, treat every secret accessible to that workflow as compromised. This includes:
GitHub tokens (both
GITHUB_TOKENand any PATs)Cloud provider credentials (AWS access keys, GCP service account keys, Azure credentials)
Container registry tokens (Docker Hub, GHCR, ECR, etc.)
SSH keys
Database passwords
API keys and webhook secrets
3. Audit for attacker persistence
On self-hosted runners and developer machines, check for the persistence mechanism:
4. Check for the exfiltration fallback
Search your GitHub organization for repositories named tpcp-docs. If found, the attacker successfully exfiltrated data from your pipeline.
5. Run the compromise scanner
StepSecurity released a dedicated trivy-compromise-scanner tool that audits GitHub Actions workflow run logs across repos or entire organizations, checking for runs that used a compromised action reference during the attack window.
What Aqua Security has done since
Following the incident, the Trivy maintainers have:
Restored all trivy-action tags to their original safe commits, with a new
v-prefix naming convention (e.g.,0.34.0is nowv0.34.0)Enabled immutable releases on the trivy, trivy-action, and setup-trivy repositories, preventing future tag force-pushes
Removed all malicious artifacts from Docker Hub, GHCR, AWS ECR, and GitHub Releases
Locked down automated actions and tokens organization-wide
As maintainer DmitriyLewen noted in the issue thread: "Regardless of incidents, it's always better to use commit hashes."
CVE-2026-28353 (CVSS 10.0) was assigned to the first incident's VS Code extension compromise, but no CVE has been assigned to the March 19 tag-hijacking attack specifically, despite community requests for formal disclosure. A separate, earlier vulnerability (CVE-2026-26189 / GHSA-9p44-j4g5-cfx5, CVSS 5.9) affecting script injection in the composite action was patched in v0.34.0 prior to the supply chain attack.
StepSecurity's Harden-Runner has already filed security alert issues at affected open-source projects, including Fluent Bit (CNCF), k8gb (Kubernetes DNS), HL7 FHIR IG Publisher (healthcare standards), and Canonical's Charmed Kubeflow.
Lessons for securing GitHub Actions
This incident, combined with the tj-actions/changed-files compromise and the Clinejection attack on Cline's CI/CD pipeline, reinforces a pattern: GitHub Actions supply chain attacks are a recurring risk for organizations using CI/CD. If you haven't recently audited your pipeline security posture, now is the time.
Pin actions to commit SHAs, not tags
Tags are mutable. Anyone with write access can force-push a tag to point to a completely different commit. Commit SHAs are immutable.
This is the single most important defense against this class of attack. GitHub's own security hardening guide recommends pinning to full-length commit SHAs.
Tools like zizmor (static analysis for GitHub Actions workflows), pinact (automated SHA pinning), StepSecurity's secure-repo, and Renovate's pinGitHubActionDigests option can automate this across your organization.
Audit pull_request_target workflows
The root cause of this entire attack chain was a misconfigured pull_request_target workflow. If your repositories use this event trigger, audit them carefully:
Does it check out code from the PR? If so, it's likely vulnerable.
Does it have write permissions or access to secrets? Minimize these.
Can you use
pull_requestinstead? In most cases, you can.
The Ultralytics incident demonstrated the same vulnerability pattern. So did the Clinejection attack, where a prompt injection in a GitHub issue title gave attackers code execution inside a privileged workflow context.
Use repo-scoped tokens, not org-scoped PATs
The stolen ORG_REPO_TOKEN gave the attacker access across 33+ workflows in the Aqua Security organization. Repo-scoped tokens limit the blast radius of any single credential compromise. For release automation, fine-grained personal access tokens or GitHub App installation tokens scoped to specific repositories are the safer choice.
Monitor outbound network connections from CI/CD
Both the tj-actions and Trivy compromises were initially detected through anomalous outbound network activity from CI runners. Notably, tag force-pushes are invisible to GitHub Archive event data, making them a blind spot for event-based monitoring. Runtime network monitoring is currently the most reliable detection layer. Tools like StepSecurity Harden-Runner can monitor and restrict network access from your GitHub Actions workflows, and poutine can scan entire GitHub organizations for CI/CD supply chain risks.
Enforce GPG-signed commits on releases
The malicious commits in this attack were unsigned, while the legitimate releases were GPG-signed. Signature verification on release tags is one layer that makes this class of attack harder to execute undetected.
Practice complete credential rotation
The March 19 attack exploited credentials that survived the rotation after the March 1 incident, as Aqua Security acknowledged. This echoes the lesson from the CircleCI secrets rotation incident: when responding to a credential compromise:
Rotate all credentials atomically (or as close to atomically as possible)
Assume that any credential accessible from the compromised environment has been exfiltrated
Verify the rotation by confirming old credentials are invalidated, not just that new ones work
The bigger picture
Almost exactly one year after the tj-actions/changed-files compromise, the same class of attack recurred. The structural similarities are worth examining:
tj-actions (March 2025) | Trivy (March 2026) | |
|---|---|---|
Method | Tags rewritten to malicious commits | Tags force-pushed to malicious commits |
Root cause | Compromised token via reviewdog chain | Compromised PAT via |
Payload | Secrets printed to build logs in plaintext | Secrets encrypted and exfiltrated to C2 |
Blast radius | \~23,000 repos | \~10,000+ workflow files referencing trivy-action |
CVE | CVE-2025-30066 | Not yet assigned |
The Trivy incident illustrates how known supply chain attack patterns combine in practice. No single vulnerability was novel:
pull_request_targetmisconfigurations have been documented for yearsTag mutability in GitHub Actions is a known design limitation
Memory dumping of Runner.Worker processes were first seen in the tj-actions compromise
Typosquat C2 domains are a standard exfiltration technique
What made this attack effective was the composition of these techniques into a persistent, multi-stage campaign. The initial pull_request_target exploit led to credential theft, which the attacker used to launch a second attack that reached thousands of downstream pipelines.
For security teams, the takeaway is clear: your CI/CD pipeline is part of your attack surface. The tools running in your pipeline, including your security scanners, are dependencies that need the same scrutiny you give to your application dependencies. Securing your CI/CD pipeline deserves the same rigor as securing your application code.
Pin your actions to commit SHAs. Scope your tokens narrowly. Rotate your secrets completely. Monitor your runner network traffic. And when an incident does happen, treat every accessible credential as compromised until you can confirm otherwise.
This is a developing story. For the latest updates, see Aqua Security's official disclosure (discussion #10425).

How to Use GitHub Actions Environment Variables and Secrets — Learn how to properly handle secrets in GitHub Actions workflows to reduce your exposure to supply chain attacks.
WHITEPAPER
The AI Security Crisis in Your Python Environment
As development velocity skyrockets, do you actually know what your AI environment can access?