From Scanner to Stealer: Inside the trivy-action Supply Chain Compromise BLOG Featured Now Live: The CrowdStrike 2026 Financial Services Threat Landscape Report May 14, 2026 Falcon AIDR Detects Threats at the Prompt Layer in Kubernetes AI Applications May 13, 2026 May 2026 Patch Tuesday: 30 Critical Vulnerabilities Among 130 CVEs May 12, 2026 Inside CrowdStrike Automated Leads: A Transformative Approach to Threat Detections May 11, 2026 Recent Video Video Highlights the 4 Key Steps to Successful Incident Response Dec 02, 2019 Helping Non-Security Stakeholders Understand ATT&CK in 10 Minutes or Less [VIDEO] Feb 21, 2019 Analyzing Targeted Intrusions Through the ATT&CK Framework Lens [VIDEO] Jan 22, 2019 Qatar’s Commercial Bank Chooses CrowdStrike Falcon®: A Partnership Based on Trust [VIDEO] Aug 20, 2018 Category Agentic SOC How Charlotte AI AgentWorks Fuels Security's Agentic Ecosystem 03/25/26 CrowdStrike Services and Agentic MDR Put the Agentic SOC in Reach 03/24/26 4 Ways Businesses Use CrowdStrike Charlotte AI to Transform Security Operations 03/12/26 Inside the Human-AI Feedback Loop Powering CrowdStrike’s Agentic Security 02/10/26 Cloud & Application Security 05/13/26 CrowdStrike Named a Leader in Frost & Sullivan 2026 Radar for Cloud-Native Application Protection Platforms 04/27/26 CrowdStrike Expands Real-Time Cloud Detection and Response to Google Cloud 04/22/26 CrowdStrike Falcon Cloud Security Delivered 264% ROI Through Unified Cloud Protection Threat Hunting & Intel 05/14/26 CrowdStrike Named a Leader in the First-Ever Gartner® Magic Quadrant™ for Cyberthreat Intelligence Technologies 05/06/26 CrowdStrike Launches Falcon OverWatch for Defender 05/05/26 Tune In: The Future of AI-Powered Vulnerability Discovery 05/01/26 Endpoint Security & XDR 05/11/26 CrowdStrike Falcon Platform Achieves 441% ROI in Three Years 04/21/26 Falcon for IT Supports Windows Secure Boot Certificate Lifecycle Management 04/01/26 Enhanced Network Visibility: A Dive into the Falcon macOS Sensor's New Capabilities 03/11/26 Engineering & Tech EMBER2024: Advancing the Training of Cybersecurity ML Models Against Evasive Malware 09/03/25 Falcon Platform Prevents COOKIE SPIDER’s SHAMOS Delivery on macOS 08/20/25 CrowdStrike’s Approach to Better Machine Learning Evaluation Using Strategic Data Splitting 08/11/25 CrowdStrike Researchers Develop Custom XGBoost Objective to Improve ML Model Release Stability 03/20/25 Executive Viewpoint Frontier AI Is Collapsing the Exploit Window.
Here’s How Defenders Must Respond. 04/20/26 Frontier AI for Defenders: CrowdStrike and OpenAI TAC 04/16/26 Anthropic Claude Mythos Preview: The More Capable AI Becomes, the More Security It Needs 04/06/26 The Architecture of Agentic Defense: Inside the Falcon Platform 01/16/26 From The Front Lines CrowdStrike Technical Risk Assessments Reveal Common Exposure Patterns 05/04/26 Introducing the CrowdStrike Shadow AI Visibility Service CrowdStrike Flex for Services Expands Access to Elite Security Expertise 03/20/26 Next-Gen Identity Security Detecting CVE-2026-20929: Kerberos Authentication Relay via CNAME Abuse 03/31/26 CrowdStrike FalconID Brings Phishing-Resistant MFA to Falcon Next-Gen Identity Security 02/26/26 CrowdStrike Named a Customers’ Choice in 2026 Gartner® Peer Insights™ Voice of the Customer for User Authentication 02/12/26 CrowdStrike to Acquire Seraphic to Secure Work in Any Browser 01/13/26 Next-Gen SIEM & Log Management Falcon Next-Gen SIEM Supports Third-Party EDR Tools, Starting with Microsoft Defender 03/23/26 Falcon Next-Gen SIEM Simplifies Onboarding with Sensor-Native Log Collection 03/06/26 Exposing Insider Threats through Data Protection, Identity, and HR Context 02/18/26 How to Scale SOC Automation with Falcon Fusion SOAR 02/11/26 Public Sector CrowdStrike Innovates to Modernize National Security and Protect Critical Systems 03/18/26 Falcon Platform for Government Now Offers Falcon for XIoT to Secure Connected Assets CrowdStrike Achieves FedRAMP® High Authorization 03/19/25 NHS Matures Healthcare Cybersecurity with NCSC’s CAF Assurance Model 03/13/25 Exposure Management 05/12/26 April 2026 Patch Tuesday: Two Zero-Days and Eight Critical Vulnerabilities Among 164 CVEs 04/14/26 How CrowdStrike Is Accelerating Exposure Evaluation as Adversaries Gain Speed 04/05/26 March 2026 Patch Tuesday: Eight Critical Vulnerabilities and Two Publicly Disclosed Among 82 CVEs Patched 03/10/26 Securing AI CrowdStrike Expands ChatGPT Enterprise Integration with Enhanced Audit Logging and Activity Monitoring 04/28/26 New CrowdStrike Innovations Secure AI Agents and Govern Shadow AI Across Endpoints, SaaS, and Cloud Secure Homegrown AI Agents with CrowdStrike Falcon AIDR and NVIDIA NeMo Guardrails 03/19/26 Introducing "AI Unlocked: Decoding Prompt Injection," a New Interactive Challenge Data Security Falcon Data Security Secures Data Wherever It Lives and Moves Falcon Data Protection for Cloud Extends DSPM into Runtime 11/20/25 CrowdStrike Stops GenAI Data Leaks with Unified Data Protection 09/18/25 Q&A: How Mastronardi Produce Secures Innovation with CrowdStrike 02/14/25 Start Free Trial The CrowdStrike Engineering team discusses how this activity was discovered, how the attack works, what the payload does, and how CrowdStrike helps organizations defend against this threat.
March 20, 2026 Adam Cardillo - Ben Ellett - Travis Lowe - Radu-Emanuel Chiscariu While investigating a spike in script execution detections across several CrowdStrike Falcon® platform customers, CrowdStrike’s Engineering team traced the activity to a compromised GitHub Action named aquasecurity/trivy-action. This popular open-source vulnerability scanner is frequently used in CI/CD pipelines. Our investigation found that 76 of the scanner’s 77 release tags had been retroactively poisoned via git tag repointing, replacing the legitimate entry point with a multi-stage credential stealer.
The malicious code runs silently before the real scanner, so workflows appear to complete normally. Aqua Security has officially confirmed the compromise of the Trivy GitHub Action script, setup script, and binary, and has subsequently quickly removed all malicious artifacts from their repositories. Additionally, CrowdStrike coordinated with Aqua Security prior to this publication. In this blog, we discuss how this activity was discovered, how the attack works, what the payload does, and how CrowdStrike helps organizations defend against this threat.
GitHub Actions and Why They Matter GitHub Actions is the CI/CD platform built into GitHub. When a developer pushes code, opens a pull request, or merges a branch, GitHub Actions can automatically build, test, and deploy that code. GitHub Actions is used by millions of repositories from small open-source projects to enterprise production pipelines. Workflows are defined in YAML files that live in a repository's .github/workflows/ directory.
Each workflow is composed of jobs, and each job runs a series of steps on a runner — a virtual machine or container provisioned to execute the work. These runners have access to the repository's code, its configured secrets (API keys, deploy tokens, cloud credentials), and often broad network access to internal infrastructure. A key feature of GitHub Actions is the ability to reference reusable actions, which are prepackaged steps published by third parties.
Instead of writing custom scripts for common tasks like checking out code, setting up a language runtime, or running a security scanner, teams reference shared actions by name and version: steps: - uses: actions/checkout@v4 - uses: aquasecurity/trivy-action@0.24.0 When a workflow runs, the runner downloads the referenced action from GitHub, extracts it, and executes its entry point. This is where the trust model becomes critical: The runner executes whatever code the action contains, with full access to the runner's environment, secrets, and network.
Most teams reference actions by tag (e.g., @0.24.0 or @v2). Tags are human-readable and easy to update. But in Git, tags are mutable — they can be silently repointed to a different commit without any visible change to the release page, the tag name, or the published dates. This is the mechanism exploited in the compromise described below. aquasecurity/trivy-action is a GitHub Action published by Aqua Security that wraps its open-source vulnerability scanner, Trivy.
It's widely adopted for scanning container images, file systems, and infrastructure-as-code for known vulnerabilities as part of CI/CD pipelines. In summary, 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, all with full access to that pipeline's secrets, credentials, and infrastructure.
Initial Discovery An initial spike on Linux platforms linked to runners was observed on March 19, 2026. The detections were concentrated on GitHub Actions runners, and the script content didn't appear to align with benign CI/CD activity. Digging into the process tree, the full execution chain was traced to one of the affected runners: runc └── /bin/bash /home/runner/run.sh └── /bin/bash /home/runner/run-helper.sh └── Runner.Listener run └── Runner.Worker spawnclient 149 152 └── /usr/bin/bash --noprofile --norc -e -o pipefail /home/runner/_work/_temp/395479a1-…ded349.sh └── /bin/bash /home/runner/_work/_actions/aquasecurity/trivy-action/0.24.0/entrypoint.sh Reading from top to bottom: runc starts the container, which launches the runner's startup scripts (run.sh → run-helper.sh).
These start Runner.Listener, which connects to GitHub and waits for jobs. When a workflow is triggered, the Listener spawns Runner.Worker to execute it. The Worker runs each workflow step as a shell script in a temp directory — the 395479a1-…ded349.sh wrapper. That wrapper calls the action's entrypoint.sh, which is where the malicious code lives. From here, CrowdStrike began investigating the action itself.
What the Payload Does The malicious entrypoint.sh prepends approximately 105 lines of attack code before the legitimate Trivy scanner logic. An example of one of the malicious commits can be viewed here . It executes a multi-stage credential theft operation, then runs the real scanner so the workflow appears to succeed normally. Stage 1: Runner Process Discovery Initially, the script enumerates process IDs (PIDs) of GitHub Actions runner processes (Runner.Worker, Runner.Listener, runsvc, run.sh) and reads their /proc/{PID}/environ files, filtering for environment variables containing env or ssh in their names.
Stage 2: Platform-Specific Credential Theft The attack branches based on the runner environment: On GitHub-hosted Linux runners, a Base64-encoded Python script is decoded and executed with sudo. It locates the Runner.Worker process, reads its memory maps via /proc/{PID}/maps, and scrapes raw process memory via /proc/{PID}/mem looking for GitHub Actions secrets — specifically JSON structures matching "name":{"value":"...","isSecret":true}.
On self-hosted runners, a different Base64-encoded Python script performs broad filesystem credential harvesting. It targets the following: SSH private keys and configs Cloud credentials (AWS, Google Cloud, Azure) Kubernetes configs and service account tokens Docker registry credentials Database credentials (MySQL, PostgreSQL, MongoDB, Redis) CI/CD configs (GitLab CI, Travis, Jenkins, Drone) Infrastructure-as-code secrets (Terraform state/vars, Ansible, Helm) TLS/SSL private keys .env files and API keys Cryptocurrency wallet keys (Bitcoin, Ethereum, Solana validator keypairs, Cardano, and others) System files (/etc/passwd, /etc/shadow, shell histories) Stage 3: Encrypted Exfiltration Collected data is encrypted with a randomly generated AES-256-CBC session key, and the session key is wrapped with a hardcoded 4096-bit RSA public key.
The encrypted bundle (payload.enc + session.key.enc) is packaged as tpcp.tar.gz . This ensures only the adversary server with access to the corresponding RSA private key can decrypt the payload. Stage 4: Dual Exfiltration Channels The primary exfiltration channel is an HTTPS POST to scan.aquasecurtiy[.]org — a typosquatted domain mimicking Aqua Security's legitimate aquasecurity.com. If the endpoint returns a non-2XX response and INPUT_GITHUB_PAT is available, the script falls back to exfiltrating through GitHub itself.
It will create a public repository named tpcp-docs under the victim's GitHub account, create a timestamped release, and upload the encrypted bundle as a release asset. Stage 5: Cleanup and Camouflage All temporary files are removed. The remainder of the script is a functional copy of the real trivy-action entry point. It downloads and runs Trivy normally, producing expected scanner output. To an operator reviewing workflow logs, the step appears to have completed successfully.
Further Discoveries Every release tag of aquasecurity/trivy-action from GitHub was downloaded and scanned. This includes 77 tags total, spanning from 0.0.1 to 0.35.0. Each entrypoint.sh was checked against five indicator of compromise (IOC) patterns derived from the malicious payload. The result of this analysis is as follows: Metric Count Total tags scanned 77 Compromised 76 Clean 1 The only clean tag is 0.35.0.
Every other release from 0.0.1 through 0.34.2 currently serves a malicious entrypoint.sh . setup-trivy action was also compromised to execute the same malicious script. In addition to the compromised trivy-action GitHub Action, the trivy scanner version 0.69.4 was also compromised. When it executes, a script is dropped to ~/.config/sysmon.py . The script acts as a lightweight stage-1 loader. After an initial five-minute sleep (likely intended to outlast sandbox analysis timeouts), it enters a polling loop that contacts a command-and-control (C2) server approximately every 50 minutes.
The C2 is hosted on the Internet Computer (ICP) blockchain ( hxxps[://]tdtqy-oyaaa-aaaae-af2dq-cai[.]raw[.]icp0[.]io ), making it resistant to traditional domain takedowns. On each cycle, the loader fetches a URL from the C2, compares it against a locally cached state file, and if the URL is new, downloads the referenced binary to /tmp/pglog . It then marks the file executable and launches it as a detached process with all output suppressed.
The choice of filename mimics PostgreSQL logging, and the state file is dot-prefixed to stay hidden from casual directory listings. Error handling is deliberately silent throughout. Every operation is wrapped in bare except blocks that discard failures, ensuring the loader never crashes or produces visible output. Combined with the spoofed browser User-Agent and the lack of any logging, the script is designed to operate invisibly on a compromised host while allowing the attacker to rotate payloads at will by simply updating the URL served by the C2.
The binary also exhibits the same credential-stealing behavior as the compromised action. It scans for credentials, bundles them, encrypts them, and then exfiltrates the data via a post request. How It Was Done Git tags are mutable references. A lightweight tag is just a pointer from a name to a commit SHA. With write access to a repository, an attacker can force-push a tag to point to a different commit: git tag -f 0.24.0 <new-commit> git push -f origin refs/tags/0.24.0 The release page on GitHub doesn't change — same name, same dates, same description.
But the source archive that gets downloaded now contains different code. We confirmed this by comparing what the 0.24.0 tag currently delivers against its parent commit: Tag 0.24.0 resolves to commit e0198fd2b6e1679e36d32933941182d9afa82f6f: entrypoint.sh is 17,592 bytes First line after the shebang: _COLLECT_PIDS="$$" (the stealer) This commit is not reachable from the master branch — it's a dangling commit that exists only because the tag points to it The parent of that commit (57a97c7e7821a5776cebc9bb87c984fa69cba8f1) is the same commit that the clean 0.35.0 tag points to: entrypoint.sh is 2,855 bytes First line after the shebang: TRIVY_CMD="${TRIVY_CMD:-trivy}" (the legitimate scanner) This commit is reachable from master The SHA256 hashes tell the story: Source entrypoint.sh SHA256 Size Tag 0.24.0 (compromised) 18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a 17,592 bytes Parent commit (legitimate) 07500e81693c06ef7ac6bf210cff9c882bcc11db5f16b5bded161218353ba4da 2,855 bytes master branch (legitimate) 07500e81693c06ef7ac6bf210cff9c882bcc11db5f16b5bded161218353ba4da 2,855 bytes The malicious commit's metadata — author name, commit message ("Upgrade trivy to v0.53.0 (#369)"), and timestamps (2024-07-09) — all mirror a legitimate prior commit.
Git allows setting arbitrary author and committer dates via GIT_AUTHOR_DATE and GIT_COMMITTER_DATE environment variables, so these cannot be trusted. The diagram below shows the full attack chain. Figure 1. High-level attack chain of this threat CrowdStrike Falcon Detection and Protection CrowdStrike's script control detection caught this activity when the malicious entrypoint.sh executed on protected runners.
The script content exhibited behaviors inconsistent with CI/CD — including credential collection, encrypted data staging, and outbound exfiltration. These signals were sufficient to prevent the script and surface the activity for investigation. Figure 2. Process tree identified within the CrowdStrike Falcon Linux sensor Figure 3. Visualized process tree identified within the CrowdStrike Falcon Linux sensor Identifying Exposed Workflows via GitHub Audit Logs Organizations ingesting GitHub Enterprise audit logs into CrowdStrike Falcon® Next-Gen SIEM can proactively identify repositories exposed to the same attack vector.
The initial compromise leveraged pull_request_target , a workflow trigger that, unlike pull_request , executes the workflow definition from the base branch with write access to repository secrets. Any public repository using this trigger allows external contributors to invoke workflows that have access to credentials. The following Falcon LogScale query surfaces these repositories across your GitHub Enterprise environment: #Vendor="microsoft" #event.module="github" | #event.dataset="github.workflows" | event.action="workflows.created_workflow_run" | Vendor.event="pull_request_target" | Vendor.public_repo="true" | ts := formatTime("%Y-%m-%d %H:%M:%S", field=@timestamp, locale=en_US, timezone=UTC) | groupBy([Vendor.org, Vendor.repo], function=[ count(as="total_runs"), count(user.name, distinct=true, as="distinct_actors"), collect([user.name, Vendor.name, Vendor.head_branch, ts]) ], limit=max) Each result represents a public repository where external pull requests can trigger workflows with elevated permissions, which is the precondition for the credential theft described above.
Repositories where distinct_actors is high or includes unfamiliar user names warrant immediate review of their workflow YAML files for unsafe patterns, particularly actions/checkout referencing github.event.pull_request.head.sha , which would execute attacker-controlled code with access to the base repository's secrets. Detecting Post-Compromise Token Abuse If a runner was compromised and credentials were exfiltrated, the stolen tokens will likely be used from infrastructure the adversary controls, not the organization's CI runners.
The following Falcon LogScale query creates a behavioral baseline to detect when a GitHub authentication token is used from a network subnet it has never been associated with to push code to a repository it has never pushed to. Matching events for a token pushing from a new network subnet to a new repo represent a high-confidence indicator of stolen credential abuse, since legitimate developer activity rarely introduces both dimensions simultaneously. // Build 7-day baseline of known: token, /16 subnet prefix, and repo // Each unique combination of hashed_token, /16 subnet prefix, and repo seen in the baseline // This period is considered "established" — any triple not in this table is anomalous defineTable( query={ #Vendor="microsoft" #event.module="github" #repo!="xdr*" | #event.kind="event" | event.action="git.push" | Vendor.hashed_token=* | source.ip=* Vendor.repo=* | ip_prefix := replace(field=source.ip, regex="^(\d+\.\d+)\.\d+\.\d+$", with="$1") | groupBy([Vendor.hashed_token, ip_prefix, Vendor.repo], function=[], limit=max) include=[Vendor.hashed_token, ip_prefix, Vendor.repo], name="known_token_subnets_and_repo", start=7d, end=70m // Detect pushes from a new subnet to a new repo in the current window | #Vendor="microsoft" #event.module="github" #repo!="xdr*" // Check for any historical data for these hashed tokens (exlude new tokens) | match(file="known_token_subnets_and_repo", field=[Vendor.hashed_token]) // Group source IPs by /16 prefix to absorb corporate NAT rotation while // preserving cross-network detection — IPs within the same /16 are treated // as the same network origin // Drop events where (token, subnet and repo) already exists in baseline | NOT match(file="known_token_subnets_and_repo", field=[Vendor.hashed_token, ip_prefix, Vendor.repo]) // TUNING: Exclude known automation accounts — bot tokens operate across many subnets and repos by design, which would generate excessive false positives | user.name != /\[bot\]$/ | groupBy([Vendor.hashed_token, user.name], function=[ new_subnets := count(ip_prefix, distinct=true), target_repos := count(Vendor.repo, distinct=true), total_pushes := count(), collect([ Vendor.repo, source.ip, ip_prefix, source.geo.country_iso_code, Vendor.org, // TUNING: Require minimum push volume to reduce noise from single-push FPs // | total_pushes >= 2 Conclusion This compromise is a straightforward example of why mutable references in software supply chains are a risk.
Git tags are convenient, but they're pointers and not guarantees. A tag that resolved to safe code yesterday can resolve to something malicious today, and nothing on the release page will reflect the change. The mechanics here weren't novel. The attacker, with write access to the repository, force-pushed tags to a new commit containing a modified entry point and relied on the fact that most workflows reference actions by tag.
The payload was well-constructed since it runs before the legitimate tool, cleans up after itself, and lets the real scanner complete, so the workflow looks normal. And the delivery mechanism was just git push -f. For a defender, the takeaway is to pin your actions by commit SHA, monitor your CI/CD runners with the same rigor as production hosts, and treat any code that runs in your pipeline as code that runs in your infrastructure.
References Malicious Commit Copying Legitimate Commit: https://api.github.com/repos/aquasecurity/trivy-action/git/commits/e0198fd2b6e1679e36d32933941182d9afa82f6f Signed Original Commit: https://api.github.com/repos/aquasecurity/trivy-action/git/commits/6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 Aquasecurity GitHub announcement: https://github.com/aquasecurity/trivy/discussions/10425 Related Content Categories CONNECT WITH US FEATURED ARTICLES May 06, 2026 May 05, 2026 SUBSCRIBE Sign up now to receive the latest notifications and updates from CrowdStrike.
Sign Up See CrowdStrike Falcon ® in Action Detect, prevent, and respond to attacks— even malware-free intrusions—at any stage, with next-generation endpoint protection. See Demo CrowdStrike Achieves NCSC CIR Assurance for Incident Response Tycoon2FA Phishing-as-a-Service Platform Persists Following Takedown Privacy Request Info Contact Us 1.888.512.8906 Accessibility