When the Security Scanner Became the Weapon — A Cyber Kill Chain Analysis of the Trivy Supply Chain Attack
Aqua Security's Trivy was compromised by TeamPCP, cascading into LiteLLM. A 7-phase Cyber Kill Chain and MITRE ATT&CK analysis of how incomplete credential rotation turned a single breach into a five-ecosystem catastrophe.
If You Can't Trust Your Security Tools, What Can You Trust?
On March 24, 2026, Trivy was running in CI/CD pipelines around the world, doing exactly what it was supposed to do: scanning container images for vulnerabilities, generating security reports, passing or failing builds. Business as usual.
Except this "security scanner" was no longer a security scanner.
Seventy-six of Trivy's GitHub Actions tags had already been replaced with malicious commits. Every time the scanner ran, after completing its legitimate vulnerability scan, it silently read the GitHub Actions runner's process memory to harvest secrets, encrypted them, and transmitted them to the attacker's server. And the scan results? They displayed normally. So nobody would notice.
The cruelest aspect of this attack is the weaponization of trust. Trivy is an open-source security scanner built by Aqua Security, an industry-standard tool with over 25,000 GitHub stars. The fact that "the tool you run for security" became the entry point for the attack raises fundamental questions about every developer's and security team's threat model.
The attacker: TeamPCP. Active since December 2025, they first breached Trivy in late February 2026 and spent roughly a month conducting a campaign across five ecosystems: GitHub Actions, Docker Hub, npm, Open VSX, and PyPI. Along the way, LiteLLM, an AI proxy package with 3.4 million daily downloads, was infected in a cascade, and a worm that automatically infects Kubernetes clusters was deployed.
There's a Latin saying: "Quis custodiet ipsos custodes?", Who watches the watchmen? TeamPCP demonstrated the answer: nobody was watching.
This article dissects the month-long campaign through the lens of the Cyber Kill Chain's 7 phases and the MITRE ATT&CK framework.
Timeline: A Month-Long Campaign
TeamPCP campaign timeline from late February to March 25 2026
Date | Event | Severity
Dec 2025 | TeamPCP begins operations (Docker API exploits, etc.) | Reconnaissance
Late Feb 2026 | Trivy GitHub Actions misconfig exploited; Argon-DevOps-Mgt PAT exfiltrated | Initial Breach
Mar 1, 2026 | Aqua Security attempts credential rotation, incomplete and non-atomic | Failed Response
Mar 19, 17:43 UTC | trivy-action 76/77 tags force-pushed; setup-trivy 7/7 tags replaced | Attack Launch
Mar 20, 2026 | CrowdStrike Linux sensor detects anomalous script execution | Detection
Mar 22, 2026 | Attacker re-establishes access. Malicious Docker images (v0.69.5, v0.69.6) pushed. 44 Aqua repos defaced in 2 minutes | Re-entry
Mar 23, 2026 | CVE-2026-33634 assigned (CVSS 9.4). Checkmarx KICS also confirmed compromised | Escalation
Mar 24, 10:39 UTC | Malicious litellm 1.82.7 published to PyPI | Cascade
Mar 24, 10:52 UTC | Malicious litellm 1.82.8 published (.pth mechanism added) | Escalation
Mar 24, 11:48 UTC | Security researcher Callum McMahon discloses LiteLLM compromise | Disclosure
Mar 24, ~13:38 UTC | PyPI quarantines affected versions (~3 hours of exposure) | Containment
Mar 25, 2026 | Microsoft publishes detection and defense guidance | Follow-up
Note the ~3-week dwell period from initial breach in late February to the active attack on March 19. During this time, the attacker reconnoitered infrastructure, registered domains, and prepared payloads. And even after Aqua's rotation attempt on March 1, the attacker retained valid access paths.
Kill Chain Analysis: 7 Phases
Cyber Kill Chain 7-phase analysis with MITRE ATT&CK TTP overlay
Phase 1: Reconnaissance
TeamPCP's first move was analyzing Trivy's GitHub Actions workflow configuration.
Trivy's GitHub Actions setup contained a critical misconfiguration: workflows triggered by external pull requests had access to repository secrets. This is one of the most well-documented dangerous patterns in GitHub Actions security. The pull_request_target event combined with excessive permissions is essentially handing secrets to external contributors.
The key asset the attacker discovered was a Personal Access Token (PAT) belonging to a service account called Argon-DevOps-Mgt (GitHub ID 139343333, created July 2023). Critically, this was a long-lived PAT, not a scoped GitHub App token, but a personal access token with broad permissions and no expiration. MFA was not required.
MITRE ATT&CK Mapping
Tactic
Technique
ID
TeamPCP Application
Reconnaissance
Active Scanning
T1595
GitHub Actions workflow configuration analysis
Reconnaissance
Gather Victim Host Information
T1592
CI/CD runner environment profiling
Resource Development
Obtain Capabilities
T1588
PAT acquisition via misconfigured workflow
The misconfiguration had existed since October 2025, approximately 5 months of exposed attack surface that nobody detected or remediated.
Phase 2: Weaponization
With the PAT secured, TeamPCP began building attack infrastructure.
First: typosquatting domain registration. scan.aquasecurtiy.org, did you notice the 'i' and 'y' are transposed in "security"? This subtle typo was crafted to deceive security analysts reviewing logs, making data exfiltration appear to flow to legitimate Aqua Security infrastructure.
Second: the malicious entrypoint.sh. The legitimate Trivy Action's entrypoint.sh is 2,855 bytes. TeamPCP's version: 17,592 bytes. Roughly 6x larger. The additional code was hidden behind the legitimate Trivy scan logic, with error handling deliberately silenced so failures produced no output.
The attacker also hardcoded a 4096-bit RSA public key. All exfiltrated data was encrypted with this key before transmission, making it impossible to inspect payloads even if network traffic was intercepted. The same RSA key was used across the Trivy, KICS (Checkmarx), and LiteLLM compromises, the key forensic link confirming a single campaign.
MITRE ATT&CK Mapping
Tactic
Technique
ID
TeamPCP Application
Resource Development
Develop Capabilities: Malware
T1587.001
Malicious entrypoint.sh (17,592 bytes)
Resource Development
Acquire Infrastructure: Domains
T1583.001
aquasecurtiy.org typosquatting domain
Resource Development
Develop Capabilities: Digital Certificates
T1587.003
4096-bit RSA key for data encryption
Phase 3: Delivery
Tag poisoning mechanism showing git push force redirected 76 of 77 tags
March 19, 2026, 17:43 UTC. TeamPCP executed git push --force using the stolen PAT.
This single command redirected 76 release tags (out of 77) in aquasecurity/trivy-action to malicious commits. All 7 tags in aquasecurity/setup-trivy were also replaced. The only survivor: trivy-action v0.35.0.
This is tag poisoning. Git tags are essentially pointers to specific commits. A git push --force redirects the pointer to a different commit, causing every workflow referencing that tag to execute malicious code.
The sophistication shows here. TeamPCP forged the Git commit metadata, author, commit message, timestamps, to make malicious commits appear as if written by legitimate Aqua Security developers. Timestamps were backdated to July 2024 to hide any trace of recent modification.
This attack is particularly dangerous because of how GitHub Actions references work:
# Dangerous: tag reference (mutable) - uses: aquasecurity/trivy-action@v0.69.4
Tags are mutable. The same tag can point to entirely different code tomorrow. Commit hashes are immutable. Hash pinning would have rendered this attack impossible.
MITRE ATT&CK Mapping
Tactic
Technique
ID
TeamPCP Application
Initial Access
Supply Chain Compromise: Software Supply Chain
T1195.002
trivy-action tag poisoning
Defense Evasion
Masquerading
T1036
Forged Git commit metadata (author, timestamps)
Persistence
Account Manipulation
T1098
Repository tag manipulation via stolen PAT
Phase 4: Exploitation
Once tag poisoning was complete, CI/CD pipelines worldwide began executing malicious Trivy. No developer needed to install anything or click anything. Already-configured workflows automatically fetched and ran the malicious code.
The malicious entrypoint.sh operated in 5 stages:
Stage 1: Process enumeration. Identified processes running on the GitHub Actions runner, specifically targeting Runner.Worker, the process holding workflow environment variables and secrets in memory.
Stage 2: Memory scraping. On GitHub-hosted runners, directly read /proc/{PID}/mem to dump process memory. On self-hosted runners, harvested credentials from the filesystem. Collected: SSH keys, cloud credentials (AWS/GCP/Azure including IMDSv2), Kubernetes secrets and service account tokens, Docker configs, .env files, shell history, and even cryptocurrency wallet seeds (Bitcoin, Ethereum, Solana, Monero).
Stage 3: Encryption and exfiltration. Collected data encrypted with AES-256-CBC, session key wrapped with the hardcoded RSA public key, bundled into tpcp.tar.gz, and transmitted to scan.aquasecurtiy.org.
Stage 4: Dual exfiltration path. A fallback exfiltration channel via GitHub releases was implemented in case HTTPS transmission failed.
Stage 5: Camouflage. The most cunning part: after all malicious activity completed, the legitimate Trivy scan runs normally. Workflow logs show the same scan results as always. Security teams reviewing logs would find nothing unusual.
Among the credentials exfiltrated was LiteLLM's PYPI_PUBLISH token. LiteLLM used Trivy in its CI/CD pipeline for security scanning, referencing an unpinned version.
MITRE ATT&CK Mapping
Tactic
Technique
ID
TeamPCP Application
Credential Access
Unsecured Credentials: Credentials in Files
T1552.001
.env, SSH keys, cloud token harvesting
Credential Access
OS Credential Dumping: /proc Filesystem
T1003.007
/proc/{PID}/mem memory scraping
Collection
Data from Information Repositories
T1213
K8s secrets, Docker config collection
Exfiltration
Exfiltration Over C2 Channel
T1041
AES-256-CBC + RSA wrapped HTTPS exfil
Defense Evasion
Indicator Removal
T1070
Legitimate Trivy scan as cover
Phase 5: Installation
Payload comparison litellm 1.82.7 source injection vs 1.82.8 pth file
March 24, 2026, 10:39 UTC. TeamPCP used the stolen PyPI token to publish litellm 1.82.7. Thirteen minutes later at 10:52 UTC, 1.82.8 followed. The two versions used different infection mechanisms, demonstrating how methodically the attacker operated.
litellm 1.82.7, Source Code Injection
A double-base64 encoded payload was inserted into litellm/proxy/proxy_server.py. It executed when running litellm --proxy or importing litellm.proxy.proxy_server. A conditional trigger, only developers using LiteLLM's proxy feature were infected.
litellm 1.82.8, .pth File Escalation
The second version, released 13 minutes later, was far more aggressive. A litellm_init.pth file was added. .pth files in Python's site-packages directory execute automatically on every Python interpreter startup. No import needed. It even runs during pip install operations. This is a vastly more effective infection mechanism than traditional import-based triggers.
Stage 2, Encryption and exfiltration: 32-byte AES-256-CBC session key generated, data encrypted, session key wrapped with RSA-OAEP, bundled into tpcp.tar.gz, POSTed to models.litellm.cloud.
Stage 3, Persistence and lateral movement: Backdoor installed at ~/.config/sysmon/sysmon.py with a systemd user service polling checkmarx.zone/raw every 5 minutes for additional payloads. Notably, a kill switch was built in that aborted execution if URLs contained youtube.com.
MITRE ATT&CK Mapping
Tactic
Technique
ID
TeamPCP Application
Execution
Event Triggered Execution
T1546
.pth file auto-execution on Python startup
Persistence
Create or Modify System Process: Systemd Service
T1543.002
sysmon.service registration (5-min polling)
Defense Evasion
Obfuscated Files or Information: Software Packing
T1027.002
Double-base64 encoded payload
Defense Evasion
Indicator Removal on Host
T1070
Silent error handling
Phase 6: Command and Control
TeamPCP's C2 infrastructure went beyond simple HTTP callbacks. They used the Internet Computer Protocol (ICP) blockchain as a C2 channel.
The advantages of blockchain-based C2 are clear. Traditional C2 servers can be neutralized through domain blocking or IP blacklisting. But C2 endpoints hosted on a blockchain are nearly impossible to take down, it requires breaking the consensus of a distributed network.
The mechanism, called CanisterWorm, stored commands in ICP blockchain canisters (smart contracts), with the sysmon.service on infected machines polling periodically.
A traditional C2 path was maintained in parallel: polling checkmarx.zone/raw every 50 minutes to download additional payloads. The "checkmarx" domain name was deliberate, Checkmarx's KICS GitHub Action was also compromised in the same campaign.
MITRE ATT&CK Mapping
Tactic
Technique
ID
TeamPCP Application
Command and Control
Web Service: Dead Drop Resolver
T1102.001
ICP blockchain canister as C2 channel
Command and Control
Encrypted Channel: Asymmetric Cryptography
T1573.002
RSA-4096 + AES-256 encrypted communications
Command and Control
Application Layer Protocol: Web Protocols
T1071.001
HTTPS polling (checkmarx.zone)
Command and Control
Multi-Stage Channels
T1104
Blockchain C2 + HTTPS polling dual channels
Phase 7: Actions on Objectives
Kubernetes worm lateral movement deploying privileged pods across cluster
TeamPCP's endgame was multi-layered.
Kubernetes worm: Using stolen K8s service account tokens, the worm enumerated all namespace secrets across the cluster, then deployed privileged pods named node-setup-{node_name} into the kube-system namespace on each node. These pods mounted the host filesystem and used chroot to directly access the node OS and install backdoors. Notably, Iranian clusters received a wiper component deploying privileged DaemonSets with a container named "kamikaze," while non-Iranian nodes received CanisterWorm.
44 repository defacement: March 22, 2026, 20:31:07–20:32:26 UTC. In exactly 79 seconds, 44 repositories in the aquasec-com GitHub organization were automatically defaced by script. All repositories were renamed with a "tpcp-docs-" prefix. This exposed source code for Tracee (runtime security tool), internal Trivy forks, CI/CD pipeline configurations, and K8s operators.
Five-ecosystem infiltration: From a single entry point (Trivy), the campaign spread across five software ecosystems:
GitHub Actions, trivy-action, setup-trivy tag poisoning
npm, 44 compromised packages, CanisterWorm distribution
Open VSX, VS Code extensions
PyPI, LiteLLM backdoor
For LiteLLM specifically, Wiz's analysis found it present in 36% of all cloud environments. Even with a ~3-hour exposure window, given its 3.4 million daily download volume, the potential blast radius was enormous.
MITRE ATT&CK Mapping
Tactic
Technique
ID
TeamPCP Application
Lateral Movement
Deploy Container
T1610
K8s privileged pod deployment (node-setup-*)
Impact
Data Destruction
T1485
Wiper deployed to Iranian clusters
Impact
Defacement: External Defacement
T1491.002
44 Aqua repos defaced (79 seconds)
Collection
Data from Cloud Storage
T1530
Cloud credential-based storage access
Lateral Movement
Use Alternate Authentication Material
T1550
Stolen service account tokens for K8s API
Incomplete Rotation, The Decision That Turned a Breach Into a Campaign
The most painful lesson from this incident isn't a technical vulnerability. It's a failure of response.
On March 1, 2026, Aqua Security detected the breach and attempted credential rotation. On the surface, this was the right response. Revoking compromised credentials and issuing new ones is incident response 101.
The problem: the rotation was incomplete and non-atomic.
"Non-atomic" means not all credentials were rotated simultaneously. Some credentials were revoked, but other access paths remained open. The attacker used still-valid credentials to re-establish access, launching a second wave on March 22 that pushed malicious Docker images and defaced 44 repositories.
GitGuardian's analysis captured it precisely: "What turned one breach into a campaign was the incomplete rotation."
This is the core problem of NHI (Non-Human Identity) governance. The Argon-DevOps-Mgt PAT was:
Created in July 2023, used for nearly 3 years without rotation
Excessively scoped, capable of repository tag manipulation, Docker image pushes, and organization settings changes
No MFA required, a single token granted full access
No usage monitoring, abnormal usage patterns (e.g., 76 simultaneous tag force-pushes) went undetected
As GitGuardian put it: "The hard problem is no longer finding a secret after it leaks. The hard problem is stopping that secret from becoming the attacker's next foothold."
Why Existing Defenses Failed
This incident exposes structural weaknesses in current software supply chain security at multiple levels.
Unpinned GitHub Actions
Most projects reference GitHub Actions by tag (@v1, @v0.69.4). Tags are mutable, a single git push --force makes the same tag point to entirely different code. Commit hash pinning (@a1b2c3d...) would have made this attack impossible, but most teams use tags for convenience. Post-incident, GitHub has begun recommending tag pinning more strongly, and Dependabot now supports hash pinning updates.
The Danger of Long-lived PATs
GitHub App tokens have limited scope, short lifetimes, and installation-level permission management. PATs carry broad user-level permissions with no default expiration. Had Argon-DevOps-Mgt's PAT been a GitHub App token:
Scoped to specific repositories and permissions, likely preventing tag force-push
Short-lived, so a late-February theft would have produced an expired token by March 19
More detailed usage logs enabling anomaly detection
"Nobody Scans the Security Scanner"
This is the most fundamental problem. Organizations use security tools to validate the security of source code, container images, and dependencies. But almost no one validates the security of the security tools themselves. When adding Trivy to CI/CD, how many teams reviewed its GitHub Actions workflow security configuration? Most rely on an implicit trust: "Aqua Security built it, so it must be safe."
This implicit trust is the core attack surface of supply chain attacks.
Rebuilding the Defense Line: The Cremit Perspective
Cremit defense points on kill chain showing NHI governance interception
Tracing this incident to its root cause leads to one conclusion: absent NHI (Non-Human Identity) governance.
1. NHI Inventory and Visibility
The Argon-DevOps-Mgt PAT existed unmanaged for 3 years. This is the norm, not the exception. In most organizations, NHI credentials, service accounts, PATs, API keys, OAuth tokens, are not tracked after issuance.
Cremit's NHI management provides a complete inventory of all non-human credentials across the organization. Which tokens are used where, when they were last accessed, and what permissions they hold, all visible at a glance. Had Aqua had this visibility, the excessive permissions and extended inactivity of the Argon-DevOps-Mgt PAT could have been identified and remediated proactively.
2. Real-time Credential Leak Detection
The moment TeamPCP scraped credentials from GitHub Actions memory, Cremit's secret detection engine can capture abnormal credential access patterns. It monitors credential exfiltration in CI/CD pipelines in real time, matching against known leak patterns (memory dumps, environment variable collection) to provide immediate alerts.
3. Rotation Verification and Completeness
Aqua's biggest mistake was incomplete rotation. Cremit verifies that all associated access paths are simultaneously revoked during credential rotation. If a single PAT is used across multiple systems, rotation remains flagged as "incomplete" until replacement is confirmed at every usage point.
4. CI/CD Pipeline Credential Monitoring
Cremit monitors access patterns for secrets used in CI/CD environments (GitHub Actions, Jenkins, GitLab CI). Anomalies like "a PyPI deployment token normally used once per week suddenly accessed from an external IP" are detected, potentially blocking the stolen PYPI_PUBLISH token before it could be used to publish malicious packages.
Five Things to Check Tomorrow
An actionable checklist from the lessons of this incident.
1. GitHub Actions Hash Pinning
Change all third-party GitHub Action references from tags to commit hashes. Configure Dependabot or Renovate to receive automatic updates even with hash pinning.
# Before (vulnerable) - uses: aquasecurity/trivy-action@v0.69.4
# After (safe) - uses: aquasecurity/trivy-action@a1234567890abcdef1234567890abcdef12345678
2. PAT to GitHub App Migration
Migrate all automation using long-lived PATs to GitHub App-based authentication. GitHub App tokens are scoped, short-lived (1 hour), and offer installation-level granular permission management.
3. Credential Inventory
Conduct an inventory of all NHI credentials in use across your organization (PATs, service account keys, API tokens, OAuth apps). Identify each credential's creation date, last used date, permission scope, and owner. Flag excessive permissions and long-unused credentials.
4. Rotation Procedure Verification
Verify that credential rotation procedures are atomic. When one credential is used across multiple systems, all usage points must be rotated simultaneously. As Aqua's failure demonstrated, rotating some while leaving others active hands the attacker a re-entry path.
5. "Security of Security Tools" Review
Review the security configuration of security tools used in your CI/CD pipeline (Trivy, Snyk, SonarQube, etc.). What permissions do they run with? What secrets can they access? Are their GitHub Actions workflow configurations secure?
TeamPCP declared on Telegram: "Many of your favourite security tools and open-source projects will be targeted in the months to come." Whether this threat is bluster or reality remains to be seen. But one thing is certain: the era of blindly trusting security tools is over.
Quis custodiet ipsos custodes?, The answer now depends on your NHI governance.
Argus by Cremit continuously scans your public and private repositories for exposed credentials, maps ownership across your teams, and automates rotation workflows. Start a 14-day free trial at argus.cremit.io.