DEV Community

Cover image for Inside the Trivy Supply Chain Compromise (CVE-2026-33634): 76 Hijacked Tags, Runner.Worker Memory Secret Theft & SHA Pinning
daniel jeong
daniel jeong

Posted on • Originally published at manoit.co.kr

Inside the Trivy Supply Chain Compromise (CVE-2026-33634): 76 Hijacked Tags, Runner.Worker Memory Secret Theft & SHA Pinning

What happens when the security scanner meant to protect your pipeline turns into the malware that steals your secrets? On March 19, 2026, that is exactly what happened to Trivy (aquasecurity). Trivy is the most widely adopted open-source vulnerability scanner in the cloud-native ecosystem, embedded in thousands of CI/CD pipelines as the aquasecurity/trivy-action GitHub Action — and by design it has access to pipeline secrets. Compromise a tool like that, and the attacker doesn't just get code: they get cloud credentials, SSH keys, and Kubernetes tokens — everything the pipeline touches.

This post breaks down the official advisory GHSA-69fq-xp46-6x23 (CVE-2026-33634, Critical) as the primary source: what happened, how the payload worked, and the SHA-pinning-based remediation ManoIT applied to its internal pipelines.

Trivy supply chain attack chain

1. What Happened — When a Security Tool Becomes the Weapon

This was not a single poisoned package. It was a multi-channel strike that hit GitHub Actions, release binaries, Docker Hub images, and package repositories simultaneously. The threat actor — TeamPCP (the payload calls itself "TeamPCP Cloud Stealer") — force-pushed 76 of 77 tags in aquasecurity/trivy-action and all 7 tags in aquasecurity/setup-trivy to malicious commits at 17:43 UTC on March 19. Less than an hour later, at 18:22 UTC, a forged v0.69.4 binary was distributed across GitHub Releases, GHCR, Docker Hub, ECR Public, deb/rpm, and get.trivy.dev.

It didn't stop there. Three days later, on March 22, the attacker used separately stolen Docker Hub credentials to push malicious v0.69.5, v0.69.6, and latest images, bypassing GitHub-based controls entirely. The same day, using a service account token (Argon-DevOps-Mgt) bridging two GitHub orgs, they defaced all 44 repositories in Aqua Security's aquasec-com org with a tpcp-docs- prefix and exposed proprietary source.

Time (UTC) Event Channel
Late Feb – 3/1 Initial breach, partial (non-atomic) credential rotation Release infra
3/19 17:43 trivy-action 76 + setup-trivy 7 tags force-pushed GitHub Actions
3/19 18:22 Forged v0.69.4 binary distributed (~3h exposure) All channels
3/20 05:40 trivy-action tags restored (~12h exposure closed) GitHub Actions
3/22 v0.69.5/v0.69.6/latest images (~10h), 44 repos defaced Docker Hub

2. Root Cause — Non-Atomic Credential Rotation

The March incident didn't come out of nowhere — it was a continuation of a supply chain attack that began in late February 2026. After the initial disclosure on March 1, credential rotation was performed but was not atomic — not all credentials were revoked simultaneously. While rotation dragged on over several days, the attacker used still-valid tokens to re-exfiltrate the newly rotated secrets, retaining the residual access that enabled the March 19 attack.

⚠️ Caution: In incident response, "we rotated the credentials" is not sufficient. The window between revocation and reissue becomes the channel for the second breach. Rotation must be atomic — bulk-invalidate old credentials and issue new ones as a single unit of work.

3. Breaking Down the Attack

3.1 trivy-action Tag Hijacking — @v0.34.0 Is a Pointer, Not a Contract

The core of this attack abused two by-design properties of Git and GitHub: mutable tags and self-declared commit identity. By default, tags are not immutable references. Anyone with push access can repoint an existing tag to an entirely different commit. The attacker force-pushed 76 tags to malicious commits and injected the payload into entrypoint.sh so it ran immediately before the legitimate Trivy scan. Pipelines looked normal while the stealer ran silently underneath. The imposter commits spoofed maintainer identities, but GitHub flagged them with "This commit does not belong to any branch on this repository."

3.2 Forging the v0.69.4 Binary — goreleaser --skip=validate

1) Push commit 1885610c
   -> swap actions/checkout reference to imposter commit 70379aad
   -> composite action that downloads malicious Go source from a typosquatted domain
2) Add --skip=validate to goreleaser -> disable binary integrity validation
3) Tag that commit as v0.69.4 -> trigger the release pipeline
Enter fullscreen mode Exit fullscreen mode

A single line that turns off build validation (--skip=validate) nullified the entire integrity guarantee.

3.3 The Payload — Reading Secrets Straight Out of Runner.Worker Memory

The most dangerous technique bypassed GitHub Actions' secret masking head-on.

# Payload behavior (reconstructed)
# 1) Scan environment variables of all runner processes
cat /proc/*/environ          # first-pass collection of SSH keys / env secrets

# 2) Key technique: read the Runner.Worker process memory directly
#    A base64-encoded Python payload targets values marked isSecret:true
#    -> secrets that show as *** in logs are extracted in plaintext from memory
python3 -c '... read /proc/<worker_pid>/mem ...'

# 3) Sweep 50+ paths: AWS/GCP/Azure tokens, k8s configs,
#    Docker configs, Terraform state, Git credentials, wallets, shell history
Enter fullscreen mode Exit fullscreen mode

Collected data was AES-256-CBC + RSA-4096 hybrid encrypted and exfiltrated via HTTPS POST to the typosquatted C2 domain scan[.]aquasecurtiy[.]org (mimicking Aqua's name, 45.148.10.212 in Amsterdam). If the C2 was unreachable and a GitHub PAT was present, the malware created a public tpcp-docs repository on the victim's account and uploaded the stolen data as release assets — turning GitHub itself into the exfiltration channel.

4. Affected vs. Safe Versions

The lesson is blunt: the safe reference is a full commit SHA, not a version tag. Why the safe versions are safe is telling too — v0.69.3 and trivy-action v0.35.0 were protected by GitHub Immutable Releases (enabled 3/3 and 3/4, before the attack).

Component Affected Exposure Safe
trivy binary v0.69.4 ~3h (3/19) v0.69.3 or earlier
trivy (Docker Hub) v0.69.5, v0.69.6, latest ~10h (3/22–24) v0.69.3 or earlier (digest-pinned)
trivy-action tags 0.0.1–0.34.2 (76) ~12h (3/19–20) v0.35.0 (57a97c7) or SHA-pinned
setup-trivy all releases ~4h (3/19) v0.2.6 (3fb12ec) or SHA-pinned

⚠️ Caution: A malicious v0.70.0 push was stopped just before the tag landed, so treat any v0.70.0 reference in logs as suspicious. Also, mirror.gcr.io may still serve cached malicious images — reference by digest (@sha256:...). The force-pushed old tags (0.0.10.34.2) hardened into immutable releases and can't be recreated under the same names; they were re-published with a v prefix.

5. Detection & Response Playbook

You need a fast answer to "were we exposed, and if so, what do we rotate?" Treat every secret accessible to a workflow that ran a compromised version during the exposure windows as compromised.

# 1) Fallback exfil trace -- search org for tpcp-docs repos (presence = successful exfil)
gh repo list <ORG> --limit 1000 --json name \
  | jq -r '.[].name' | grep -i 'tpcp-docs'

# 2) Check 3/19-20 workflow run logs for trivy-action tag references
gh run list --repo <ORG>/<REPO> --created 2026-03-19..2026-03-20 \
  --json databaseId,workflowName,createdAt

# 3) Block C2 indicators at the network level + review historical connections
#    domain: scan[.]aquasecurtiy[.]org   IP: 45.148.10.212
Enter fullscreen mode Exit fullscreen mode

Then rotate all potentially exposed secrets atomically: GitHub tokens, cloud provider credentials, registry tokens, SSH keys, DB passwords. Audit for secondary compromise — unauthorized repos, unexpected workflow runs, infrastructure changes.

6. The Real Fix — SHA Pinning and Immutable Releases

The single most important lesson: mutable tags are a liability. Pin every third-party Action to a full commit SHA, and even if a tag is repointed via force-push, your workflow only runs the commit you intended.

# BAD -- mutable tag (can be force-pushed at any time)
- uses: aquasecurity/trivy-action@0.34.0

# GOOD -- full commit SHA pin + comment for version readability
- uses: aquasecurity/trivy-action@57a97c7  # v0.35.0
  with:
    scan-type: 'fs'
    severity: 'CRITICAL,HIGH'
- uses: aquasecurity/setup-trivy@3fb12ec    # v0.2.6
Enter fullscreen mode Exit fullscreen mode

Verify binaries and images with sigstore signatures, confirming both integrity and signing time (that it was signed before the 3/19 attack).

# sigstore verification for the binary
cosign verify-blob \
  --certificate-identity-regexp 'https://github\.com/aquasecurity/' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  --bundle trivy_0.69.2_Linux-64bit.tar.gz.sigstore.json \
  trivy_0.69.2_Linux-64bit.tar.gz
# Verified OK  -> additionally confirm the signing time predates the 3/19 attack
Enter fullscreen mode Exit fullscreen mode
Defense layer Action Attack surface closed
Reference immutability Pin Actions to full SHA, images to digest Tag hijacking / force-push
Integrity verification sigstore/cosign signature + signing time check Forged binaries / images
Release protection Enable GitHub Immutable Releases Release asset rewriting
Credential hygiene Atomic rotation, least-privilege tokens, short-lived OIDC Residual access / second breach
Runner monitoring Watch CI runners like production hosts /proc/mem secret theft

7. How ManoIT Responded Internally

ManoIT used this incident to roll the following into its GitOps/CI pipelines in stages. First, pin every third-party GitHub Action to a full SHA, with Renovate/Dependabot raising update PRs at SHA granularity. Second, digest-pin container base and scanner images, and exclude cache mirrors like mirror.gcr.io from the trust boundary. Third, migrate CI secrets from static PATs to OIDC-based short-lived tokens so that even if stolen, their lifetime is short. Fourth, write "atomic rotation" explicitly into the incident runbook — coupling revoke and reissue into a single operation to eliminate the time window. Fifth, attach runtime security (eBPF-based process/network monitoring) to CI runner nodes to detect /proc/<pid>/mem access and anomalous outbound traffic.

8. Closing — Your Security Tools Are Part of Your Attack Surface

The message of the Trivy compromise is clear: your security tools are part of your attack surface. Trivy holds privileged access to CI secrets by design, and that very privilege makes it an ideal target. Single-layer defense doesn't survive modern multi-channel, multi-stage supply chain attacks — only defense in depth, stacking reference immutability, integrity verification, credential hygiene, and runner monitoring, holds up. The project has since recovered its normal release cadence, shipping v0.71.0 (2026-06-01) as the latest stable, and the core lessons — SHA pinning and Immutable Releases — are now standard practice well beyond Trivy. The point isn't to distrust your tools — it's to pin your references immutably and prove their integrity.

Category Indicator (IoC)
C2 domain scan[.]aquasecurtiy[.]org
C2 IP 45.148.10.212 (Amsterdam)
Rogue trivy commit 1885610c
Malicious checkout commit 70379aad
Compromised setup-trivy commit 8afa9b9f9183b4e00c46e2b82d34047e3c177bd0
Exfil artifact public repos named tpcp-docs
Commit warning "This commit does not belong to any branch on this repository"

Originally published on the ManoIT blog. Cross-verified against the official advisory GHSA-69fq-xp46-6x23 / CVE-2026-33634. Version and IoC data are current as of 2026-06-04; re-check the official advisory before acting. · AI writing assist: Claude (Anthropic)


Originally published at ManoIT Tech Blog.

Top comments (0)