惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
I
InfoQ
阮一峰的网络日志
阮一峰的网络日志
云风的 BLOG
云风的 BLOG
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
AWS News Blog
AWS News Blog
S
SegmentFault 最新的问题
T
Tailwind CSS Blog
The Hacker News
The Hacker News
GbyAI
GbyAI
P
Palo Alto Networks Blog
博客园 - 三生石上(FineUI控件)
Y
Y Combinator Blog
Stack Overflow Blog
Stack Overflow Blog
博客园 - Franky
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Cyberwarzone
Cyberwarzone
H
Help Net Security
S
Securelist
月光博客
月光博客
博客园 - 【当耐特】
T
Threatpost
T
Tenable Blog
G
GRAHAM CLULEY
博客园 - 司徒正美
I
Intezer
MyScale Blog
MyScale Blog
T
Threat Research - Cisco Blogs
P
Privacy & Cybersecurity Law Blog
The GitHub Blog
The GitHub Blog
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
Google DeepMind News
Google DeepMind News
C
Cybersecurity and Infrastructure Security Agency CISA
罗磊的独立博客
腾讯CDC
P
Privacy International News Feed
博客园_首页
The Cloudflare Blog
Cisco Talos Blog
Cisco Talos Blog
A
About on SuperTechFans
V
Vulnerabilities – Threatpost
A
Arctic Wolf
B
Blog RSS Feed
Recorded Future
Recorded Future
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
S
Security Affairs
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog

Devoriales - DevOps and Python Tutorials

Cloud & DevOps & AI Digest: The Week of Jun 28, 2026 Cloud & DevOps & AI Digest: The Week of Jun 20, 2026 Ansible for DevOps Engineers: Architecture, Core Concepts, and Hands-On Lab Login Must-Have Kubernetes CLI Tools Every Platform Engineer Should Know Login Login Login Why Your Best Engineers Are Quitting (And How to Stop It) Login ArgoCD Vulnerability: How the ServerSideDiff Feature Exposes Kubernetes Secrets Login How Kubernetes Controls What Your Containers Can Do Login Multi-AZ Is Not Disaster Recovery: What the AWS Bahrain Outage Finally Proved Is Claude Opus 4.6 Fast Mode Really Worth 6× the Price? Login Unlocking Higher Pod Density in EKS with Prefix Delegation AWS Regional NAT Gateway: What It Is and Why You Should Care Kubernetes 1.35 Timbernetes Release AWS re:Invent 2025: The Future of Kubernetes on EKS Debate Series: How Do We Control Deployment Order in Kubernetes? Debate Series: Should We Eliminate Kubernetes Secrets Entirely? Kubernetes CRDs Explained: A Beginner-Friendly Guide to Extending the Kubernetes API Reduce Cloud Cross-Zone Data Transfer Costs with Kubernetes 1.33 trafficDistribution Building Custom Bitnami Images: A Guide for Self-Hosted Container Images New Features in Kubernetes 1.34: An Overview From Free to Fee: How Broadcom's Bitnami Monetization Disrupts DevOps Infrastructure Claude Code Cheat Sheet: The Reference Guide Kubernetes Loses Enterprise Slack Status: Discord Among Platforms Being Considered Understanding Container Security: A Guide to Docker and Pod Security Container Patterns in Kubernetes: Init Containers, Sidecars, and Co-located Containers Explained AWS Launches Serverless MCP Server: AI-Powered Development Gets a Serverless Boost Valve Responds to Alleged Steam Data Breach Reports: What Users Need to Know ArgoCD 3.0: The Evolution Toward Secure GitOps Redis Returns to Open Source: The AGPLv3 Licensing Decision New Features in Kubernetes 1.33: An Overview Prometheus: How We Slashed Memory Usage IngressNightmare: Critical Ingress-NGINX Vulnerabilities and How to Check Your Exposure New Features in Kubernetes 1.32: An Overview What to Consider If You're Not Signing Up for Bitnami Premium Certified Kubernetes Administrator (CKA) Exam Updates for 2025 DeepSeek AI and the Question of the AI Bubble Python Tops the Tiobe Index: The Most Popular Programming Languages - January 2025 2024 in Review: IT Trends, Startups, and What’s Next Inside Argo: The Open-Source Journey Captured in a CNCF Documentary Running Docker on macOS Without Docker Desktop - updated with Kubernetes installation HashiCorp Rolls Out Terraform 2.0 at HashiConf, Keeps IBM Acquisition in the Shadows Is the EU Falling Behind in the Global AI Race? Prometheus Essentials: Node Exporter And System Monitoring Prometheus Essentials: Install and Start Monitoring Your App Prometheus Essentials: Introduction To Metric Types Kubernetes Pod Scheduling Explained: Taints, Tolerations, and Node Affinity Retrieval Augmented Generation (RAG) Explained for Beginners Like Me Using Sealed Secrets with Your Kubernetes Applications
Trivy Supply Chain Attack: When Your Security Scanner Becomes the Threat
Aleksandro Matejic · 2026-03-22 · via Devoriales - DevOps and Python Tutorials

Attacked workflows

Executive Summary

Incident

Supply chain attack on aquasecurity/trivy-action and the Trivy binary, March 2026

Scope

76 of 77 trivy-action version tags rewritten. Any pipeline referencing a tag before 0.35.0 was potentially compromised.

Attack method

Compromised credentials used to force-push malicious code into GitHub Action tags. The payload ran silently before the legitimate scanner and exfiltrated CI/CD secrets.

Data at risk

AWS credentials, GitHub tokens, Kubernetes service account tokens, SSH keys, Docker registry passwords, database credentials, and cryptocurrency wallet keys

Safe versions

trivy-action 0.35.0  •  setup-trivy v0.2.6  •  Trivy binary v0.69.3

Immediate action

Check workflows for any trivy-action tag other than 0.35.0. Rotate all pipeline secrets. Block scan.aquasecurtiy[.]org and IP 45.148.10.212 at the network level. Check your GitHub account for an unexpected repository named tpcp-docs.

Key takeaway

This attack exploited mutable Git tags — a risk that applies to every third-party GitHub Action you reference by tag. Pin your actions to full commit SHAs and treat your CI/CD runners as production infrastructure.

Introduction

Trivy is one of the most trusted open-source security scanners in the DevOps world. Maintained by Aqua Security, it is used by thousands of engineering teams to scan container images, filesystems, and Kubernetes clusters for known vulnerabilities. In March 2026, attackers compromised Trivy's own GitHub Actions. They rewrote 76 of 77 version tags to deliver a credential-stealing payload directly into CI/CD pipelines around the world.

This was not a theoretical risk. It was a real, active attack that ran silently inside GitHub Actions runners, collected secrets from the environment, encrypted them, and sent them to an attacker-controlled server. Every team that ran a Trivy scan during the window of compromise was potentially exposed.

This article explains exactly what happened, how the attack worked at a technical level, what data was at risk, what you should do right now, and how to prevent this class of attack from reaching your pipelines in the future. The analysis draws on reporting from The Hacker News, CrowdStrike's engineering team, Socket Security, and Aqua Security's own public disclosure.

Table of Contents

What Is Trivy and Why Should You Care?

Trivy is an open-source vulnerability scanner maintained by Aqua Security. It scans container images, code repositories, Infrastructure-as-Code files, and Kubernetes clusters for known CVEs (Common Vulnerabilities and Exposures). Many teams integrate it into GitHub Actions using the official action, aquasecurity/trivy-action, or set it up through aquasecurity/setup-trivy. Whether you use Trivy today or not, this attack matters to you — because the mechanism it exploited, trusted GitHub Actions running inside your pipeline with full access to your secrets, applies to any third-party action you reference by tag.

The trust teams place in Trivy is exactly what made this attack so effective. When your CI/CD pipeline runs aquasecurity/trivy-action, it runs code directly on your GitHub Actions runner. That runner has access to every secret you have configured in your repository, from AWS credentials to Kubernetes service account tokens. As CrowdStrike's engineering team noted in their post-compromise analysis: if an action's code is modified, whether by its maintainers or by someone who gained write access, every pipeline that references it will trust and execute the new code on its next run, with full access to that pipeline's secrets, credentials, and infrastructure.

What Happened: The Attack in Plain English

To understand this incident clearly, it helps to think of it as two separate attacks with two separate victim groups. The first attack targeted Aqua Security internally. The second attack targeted the thousands of organisations that trusted Trivy's release tags in their own pipelines. The only reason the second attack was possible is because the first one was not fully contained.

Act One: Aqua Security's own CI is compromised. In late February 2026, an automated bot called hackerbot-claw scanned public GitHub repositories for a well-known misconfiguration in GitHub Actions workflows. It found one inside Aqua Security's own aquasecurity/trivy repository. The misconfiguration involved a pull_request_target workflow — a GitHub Actions trigger that runs in the context of the base repository and carries write access to that repository's secrets, even when triggered by a pull request from a complete stranger on the internet. Critically, this workflow also checked out code from the incoming pull request and executed it, which meant an external contributor's code was running inside a privileged environment with access to Aqua Security's internal tokens. The attacker opened a pull request against the Trivy repository itself, triggered that workflow, and stole a Personal Access Token belonging to aqua-bot — a service account with write access across 33 workflows in the Aqua Security GitHub organisation. This had nothing to do with your pipeline or your workflows. This was a break-in to Aqua Security's internal CI infrastructure.

Act Two: downstream users are hit through tag poisoning. Aqua Security caught the first attack, rotated their secrets and tokens, and believed the problem was contained. As their VP of open source Itay Shakury later acknowledged, however, the rotation was not atomic — meaning credentials were rotated one by one over time rather than all at once in a single coordinated action. This gave the attacker a window to capture freshly rotated tokens before the old ones were fully invalidated. Using those surviving credentials, the attacker force-pushed 76 of 77 version tags in the aquasecurity/trivy-action repository, rewriting them to point at commits containing a malicious payload. This is the attack that affected downstream organisations. Every team that referenced a trivy-action tag other than 0.35.0 in their CI pipeline was now running malicious code, regardless of whether their own workflows were correctly configured. They were not compromised because of anything they did wrong — they were compromised because they trusted a release tag that had been silently rewritten.

The most dangerous words in incident response are "we rotated our secrets." Rotation means nothing if the attacker captured a new token before the old one was invalidated, or if not every credential surface was covered in a single coordinated action.

CrowdStrike confirmed the scope of the tag poisoning by independently downloading and scanning every release tag. Force-pushing a tag means rewriting it so it points to a different commit than it originally did — the tag name stays the same, the release page looks identical, but the code it delivers has been replaced. The only clean tag was 0.35.0. Every other release from 0.0.1 through 0.34.2 served malicious code.

A key evasion detail from CrowdStrike's analysis: the attacker forged the malicious commits' metadata. The author name, commit message, and timestamps all mirrored a legitimate prior commit. Git allows setting arbitrary author and committer dates via environment variables, so commit metadata alone cannot be trusted when investigating a compromise.

The Compromised Version: trivy 0.69.4

Separately from the tag poisoning of the GitHub Actions, the attackers also published a malicious version of the Trivy binary itself: version 0.69.4. This version was flagged by security researcher Paul McCarty and has since been removed from the repository. Version 0.69.4 started the legitimate Trivy process as normal, so nothing looked broken, and simultaneously executed the malicious code alongside it. This is a deliberate attacker technique: keep the expected behavior running so no alerts are triggered while malicious activity runs in the background.

How the Malware Worked: A Five-Stage Attack

Before getting into the technical stages, it is worth being precise about the moment of compromise for a downstream organisation, because this is the part the article so far has only implied. When your GitHub Actions workflow contains a line like uses: aquasecurity/[email protected], GitHub resolves that tag name to whatever commit it currently points to, downloads that code onto your runner, and executes it. There is no warning, no diff, no notification that the tag has been rewritten since the last time it ran. Your pipeline simply trusts the tag. So the moment of compromise was not when Aqua Security was breached, and it was not when the tags were rewritten. It was the next time your workflow ran after the poisoning — a normal-looking Trivy scan step that started with your secrets already injected into the runner environment, ran the malicious code first, stole everything it could find, and then handed off to the legitimate scanner so your pipeline showed a green check mark. From your perspective, nothing looked wrong.

The payload embedded in the compromised tags was a multi-stage credential stealer. CrowdStrike's engineering team identified approximately 105 lines of malicious code prepended to the legitimate entrypoint.sh before the real Trivy scanner logic. The stealer ran first, then handed off to the real tool so the workflow completed normally and operators saw a green check mark.

A critical detail is that the attack behaved differently depending on whether it was running on a GitHub-hosted runner or a self-hosted runner, because the attack surface is different in each environment.

Stage 1: Runner Process Discovery

The script began by enumerating the process IDs of GitHub Actions runner processes, specifically Runner.Worker, Runner.Listener, runsvc, and run.sh. It then read their /proc/{PID}/environ files, filtering for environment variables whose names contained "env" or "ssh". This gave the attacker a map of what credentials were present before deciding which harvesting path to follow.

Stage 2: Platform-Specific Credential Theft

On GitHub-hosted Linux runners, a Base64-encoded Python script was decoded and executed. It located the Runner.Worker process, read its memory maps via /proc/{PID}/maps, and scraped raw process memory via /proc/{PID}/mem to extract GitHub Actions secrets directly. It specifically looked for JSON structures matching the pattern "name":{"value":"...","isSecret":true}, which is how GitHub Actions stores secrets internally in the runner process.

On self-hosted runners, the attack took a broader approach. A separate script performed comprehensive filesystem credential harvesting, targeting SSH private keys and configs, cloud provider credentials for AWS, Google Cloud, and Azure, Kubernetes configs and service account tokens, Docker registry credentials, database credentials for MySQL, PostgreSQL, MongoDB, and Redis, CI/CD configuration files for GitLab CI, Travis, Jenkins, and Drone, Infrastructure-as-Code secrets from Terraform state files, Ansible, and Helm, TLS and SSL private keys, .env files, API keys, cryptocurrency wallet files including Solana validator keypairs, and system files including shell history.

Self-hosted runners are a larger target because they are persistent machines that accumulate credentials over time, unlike GitHub-hosted runners which are ephemeral and destroyed after each workflow run.

Stage 3: Encrypted Exfiltration

Collected data was not sent in plaintext. The stealer encrypted the bundle using AES-256-CBC with a randomly generated session key, and then wrapped that session key with a hardcoded 4096-bit RSA public key. The encrypted bundle was packaged as tpcp.tar.gz. This two-layer encryption means that even if network traffic is intercepted, only the attacker holding the corresponding RSA private key can decrypt the stolen data.

The primary exfiltration channel was an HTTPS POST to scan.aquasecurtiy[.]org — note the deliberate misspelling of "Aqua Security". The IP address behind this domain was 45.148.10.212. Both should be blocked at the network or DNS level immediately if you were running Trivy during the compromise window.

Stage 4: Fallback Exfiltration via GitHub

If the primary channel returned a non-2XX response, the stealer fell back to exfiltrating through GitHub itself. Using the captured INPUT_GITHUB_PAT environment variable, it created a public repository named tpcp-docs in the victim's own GitHub account, created a timestamped release, and uploaded the encrypted bundle as a release asset. This fallback uses GitHub's own API and requires no direct connection to attacker infrastructure, making it harder to catch with outbound traffic monitoring alone. If you find a repository named tpcp-docs in your GitHub account that you did not create, this is a strong indicator that exfiltration succeeded.

Stage 5: Cleanup and Camouflage

All temporary files were removed. The script then handed off to the legitimate Trivy scanner, which ran normally and produced the expected vulnerability report. From an operator reviewing workflow logs, the step appeared to have completed successfully with nothing to alert on.

Persistence on Developer Machines (trivy 0.69.4)

The compromised Trivy binary 0.69.4 went further. When executed on a developer's local machine, it dropped a Python script to ~/.config/sysmon.py. After an initial five-minute sleep, designed to outlast automated sandbox analysis timeouts, it entered a polling loop contacting a command-and-control server approximately every 50 minutes.

What makes this C2 infrastructure notable is where it was hosted: on the Internet Computer (ICP) blockchain, making it resistant to traditional domain takedowns since there is no registrar to contact or DNS record to remove. On each polling cycle, the loader fetched a URL from the C2, and if the URL was new, downloaded a binary to /tmp/pglog, a filename chosen to mimic PostgreSQL logging, then executed it as a detached background process with all output suppressed. This allowed the attacker to rotate payloads at will simply by updating the C2.

How Tag Poisoning Works: The Git Mechanics

A Git tag is simply a pointer from a human-readable name to a specific commit hash. When you write aquasecurity/[email protected] in a workflow file, GitHub resolves that tag name to whatever commit hash it currently points to, downloads that content, and executes it. The tag name is not cryptographically bound to any particular content.

CrowdStrike confirmed this by comparing what the 0.24.0 tag delivered during the attack against its parent commit. The tag resolved to commit e0198fd2b6e1679e36d32933941182d9afa82f6f, which contained an entrypoint.sh of 17,592 bytes. The legitimate version of that same file on the master branch was 2,855 bytes. The attacker simply ran:

# Force-push a tag to point at a malicious commit
# The release page on GitHub showed the same name, dates, and description as before
git tag -f 0.24.0 <malicious-commit-sha>
git push -f origin refs/tags/0.24.0

# The malicious commit also had forged metadata:
# Author name, commit message, and timestamp copied from a legitimate prior commit
# Git allows arbitrary date overrides via GIT_AUTHOR_DATE and GIT_COMMITTER_DATE

The release page on GitHub did not change. The tag name, the published date, the description — everything looked identical to before. The only way to detect the change was to compare the actual commit SHA that the tag resolved to against what it historically resolved to, which almost no team does in practice.

A Realistic Example: Your Pipeline Under Attack

Here is a standard GitHub Actions workflow that builds a Docker image and runs a Trivy scan before pushing to a registry. Tag 0.24.0 was confirmed by CrowdStrike as compromised, resolving to the malicious commit e0198fd during the attack window. Any tag other than 0.35.0 should be treated as potentially rewritten.

# .github/workflows/build-and-scan.yml
name: Build and Security Scan

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Build image
        run: docker build -t myapp:latest .

      # Tag 0.24.0 confirmed compromised by CrowdStrike (malicious commit e0198fd)
      # Every tag before 0.35.0 was rewritten — this is not a single named bad version
      - name: Run Trivy vulnerability scan
        uses: aquasecurity/[email protected]   # <-- tag was poisoned
        with:
          image-ref: 'myapp:latest'
          format: 'table'
          exit-code: '1'
          severity: 'CRITICAL,HIGH'

      - name: Push to registry
        run: docker push myapp:latest

When that Trivy scan step executed on a GitHub-hosted runner, the malicious entrypoint.sh ran first. It located the Runner.Worker process, read its process memory to extract secrets, and collected any credentials in the runner environment and filesystem. These would typically include values like the following.

# Secrets injected into the runner environment by GitHub Actions
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
DOCKER_REGISTRY_PASSWORD=myRegistryPass123
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DB_CONNECTION_STRING=postgresql://user:[email protected]:5432/prod
KUBECONFIG=/home/runner/.kube/config

# The stealer encrypted all of this with AES-256-CBC + RSA-4096,
# packaged it as tpcp.tar.gz, and POSTed it to scan.aquasecurtiy[.]org
# If that failed, it staged the encrypted bundle in a public GitHub repo: tpcp-docs

The Trivy scan then completed successfully and produced a normal vulnerability report. You saw a green check mark. The attacker had your AWS keys, your Docker registry password, your GitHub token, and potentially your Kubernetes cluster credentials. With AWS keys, an attacker can enumerate your infrastructure, read data from S3 buckets, extract secrets from Secrets Manager, and pivot to other services. With a Kubernetes service account token, they can list and exec into pods, read ConfigMaps and Secrets, and potentially gain cluster admin access depending on your RBAC configuration.

What to Do Right Now: Immediate Mitigation Steps

If there is any chance your pipelines ran a compromised version of Trivy, treat every secret that was available to those pipelines as already compromised. Here is the response plan in order of priority.

Step 1: Identify Your Exposure

Every tag of trivy-action other than 0.35.0 was confirmed compromised by CrowdStrike's independent analysis. Check your workflow files across all repositories.

# Search all your workflow files for Trivy references
grep -r 'trivy' .github/workflows/

# SAFE versions confirmed by Aqua Security
aquasecurity/trivy           v0.69.3 or later
aquasecurity/trivy-action    0.35.0 only (every other tag was rewritten)
aquasecurity/setup-trivy     v0.2.6 or later

# Also check your GitHub account for the fallback exfiltration repository
# If this exists and you did not create it, exfiltration succeeded:
# Repository name: tpcp-docs

Step 2: Rotate All Pipeline Secrets

Rotation means generating entirely new credentials, not just changing passwords on the same credential. The following categories were all targeted by the stealer and should be rotated if they were available to any affected pipeline.

  • Rotate all AWS access keys used by CI/CD pipelines and audit recent CloudTrail logs for unexpected API calls from unfamiliar IP addresses or regions.
  • Revoke and regenerate all GitHub Personal Access Tokens associated with automation. Check your GitHub audit log for pushes from unexpected network subnets.
  • Rotate Docker registry passwords and check for unauthorized image pushes or pulls during the compromise window.
  • Rotate database credentials and review connection logs for unexpected access.
  • Rotate Kubernetes service account tokens and review the API server audit log for unusual exec, list, or get operations.
  • Revoke any SSH keys that were present on affected runners or developer machines where Trivy 0.69.4 was installed.
  • If your runners had access to Terraform state files, Ansible vault keys, or Helm secrets, rotate those as well.

Step 3: Block the Exfiltration Infrastructure

Block the attacker's domain and IP address at your network or DNS level. Also block the blockchain-hosted C2 used by the Trivy binary persistence module.

# Block at your firewall or DNS sinkhole
# Primary exfiltration domain (note the deliberate misspelling)
Domain: scan.aquasecurtiy[.]org
IP:     45.148.10.212

# Blockchain-hosted C2 used by the Trivy binary persistence module
# Traditional domain takedown does not work against ICP-hosted endpoints
C2: hxxps://tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io

# Example: block the exfiltration IP using iptables
sudo iptables -A OUTPUT -d 45.148.10.212 -j DROP

# Check outbound proxy logs for past connections to any of these endpoints

Step 4: Update to Safe Versions and Pin to SHA

Update all workflow files to reference the confirmed safe versions, then immediately pin those references to full commit SHAs. The SHA pinning step is not optional — the next section explains why.

# BEFORE: Mutable tag — can be silently repointed to malicious code
uses: aquasecurity/[email protected]

# STEP 1: Update to the safe version
uses: aquasecurity/[email protected]

# STEP 2 (strongly recommended): Pin to the full commit SHA
# Get the SHA by running:
# git ls-remote https://github.com/aquasecurity/trivy-action refs/tags/0.35.0
# Then use the resolved SHA, not the tag itself
uses: aquasecurity/trivy-action@<40-character-sha>  # pinned to 0.35.0

Best Practices to Prevent This Class of Attack

The Trivy incident is a textbook supply chain attack. The target was not your application but the tools and automation that build it. These are the controls that would have contained or prevented the impact.

1. Pin GitHub Actions to Full Commit SHAs

This is the single most effective control against tag poisoning. A commit SHA is derived cryptographically from the content of the commit itself. You cannot change the content of a commit without changing its SHA. When you reference a GitHub Action by its full 40-character SHA, you are guaranteed to run exactly that code regardless of what happens to the tag. An attacker who rewrites a tag cannot affect a workflow pinned to a SHA, because the SHA and the tag are now independent references.

# SHA pinning makes tag poisoning irrelevant
# Get the SHA for a tag:
git ls-remote https://github.com/aquasecurity/trivy-action refs/tags/0.35.0

# Use the resolved SHA in your workflow:
uses: aquasecurity/trivy-action@<resolved-sha>  # pinned to 0.35.0

# Tools that can automate SHA pinning across all your workflows:
# - StepSecurity Harden-Runner
# - pin-github-action CLI
# - Dependabot (can pin and auto-update SHAs)

2. Audit the pull_request_target Trigger

The first Trivy breach began because a pull_request_target workflow was configured in a way that gave external contributors access to repository secrets. Unlike the standard pull_request trigger, pull_request_target runs in the context of the base branch and has write access to secrets. Any public repository using this trigger allows external pull requests to invoke workflows with elevated permissions. Search your workflow files for pull_request_target and review whether those workflows also run checkout steps referencing the pull request head.

3. Use OIDC Instead of Long-Lived Credentials

OpenID Connect (OIDC) allows GitHub Actions workflows to authenticate with cloud providers like AWS without storing long-lived access keys as repository secrets. Instead of a static AWS_ACCESS_KEY_ID that an infostealer can capture and use indefinitely, the workflow requests a short-lived token that expires after the workflow run. A stolen OIDC token is useless within minutes.

# OIDC-based AWS authentication: no long-lived keys stored as secrets
# deliver malicious code into this workflow.
- name: Configure AWS credentials via OIDC
  uses: aws-actions/configure-aws-credentials@00943011d9042930efac3dcd3a170e4273319bc8 # v5.1.0
  with:
    role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
    aws-region: eu-west-1
    # GitHub generates a short-lived OIDC token for this workflow run only
    # A stolen token from a compromised action would expire almost immediately

4. Monitor CI/CD Runners Like Production Infrastructure

CrowdStrike detected this attack because their Falcon sensor was running on affected runners and flagged script execution behaviors inconsistent with CI/CD activity, specifically credential collection, encrypted data staging, and outbound exfiltration. Most teams treat their CI/CD runners as disposable infrastructure with far less monitoring than a production server. This attack demonstrates that a runner is production infrastructure: it holds your secrets and has network access to your internal systems. Tools like Falco, StepSecurity's Harden-Runner, or a commercial EDR deployed on runners can detect the behavioral patterns this attack relied on.

5. Block Unexpected Outbound Connections from Runners

This attack exfiltrated data over HTTPS to an external domain. A network policy restricting outbound connections from CI/CD runners to a known allowlist would have blocked the primary exfiltration channel entirely. Self-hosted runners can be placed behind a firewall or proxy that enforces outbound rules. Blocking connections to anything other than your package registries, GitHub itself, and your internal infrastructure is a strong control.

6. Maintain a Secret Rotation Runbook

The Aqua Security team's incomplete rotation was not unusual. Rotating secrets across an entire organization under time pressure is genuinely difficult. The teams that do it effectively are the ones who have documented and practiced the process before an incident. A rotation runbook should map every credential to the systems that depend on it, define who owns the rotation for each credential type, specify how to verify that old credentials are fully revoked, and set a target time window for completing rotation end to end.

7. Subscribe to Security Advisories for Critical Tools

The Trivy incident was publicly disclosed quickly. Teams subscribed to Aqua Security's GitHub release notifications or monitoring the GitHub advisory database would have had actionable information within hours. For any tool running in your CI/CD pipeline with access to secrets, watching its repository for security announcements is worth the noise.

What This Tells Us About Supply Chain Security in 2026

The Trivy attack fits a pattern that has been building for several years. The XZ Utils backdoor, the Codecov breach, the SolarWinds incident, and now Trivy all share the same fundamental structure: attackers gaining access to a trusted tool and using that trust as the delivery mechanism for malicious code. The target is not a vulnerability in the tool itself but a compromise of the humans, credentials, or processes that maintain it.

The most dangerous tools are the ones you trust the most. A scanner that protects you from vulnerabilities is also a tool that runs code with access to your secrets on every single pipeline run. Trust in open-source software is not binary, and it cannot be set-and-forget.

What makes the Trivy incident particularly instructive is the cascading failure. The first attack succeeded because of an insecure workflow configuration. The response was incomplete because secret rotation was not atomic. The second attack succeeded because of that gap. After the Trivy compromise, researchers found that stolen credentials were then used to push malicious versions of several npm packages, spreading the attack further into the ecosystem.

There is no such thing as a fully trusted tool. Every external dependency you bring into your pipeline is a potential entry point. The question is not whether to trust open-source tools, you have to use them, but how to structure that trust so that a compromise of any single tool cannot cascade into a full infrastructure breach. SHA pinning, OIDC authentication, least-privilege secrets, and runtime monitoring on runners are not optional hardening steps. After this incident, they are the baseline.

Other Known GitHub Actions Supply Chain Attacks

The Trivy compromise did not happen in a vacuum. It is part of a clear and accelerating pattern of attackers targeting GitHub Actions as a delivery mechanism for credential theft. The table below summarises the most significant confirmed incidents. In each case the attack method is essentially the same as what happened to Trivy: a trusted action is compromised, version tags are rewritten to point at malicious code, and the payload harvests secrets from CI/CD runners. The difference between incidents is largely a matter of scale and how secrets were exfiltrated.

Repository / Action

Date

CVE

What happened

Impact

reviewdog/action-setup

Mar 11, 2025

CVE-2025-30154

Attacker exploited an automated contributor invite process to gain write access, then force-pushed the v1 tag to a malicious commit. Payload dumped runner secrets to workflow logs.

Root cause of the cascading tj-actions attack. 6 downstream reviewdog actions also compromised.

tj-actions/changed-files

Mar 10–14, 2025

CVE-2025-30066

All version tags rewritten to point at a malicious commit. Payload printed CI/CD secrets to workflow logs as double-encoded base64. Attacker impersonated the Renovate bot to disguise the commit.

Over 23,000 repositories affected. Initially targeted Coinbase, then expanded broadly. AWS keys, GitHub PATs, npm tokens, and RSA keys exposed in public workflow logs.

spotbugs/sonar-findbugs

Nov 2024

A maintainer added a PAT to a workflow for legitimate reasons. Attacker submitted a malicious pull request exploiting a pull_request_target trigger to steal that token. The stolen credentials were later used as the entry point for the tj-actions cascade.

Origin of the entire March 2025 cascade. Attack went undetected for over three months.

Beyond the incidents in the table above, the HackerBot-Claw campaign in February 2026 is worth singling out because it demonstrates something new: fully automated, AI-driven exploitation at scale. The bot claimed to have scanned over 47,000 public repositories in one week, looking for misconfigured pull_request_target workflows, and successfully exploited seven of them using five different attack techniques, each one tailored to the specific workflow configuration of the target. The repositories hit included microsoft/ai-discovery-agent (arbitrary code execution via CI workflow), DataDog/datadog-iac-scanner (filename injection), avelino/awesome-go (GitHub token exfiltration from a 140,000-star repository), project-akri/akri from the CNCF, RustPython/RustPython, and aquasecurity/trivy, which suffered the worst outcome of the group. The only target that came through intact was ambient-code/platform, where Claude Code was integrated as a CI code reviewer and detected the attack as a prompt injection attempt, refusing to follow the malicious instructions.

A few patterns stand out across all of these incidents that are worth internalising. First, the pull_request_target trigger appears repeatedly as the entry point, in the spotbugs case that started the 2025 cascade and in the first Trivy breach. It is a legitimate feature that is genuinely useful, but any public repository using it should be treated as a high-risk surface until it is audited. Second, mutable version tags are the common mechanism in every single case. Every attack reused the same technique: force-push a tag, point it at malicious code, let normal workflow execution do the rest. Third, the attacks are connected. The November 2024 spotbugs breach fed into the March 2025 reviewdog breach, which fed into the tj-actions breach, which exposed Coinbase credentials that were then potentially available to fund further attacks. The Trivy March 2026 breach was itself a second strike enabled by incomplete remediation of an earlier compromise. Supply chain attacks are not one-off events — they cascade.

Conclusion

The Trivy supply chain attack turned a security tool into a credential harvester. Attackers rewrote 76 of 77 version tags to deliver a multi-stage infostealer that collected cloud credentials, Kubernetes tokens, SSH keys, and database passwords, encrypted them with AES-256-CBC and RSA-4096, and exfiltrated them to infrastructure designed to evade detection. The persistence mechanism on the Trivy binary used a blockchain-hosted C2, making it resistant to traditional takedown. The forged commit metadata made the malicious commits look identical to legitimate ones.

If you use Trivy, check your workflow files right now. Update to safe versions. Rotate any secrets that were available to affected pipelines. Check your GitHub account for a repository named tpcp-docs that you did not create. And if you have not already done so, start pinning your GitHub Actions to full commit SHAs.

The broader lesson is that your security posture is only as strong as the trust you extend to your tools, and that trust must be earned continuously, not assumed permanently. Build systems that limit what any single compromised component can reach, keep credentials short-lived where possible, and maintain a rotation plan you can execute under pressure.

Indicators of Compromise

If you suspect your pipeline ran a compromised version of Trivy, the table below gives you the specific artifacts to hunt for. The domain and IP address are what the malware used as its primary exfiltration channel — check your outbound proxy or firewall logs for any connections to either of them during the period you were running trivy-action. T

he blockchain C2 address is the command-and-control server used by the persistence module in the Trivy 0.69.4 binary on developer machines. It cannot be taken down through normal registrar or DNS channels, so blocking it at the network level is the only reliable control.

The malicious commit SHA lets you verify whether a specific tag in your pipeline history actually pointed at the backdoored code — you can compare it against what your workflow resolved at runtime.

The two malicious files are what the Trivy binary drops when it detects it is running on a developer machine rather than in CI; if either path exists on a machine that ran Trivy 0.69.4, treat that machine as fully compromised. Finally, if the fallback exfiltration channel ran successfully, a repository named tpcp-docs will have been created in your own GitHub account with the stolen data staged as a release asset inside it.

Type

Indicator

Domain

scan.aquasecurtiy[.]org (note misspelling)

IP Address

45.148.10.212

C2 (blockchain)

tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0[.]io

Malicious commit

e0198fd2b6e1679e36d32933941182d9afa82f6f (trivy-action)

Malicious file

~/.config/sysmon.py (trivy binary persistence)

Malicious file

/tmp/pglog (payload dropped by sysmon.py)

GitHub repo

tpcp-docs (fallback exfiltration staging)

Archive

tpcp.tar.gz (encrypted credential bundle)

Quick Reference: Safe Versions

Package

Safe Version

Notes

aquasecurity/trivy

v0.69.3 or later

v0.69.4 was confirmed malicious. Remove it from all machines.

aquasecurity/trivy-action

0.35.0 only

All 76 other tags were rewritten. Pin to SHA after updating.

aquasecurity/setup-trivy

v0.2.6 or later

7 tags were force-pushed. Update and pin to SHA.

References