Teardown: the Trivy tag hijack
A security scanner's own action tags, re-pointed to steal CI secrets.
What happened on 19 March 2026, how credentials left over from an earlier breach became a release key, and where Chainsaw would and would not have stopped it.
Questions and answers
What happened?
On 19 March 2026, an attacker used a credential left over from an earlier breach to publish a malicious Trivy v0.69.4 and to force-push malicious commits onto the version tags of aquasecurity/trivy-action and aquasecurity/setup-trivy. The incident is tracked as GitHub advisory GHSA-69fq-xp46-6x23 and CVE-2026-33634. Aqua Security gives these exposure windows (UTC):
| Component | Window | Length |
|---|---|---|
trivy v0.69.4 (GitHub releases, GHCR, ECR Public, Docker Hub, deb, rpm) | 19 March 18:22 to about 21:42 | about 3 hours |
trivy-action | 19 March about 17:43 to 20 March about 05:40 | about 12 hours |
setup-trivy | 19 March about 17:43 to about 21:44 | about 4 hours |
aquasec/trivy 0.69.5 and 0.69.6, Docker Hub only | 22 March 15:43 to 23 March about 01:40 | about 10 hours |
The Docker Hub images on 22 March showed the attacker had regained access. Aqua’s own blog puts their publication at about 16:00 UTC; the advisory says 15:43. The malware calls itself TeamPCP. Aqua says that name could be a false flag but that the attribution is plausible.
How did the attacker get in?
The way into Trivy was a set of credentials that survived the cleanup of a breach three weeks earlier. The first incident, as described by StepSecurity, began on 28 February 2026, when an automated account called hackerbot-claw exploited a pull_request_target workflow in the Trivy repository and stole a personal access token, which led to a full takeover of the repository. Aqua disclosed the breach on 1 March and rotated secrets, but Aqua’s statement on the second incident says the rotation “wasn’t atomic”, and the attackers may have seen the new tokens.
The advisory describes how the v0.69.4 release was made. The attacker pushed a commit that swapped the workflow’s actions/checkout reference for an imposter commit. The imposter commit held a composite action, which downloaded malicious Go source from a look-alike domain. The attacker also added --skip=validate to the GoReleaser step and tagged the commit, which started the normal release pipeline. Aqua’s blog says a compromised aqua-bot service account triggered the release automation.
What changed?
In the Trivy attack, the version tags moved; the workflows that used them did not. The advisory says 76 of 77 trivy-action tags were force-pushed to malicious commits (Wiz counts 75 of 76), along with all seven setup-trivy tags, v0.2.0 to v0.2.6. A workflow with uses: aquasecurity/trivy-action@0.34.2 ran different code, without any change on the workflow’s side, on 19 March 2026.
Each malicious trivy-action commit, according to Aqua, changed only entrypoint.sh and copied the original commit’s metadata. StepSecurity found the malicious setup-trivy commit sat on top of a legitimate commit, reused its message, and was on no branch.
Pinning to a commit SHA protected users, with one exception the advisory lists: trivy-action pinned to a commit from before 9 April 2025.
What ran?
The Trivy payload of 19 March 2026 was a credential stealer that ran before the real scan, according to Wiz and the GitHub advisory. The code injected into trivy-action and setup-trivy:
- read the memory of the GitHub Actions
Runner.Workerprocess through/proc/<pid>/mem, looking for secrets; - searched more than 50 file paths for SSH keys, AWS, GCP and Azure credentials, Kubernetes tokens, Docker configs,
.envfiles and crypto wallets; - encrypted what it found with AES-256-CBC and RSA-4096 and sent it to
scan.aquasecurtiy[.]org, a misspelling of Aqua’s name; - if that failed and a GitHub token was available, created a public repository named
tpcp-docson the victim’s account and uploaded the data as a release asset.
The v0.69.4 binary phoned home to the same domain. Aqua says the binary also tried to persist on developer machines with a Python script and a systemd user unit that polled a command server hosted on the Internet Computer (ICP). The Docker Hub images 0.69.5 and 0.69.6 contained the same domain.
Would Chainsaw have stopped it?
Yes for the Trivy Docker images, which are refused from a built-in list of known-bad images; the action tags need a policy setting turned on. We measured this in our re-run of 58 attacks from 2026 against production Chainsaw (v0.22.118) on 19 October 2026:
- The Docker proxy refuses the bad images. The proxy’s built-in seed refuses
aquasec/trivy0.69.4 to 0.69.6 by tag, and the 15 digests published in the advisory on any registry. A refusal based on a list of known-bad images is not a day 0 block. - The action tags need a policy setting. pr-scan blocks a pull request that adds
trivy-actionorsetup-trivyby tag, or by SHA from a publisher outside the known list, so a re-pointed tag cannot reach a ref added through a gated pull request. Refs already in workflows are judged only byscan-actions --fail-on medium. Both refuse only as a required check with the Guard Action set to block mode. The Guard Action ships in warn mode. - The February break-in needs the same setting, and has a gap. Chainsaw flags a
pull_request_targetworkflow and blocks a pull request that adds one, but only as a required check with the Guard Action in block mode. Neither stops a fork pull request against a workflow that is already merged, which is how Trivy was hit. - The Go module was not refused on 19 October. That run graded this attack FULL, but the Go module
github.com/aquasecurity/trivy@v0.69.4was never refused, so our 25 October re-run against v0.22.127 corrected the outcome to PARTIAL. On v0.22.127 the default CISA KEV rule refuses that module on the install path from about day 7: CISA added CVE-2026-33634 to its known-exploited list on 26 March 2026, seven days after the attack.v0.69.3andv0.69.5do not match. The Docker images are unaffected by this rule. - One image gap is open. The advisory lists no 0.69.5 linux/amd64 digest, so the pull from GHCR or ECR is served.
- Pulls that bypassed the proxy got the malicious images. A runner pulling straight from Docker Hub was not protected.
What should you do?
The first thing to do is a full rotation of every secret an affected pipeline could reach. The Trivy compromise began on 19 March 2026. If any workflow ran trivy-action or setup-trivy by tag on 19 or 20 March 2026, or anything ran Trivy 0.69.4 or pulled aquasec/trivy 0.69.5 or 0.69.6 from Docker Hub, follow the advisory: treat every secret the pipeline could reach as stolen and rotate each one. Aqua lists cloud, Git, registry, SSH, Kubernetes and npm publish tokens.
Then check what happened:
- Read the workflow logs from 19 and 20 March. Wiz points to the
Run TrivyandSetup environmentsteps. - Search your GitHub organisation for
tpcp-docsrepositories. One there suggests secrets were stolen through the fallback route. - Look for the network indicators
scan.aquasecurtiy[.]organd45.148.10.212in network logs, and block both. - On developer machines, look for
~/.config/systemd/user/sysmon.pyand the systemd unit that runs the script, and remove both. - Move to the safe versions: Trivy
v0.69.3orv0.69.2,trivy-actionv0.35.0,setup-trivyv0.2.6.
For the next one:
- Pin GitHub Actions to full commit SHAs, not tags. Aqua notes that GitHub’s “Immutable” release badge does not stop a tag from being force-pushed.
- Reference container images by digest. The advisory lists images referenced by digest as not affected.
- Rotate all of a project’s credentials at once after an incident. The Trivy attack worked because the first rotation left a window the attacker could use.
Sources
- GitHub Advisory Database, GHSA-69fq-xp46-6x23 / CVE-2026-33634: Trivy supply chain compromise, 21 March 2026.
- Aqua Security, Trivy v0.69.4 supply chain compromise (discussion #10425), 20 March 2026.
- Aqua Security, Trivy supply chain attack: what you need to know, updated 1 April 2026.
- Wiz, Trivy compromised: everything you need to know about the latest supply chain attack.
- StepSecurity, Trivy compromised a second time: malicious v0.69.4 release.
One teardown per notable attack
Get the next teardown by email
What happened, the install-path angle most coverage misses, and the rule that refuses it.