# Trivy tag hijack teardown: force-pushed tags and CI secrets

> How a malicious Trivy 0.69.4 and force-pushed trivy-action tags stole CI secrets from runner memory, and where Chainsaw would have stopped it.

Source: https://chain305.com/teardowns/trivy-tag-hijack/

---

# 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.

By [Muhammad Zeeshan Sultan](https://chain305.com/about/#founder), founder of Chain305 · October 2026

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.Worker` process 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, `.env` files 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-docs` on 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/trivy` 0.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-action` or `setup-trivy` by 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 by `scan-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_target` workflow 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.4` was 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.3` and `v0.69.5` do 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:

1.  **Read the workflow logs from 19 and 20 March.** Wiz points to the `Run Trivy` and `Setup environment` steps.
2.  **Search your GitHub organisation for `tpcp-docs` repositories.** One there suggests secrets were stolen through the fallback route.
3.  **Look for the network indicators** `scan.aquasecurtiy[.]org` and `45.148.10.212` in network logs, and block both.
4.  **On developer machines, look for `~/.config/systemd/user/sysmon.py`** and the systemd unit that runs the script, and remove both.
5.  **Move to the safe versions:** Trivy `v0.69.3` or `v0.69.2`, `trivy-action` `v0.35.0`, `setup-trivy` `v0.2.6`.

For the next one:

1.  **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.
2.  **Reference container images by digest.** The advisory lists images referenced by digest as not affected.
3.  **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](https://github.com/advisories/GHSA-69fq-xp46-6x23), 21 March 2026.
-   Aqua Security, [Trivy v0.69.4 supply chain compromise (discussion #10425)](https://github.com/aquasecurity/trivy/discussions/10425), 20 March 2026.
-   Aqua Security, [Trivy supply chain attack: what you need to know](https://www.aquasec.com/blog/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](https://www.wiz.io/blog/trivy-compromised-teampcp-supply-chain-attack).
-   StepSecurity, [Trivy compromised a second time: malicious v0.69.4 release](https://www.stepsecurity.io/blog/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.

[Subscribe to teardowns](https://chain305.com/teardowns/) [Read what a package firewall catches →](https://chain305.com/what-is-a-package-firewall/)
