The Trivy Supply Chain Attack: How a Compromised Security Scanner Stole Crypto Keys — And the CI/CD Hardening Playbook for DeFi Teams
Your vulnerability scanner just became the vulnerability. Here's what happened, why crypto projects are uniquely at risk, and 7 concrete defenses you can implement today.
On March 19, 2026, Aqua Security's Trivy — the most popular open-source vulnerability scanner in the cloud-native ecosystem, used by over 12,000 public repositories — was compromised for the second time in three weeks. The attacker injected credential-stealing malware into official GitHub Actions, Docker images, and binary releases. The malware specifically searched for Solana wallet credentials and crypto RPC authentication tokens alongside cloud credentials.
Microsoft's Defender team confirmed the full attack chain. StepSecurity detected it through anomalous outbound connections. And every CI/CD pipeline that ran trivy-action during the 12-hour window executed the attacker's code while displaying perfectly normal scan results.
If your DeFi project uses Trivy in CI — and statistically, many do — this is the wake-up call that your CI/CD pipeline is an attack surface, not just a development tool.
The Attack: Security Tooling Weaponized Against Its Users
Timeline
| Time (UTC) | Event |
|---|---|
| Feb 28, 2026 | First compromise: autonomous bot hackerbot-claw steals PAT via pull_request_target workflow |
| Mar 19, 00:00 | Attacker force-pushes 76 of 77 version tags in trivy-action, all 7 tags in setup-trivy
|
| Mar 19, ~00:00 | Malicious Trivy v0.69.4 published to GitHub Releases, Homebrew, Docker Hub |
| Mar 19, ~00:01 | Spam bots flood incident discussion to bury real reports |
| Mar 19, ~12:00 | Trivy team identifies and remediates compromise |
| Mar 22, 2026 | Docker Hub images v0.69.5 and v0.69.6 also confirmed compromised |
How It Worked
The attacker exploited Git's mutable tag design. Tags in Git are just pointers — they can be reassigned to different commits. By force-pushing all version tags to malicious commits, every pipeline referencing trivy-action@v0.34.0 (or any other version) silently started running attacker-controlled code.
The malicious entrypoint.sh added 105 lines that:
-
Located CI runner processes (
Runner.Worker,Runner.Listener) - Inspected process memory for secrets
- Decoded and executed a Python credential stealer
- Ran the legitimate Trivy scan — output looked completely normal
The Python stealer harvested:
✓ Cloud credentials (AWS IAM, GCP service accounts, Azure tokens)
✓ Kubernetes secrets (mounted service accounts, cluster-wide secret dump)
✓ CI/CD secrets (.env files, API keys in YAML/JSON)
✓ Infrastructure (WireGuard VPN configs, SSH auth logs, database connection strings)
✓ Slack/Discord webhooks
✓ Solana wallet variables and RPC authentication credentials ← crypto-specific
Everything was encrypted with AES-256-CBC + RSA and exfiltrated to scan.aquasecurtiy[.]org (note the typosquatted domain — securtiy instead of security).
Why Crypto Projects Are the Primary Target
The stealer's explicit targeting of Solana wallet credentials and RPC auth tokens isn't random. CI/CD environments for DeFi projects routinely contain:
- Deployer private keys — the keys that deploy and upgrade smart contracts
- Upgrade authority credentials — Solana program upgrade authorities, EVM proxy admin keys
- Hot wallet keys — for automated operations, treasury management
- RPC endpoint secrets — premium RPC providers (Alchemy, Helius, QuickNode) with mainnet access
- Multisig signer keys — individual signer keys for Safe, Squads Protocol
- API keys for oracles — Chainlink, Pyth, Switchboard configurations
A single compromised CI runner can yield the keys to drain an entire protocol. This is exactly what happened to Step Finance in January 2026 ($27.3M stolen via compromised keys) and countless smaller incidents that never make headlines.
The Deeper Problem: CI/CD Is DeFi's Unaudited Attack Surface
DeFi teams spend $50K-$500K on smart contract audits. They spend exactly $0 auditing their deployment pipeline.
Consider the typical DeFi project's CI/CD setup:
# .github/workflows/deploy.yml — what most DeFi projects look like
name: Deploy to Mainnet
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aquasecurity/trivy-action@v0.34.0 # Tag-based reference!
with:
scan-type: 'fs'
- name: Deploy contracts
env:
DEPLOYER_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}
RPC_URL: ${{ secrets.MAINNET_RPC }}
run: |
forge script script/Deploy.s.sol --broadcast --rpc-url $RPC_URL
Every third-party GitHub Action referenced by tag is a potential supply chain vector. The Trivy attack proved that even security-focused tools maintained by reputable companies can be weaponized.
The 7-Point CI/CD Hardening Playbook for DeFi Teams
1. Pin Actions by SHA, Never by Tag
Tags are mutable. Commit SHAs are not.
# ❌ Vulnerable — tag can be reassigned to malicious commit
- uses: aquasecurity/trivy-action@v0.34.0
# ✅ Secure — SHA is immutable
- uses: aquasecurity/trivy-action@062f2592684a31eb3aa050cc61312538b1c9a547
For every action in your pipeline, replace the tag reference with its full SHA:
# Get the commit SHA for a specific tag
gh api repos/aquasecurity/trivy-action/git/ref/tags/v0.34.0 \
--jq '.object.sha'
Use Dependabot or Renovate to manage SHA updates — they'll open PRs when actions release new versions, giving you a review checkpoint.
2. Restrict Network Access from CI Runners
The Trivy malware exfiltrated data via HTTP POST. If your CI runner can't reach arbitrary external domains, exfiltration fails.
# Use StepSecurity Harden-Runner to restrict network access
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v2
with:
egress-policy: audit # Start with audit mode, then switch to block
allowed-endpoints: >
github.com:443
api.github.com:443
rpc.mainnet-beta.solana.com:443
eth-mainnet.g.alchemy.com:443
In block mode, any connection to an unlisted domain is rejected. The Trivy exfiltration to scan.aquasecurtiy.org would have been blocked immediately.
3. Never Store Deployer Keys in CI Secrets
CI secrets are environment variables. Every process in the runner can read them — including compromised third-party actions.
Better alternatives:
For EVM deployments (Foundry):
# Use a hardware wallet or remote signer instead of raw private keys
forge script script/Deploy.s.sol \
--broadcast \
--ledger \ # Hardware wallet signing
--sender 0xYourAddress \
--rpc-url $RPC_URL
# Or use a KMS signer (AWS KMS, GCP Cloud KMS)
forge script script/Deploy.s.sol \
--broadcast \
--aws-kms-key-id "alias/deployer" \
--rpc-url $RPC_URL
For Solana deployments:
# Use Squads multisig for program upgrades — never a single key
# The CI pipeline proposes; humans approve via multisig UI
solana program write-buffer target/deploy/program.so
squads propose-upgrade \
--program-id <PROGRAM_ID> \
--buffer <BUFFER_ADDRESS> \
--multisig <SQUADS_ADDRESS>
The principle: CI should propose deployments, not execute them. Human approval via multisig is the final gate.
4. Isolate Deployment Steps from Scanning Steps
Never run security scans and deployments in the same job. A compromised scanner action in the scan job shouldn't have access to deployment secrets.
jobs:
# Job 1: Scan (no secrets)
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<SHA>
- name: Run Slither # Self-hosted tool, no third-party action
run: |
pip install slither-analyzer
slither . --json slither-results.json
- uses: actions/upload-artifact@<SHA>
with:
name: scan-results
path: slither-results.json
# Job 2: Deploy (secrets, but no third-party scanning actions)
deploy:
needs: security-scan
runs-on: self-hosted # Use a dedicated, hardened runner
environment: production # Requires manual approval
steps:
- uses: actions/checkout@<SHA>
- uses: actions/download-artifact@<SHA>
with:
name: scan-results
- name: Verify scan passed
run: python scripts/check-slither-results.py
- name: Deploy via multisig proposal
env:
PROPOSER_KEY: ${{ secrets.PROPOSER_KEY }} # Can only propose, not execute
run: forge script script/Propose.s.sol --broadcast
5. Use GitHub Environments with Required Reviewers
GitHub Environments add human-in-the-loop approval before secrets are exposed:
jobs:
deploy:
environment:
name: mainnet
url: https://etherscan.io/address/${{ env.CONTRACT }}
# This job won't start until an authorized reviewer approves in GitHub UI
Configure the mainnet environment to require approval from at least 2 team members. Combined with separated scanning/deployment jobs, this ensures no automated process can access deployment keys without human sign-off.
6. Monitor Runner Behavior with Egress Logging
Even with hardening, assume compromise and monitor:
# Log all outbound connections from your CI runner
- name: Capture network connections
if: always()
run: |
# Dump all connections made during the workflow
ss -tunap > /tmp/connections.log
cat /tmp/connections.log
# Check for unexpected domains
grep -v -E "(github\.com|registry\.npmjs\.org|crates\.io)" \
/tmp/connections.log && echo "⚠️ UNEXPECTED CONNECTIONS DETECTED"
StepSecurity's Harden-Runner provides this automatically with a dashboard. For self-hosted runners, consider deploying network monitoring via eBPF:
# Using cilium/hubble on self-hosted runners
hubble observe --from-pod ci-runner --protocol http \
--not --to-fqdn "*.github.com" \
--not --to-fqdn "*.npmjs.org" \
-o json > unexpected-traffic.json
7. Vendor Your Critical Dependencies
For maximum security, don't pull tools from external sources during CI at all:
# Dockerfile.ci — pre-built CI image with pinned tools
FROM ubuntu:22.04
# Install tools from verified sources with checksum validation
RUN curl -L https://github.com/foundry-rs/foundry/releases/download/v1.0.0/foundry-linux-amd64.tar.gz \
-o foundry.tar.gz && \
echo "abc123...expected_sha256 foundry.tar.gz" | sha256sum -c && \
tar xf foundry.tar.gz -C /usr/local/bin
RUN curl -L https://github.com/crytic/slither/archive/refs/tags/0.10.4.tar.gz \
-o slither.tar.gz && \
echo "def456...expected_sha256 slither.tar.gz" | sha256sum -c && \
pip install slither.tar.gz
Build this image in a separate, air-gapped pipeline. Use it as the base for all CI jobs. Update the image on a scheduled basis with manual review of each tool update.
Solana-Specific Considerations
Solana programs have additional CI/CD attack surfaces:
// anchor.toml — common pattern that leaks keys in CI
[provider]
cluster = "mainnet"
wallet = "~/.config/solana/deployer.json" // ← This file is the target
[programs.mainnet]
my_program = "ProgramID..."
[scripts]
deploy = "anchor deploy --provider.cluster mainnet"
Solana hardening checklist:
□ Program upgrade authority is a Squads multisig, not an EOA
□ Deployer keypair is NOT stored in CI secrets or filesystem
□ Buffer accounts are used for staged upgrades (write-buffer → propose → approve)
□ Verifiable builds are enabled (anchor build --verifiable)
□ Program is marked immutable (--final) when upgrades are no longer needed
□ IDL authority is transferred to multisig
□ Close authority on PDAs is restricted
The Real Lesson: Trust Is a Vulnerability
The Trivy compromise is a perfect case study in misplaced trust:
- Trust in tags → Tags are mutable pointers, not guarantees
- Trust in security tools → The scanner became the attacker
- Trust in remediation → The first fix was incomplete; attacker returned in 3 weeks
- Trust in CI isolation → Every action runs in the same environment by default
For DeFi teams, the stakes are existential. A compromised enterprise CI pipeline leaks source code and cloud credentials. A compromised DeFi CI pipeline leaks the keys to tens or hundreds of millions of dollars in user funds.
The protocols that survive the next supply chain attack won't be the ones with the best smart contract audits. They'll be the ones that treated their CI/CD pipeline with the same rigor as their on-chain code.
Quick Reference: Before and After
| Before (Vulnerable) | After (Hardened) |
|---|---|
| Actions pinned by tag | Actions pinned by SHA |
| Deployer key in CI secrets | KMS signer / hardware wallet |
| Scan + deploy in same job | Isolated jobs with separate permissions |
| Unrestricted runner network | Egress policy with allowlisted domains |
| Auto-deploy on push | Environment with required reviewers |
| Tools downloaded at runtime | Vendored in pre-built CI image |
| No network monitoring | Harden-Runner or eBPF egress logging |
This article is part of the DeFi Security Research series. The Trivy compromise is still developing — Docker Hub images v0.69.5 and v0.69.6 were confirmed compromised on March 22, and the campaign has expanded to Checkmarx KICS and LiteLLM. Pin your dependencies, audit your pipelines, and assume every third-party action is a potential vector.
Top comments (0)