Trivy Supply Chain Incident: GitHub Actions Compromise Breakdown
Executive Summary
On March 19-20, 2026, the Trivy supply chain incident impacted the trivy project and the GitHub Actions many teams rely on to install and run Trivy in CI/CD pipelines.
Late Thursday night, Upwind’s MDR team observed observed anomalous Trivy activity inside a customer environment that deviated from established runtime baselines. The team identified previously unseen command execution patterns tied to Trivy workflows and began investigating those indicators in parallel with emerging public reporting. The findings aligned with reports that this was the second Trivy-related compromise in March, following an earlier February 28 repository breach
Background: Two Incidents, One Root Cause
To understand the March 19 attack, you need to understand what happened three weeks earlier.
Incident 1: The hackerbot-claw Campaign (Feb 27 – Mar 2, 2026)
An autonomous AI bot called hackerbot-claw exploited a pull_request_target misconfiguration in Trivy’s GitHub Actions workflows to steal a Personal Access Token, ultimately achieving a full repository takeover. Aqua Security rotated secrets and tokens after discovery – but the rotation wasn’t atomic, and that gap is what enabled the second attack.
We covered the hackerbot-claw campaign in depth in a separate post
Incident 2: The TeamPCP Attack (March 19, 2026)
Initial Access: Credential Reuse
The attackers – believed to be TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce, CipherForce) – retained access to credentials that survived the incomplete rotation from Incident 1. They also compromised the aqua-bot service account.
Using these credentials, they made impersonated commits:
- Pushed to
actions/checkoutwhile spoofing GitHub userrauchg - Pushed to
aquasecurity/trivywhile spoofing userDmitriyLewen
At 17:43:37 UTC, the v0.69.4 tag was pushed to the Trivy repository, triggering an automated release pipeline. This propagated malicious binaries to:
- GitHub Releases
- Docker Hub
- GHCR (GitHub Container Registry)
- Amazon ECR
Additional credentials stolen during this phase included GPG keys and credentials for Docker Hub, Twitter, and Slack, all exfiltrated to a Cloudflare Tunnel C2 endpoint (plug-tab-protective-relay.trycloudflare.com). Using Cloudflare Tunnels for C2 is a deliberate evasion choice: traffic blends with legitimate Cloudflare network activity.
Attack Flow: How It Actually Worked
1. Abusing Trust in Mutable GitHub Action Tags
The most important technical detail in this attack is not the malware itself – it’s the delivery method. The attacker force-pushed 75 existing tags in aquasecurity/trivy-action to new malicious commits. Any GitHub Actions workflow referencing a tag like aquasecurity/[email protected] automatically resolved to whatever commit that tag pointed to at runtime. Once the tag was moved, the workflow consumed attacker code without any change in the workflow file itself.
The malicious commits reused original author metadata, timestamps, and commit messages, but swapped in a poisoned entrypoint.sh while keeping the rest of the tree aligned with the latest master state. This created a deceptive appearance in Git history while changing the code that actually executed inside the runner.
A forensic tell identified was that each malicious commit claimed a timestamp from the original release year (2021, 2022, etc.) but had a parent commit dated March 2026 – physically impossible. Additionally, the malicious commits modified only entrypoint.sh, whereas original commits typically touched multiple files. Any team diffing a tag against HEAD would have caught this. Most don’t.
A compounding problem: the attacker may have deliberately published GitHub Immutable Releases when poisoning the tags. GitHub’s “Immutable” badge was intended as a trust signal – this attack demonstrates it cannot be relied upon in isolation to verify tag integrity.
2. The Malicious Payload Ran Before the Legitimate Scan
The attacker’s entrypoint.sh contained both the malicious logic and the legitimate Trivy scanning logic. The malware ran first, then the expected scan continued, making the workflow appear to complete normally. That blend-in execution is especially dangerous in CI/CD because developers have no reason to suspect anything wrong when their security scan produces the expected output.
3. Three-Stage Payload: Collection, Encryption, Exfiltration
Stage 1 – Collection
The malware targeted what matters most in CI/CD: credentials already present in the runtime. It searched for GitHub Actions runner processes (Runner.Worker, Runner.Listener, runsvc, run.sh) and extracted credentials via two methods:
/proc/<pid>/environ– harvested plaintext environment variables and file-backed secrets associated with SSH or environment-based auth material/proc/<pid>/mem– read directly from runner process memory to extract secrets that were never exposed as environment variables at all, searching for the internal GitHub pattern{"value":"<secret>","isSec...
This memory-reading approach is significant: it bypasses GitHub’s log-masking entirely, since extraction happens in memory before any masking occurs. Upwind’s eBPF sensors, running on affected runners, directly observed this in progress – capturing python3 reading Runner. Worker process memory during compromised workflow runs. A cross-process memory read like this has no legitimate use in a CI/CD context.
The credential collection scope was broad and targeted specifically at cloud-native environments:
- SSH private keys (
~/.ssh/) - Cloud credentials: AWS, GCP, Azure
- Kubernetes secrets (
~/.kube/config) - Docker registry credentials (
~/.docker/config.json) - Database credentials and Terraform state
- Git credential helpers and shell histories
- SSL private keys
- Solana validator keypairs and cryptocurrency wallet files
Stage 2 – Encryption
Collected data was encrypted with AES-256-CBC, with the encryption key itself wrapped using an RSA-4096 public key. This means network-layer inspection tools see only opaque blobs, defeating signature-based detection on the wire.
Stage 3 – Exfiltration (with fallback)
Primary: HTTP POST to scan.aquasecurtiy[.]org (note the typo – “securtiy” – resolving to 45.148.10.212, hosted in Amsterdam). Network telemetry from affected runs showed outbound HTTPS connections to this host alongside normal Trivy download traffic – easy to miss in noisy pipeline logs.
Upwind’s network monitoring flagged these outbound connections against established baselines, surfacing the anomaly even though the traffic blended with expected Trivy activity.
Fallback: If the primary C2 was unreachable and the action had access to a GitHub PAT, the malware used the token to create a public repository named tpcp-docs in the victim’s own GitHub account to stage the stolen data. This uses the victim’s own trusted infrastructure as an exfiltration channel – outbound connections to github.com are almost never blocked or alerted on.
4. setup-trivy Was Also Implicated
A malicious commit (8afa9b9) in aquasecurity/setup-trivy injected a similar credential stealer into action.yaml. All version tags except v0.2.6 were removed during incident response. Notably, the malicious commit did not belong to any branch on the repository – itself a suspicious signal when reviewing GitHub Action provenance.
This matters because many organizations use both setup-trivy and trivy-action in different workflows. Even teams that didn’t invoke the scanning action directly could have been exposed if installation workflows referenced the compromised action path during the affected window.
5. Persistence on Developer Machines
If the malware detected it was running on a developer workstation rather than an ephemeral CI runner, it deployed a systemd service configured to run sysmon.py – a polling implant that reaches back to the attacker’s server to fetch and execute further payloads. This extends the blast radius well beyond a single pipeline run.
Immediate Remediation Checklist
If you use Trivy, trivy-action, or setup-trivy in any pipeline, take these steps now:
Verify you’re on safe versions:
trivy→ v0.69.3trivy-action→ v0.35.0setup-trivy→ v0.2.6
Assess exposure:
- Identify any pipeline runs using
trivy-actionorsetup-trivybetween ~17:00 and 23:13 UTC on March 19, 2026 - Search your GitHub organization for any public repository named
tpcp-docs– its presence indicates successful exfiltration via the fallback mechanism - Check DNS/network logs for queries to
scan.aquasecurtiy[.]organd connections to45.148.10.212
If you were exposed:
- Treat all pipeline secrets as compromised and rotate immediately
- Rotate cloud provider credentials (AWS, GCP, Azure) accessible from affected runners
- Revoke and reissue SSH keys present on runner environments
- Rebuild CI environments from clean, trusted base images
Long-Term Hardening Recommendations
1. Pin Actions to full commit SHAs, not version tags.
Replace uses: aquasecurity/[email protected] with uses: aquasecurity/trivy-action@<40-char-sha>. Tags can be rewritten. Commit SHAs cannot.
2. Audit pull_request_targetworkflows.
Any workflow using this trigger that also checks out PR code should be considered high risk. Separate the trusted workflow from the untrusted code execution, or eliminate the trigger entirely.
3. Apply least-privilege to runner tokens.
The GITHUB_TOKEN in most pipelines has far more permissions than required. Scope it explicitly using the permissions: key in your workflow YAML.
4. Treat your security tooling like any other dependency.
Trivy, Snyk, Semgrep, and similar tools run with elevated trust in your pipelines. Apply the same supply chain scrutiny to them that you apply to application dependencies – pin versions, verify checksums, and monitor runtime behavior.
5. Don’t rely on GitHub’s “Immutable” badge alone.
Verify tag integrity against known-good commit SHAs out-of-band. This attack demonstrated that the badge can be present on compromised artifacts.
6. Deploy runtime security on CI runners.
Ephemeral CI environments are blind spots for most security programs. Monitoring process behavior, network connections, and file access on runners – even short-lived ones – catches attacks that static analysis entirely misses.
How Upwind Security Helps
Detecting this kind of attack requires visibility that goes beyond static policy checks and into what’s actually executing at runtime. Upwind’s Cloud-Native Application Protection Platform (CNAPP) provides exactly that layer of defense:
- Runtime Behavioral Detection: Upwind’s lightweight eBPF sensors continuously baseline process behavior across your environments, including CI/CD runners. When compromised components execute, Upwind can flag the deviation from established runtime baselines, surfacing anomalous command execution patterns, even though the workflow output appeared completely normal.
- Process-Level Visibility into CI/CD Workloads: Upwind monitors process activity such as reads from
/proc/<pid>/memand/proc/<pid>/environ– the exact techniques the Trivy attacker used to harvest secrets from GitHub Actions runner memory. These behaviors are immediately surfaced as suspicious indicators.
Network Anomaly Detection: Upwind tracks outbound connections from workloads in real time. Exfiltration attempts to domains like scan.aquasecurtiy.org or unexpected HTTPS traffic from CI/CD runners are flagged.


