Threat ID: TL-2026-0288 | Severity: CRITICAL | Status: ACTIVE
Actor: TeamPCP / Vect | Nation: Unknown | MITRE: 40+ techniques
Detections: 63 | IOCs: 39
On March 26, 2026, TeamPCP — the group behind the Trivy and LiteLLM supply chain compromises — announced a partnership with the Vect ransomware-as-a-service operation on BreachForums. The 300GB of compressed credentials stolen from 60,000+ compromised servers now feed directly into ransomware campaigns, with operators collecting 80–88% revenue share. We tracked 7 related threats across this campaign, mapped 85 MITRE ATT&CK techniques, and wrote 63 production-ready detections.
TeamPCP actor profile on Threadlinqs Intelligence — 6 tracked threats, 85 MITRE techniques, 54 detections, 160 IOCs. Aliases: PCPcat, Persy_PCP, ShellForce, DeadCatx3.
From Scanner to Supply Chain Weapon
The campaign started with a misconfigured pull_request_target workflow in Aqua Security's Trivy repository. On February 28, 2026, TeamPCP exploited this to steal a privileged Personal Access Token from the aqua-bot service account. A credential rotation on March 1 was incomplete — residual access through Argon-DevOps-Mgt remained.
By March 19, that residual access turned into a supply chain weapon. TeamPCP force-pushed malicious commits to 75 of 76 trivy-action tags and all 7 setup-trivy tags, converting pinned CI/CD references into payload delivery channels. The embedded infostealer harvested Runner.Worker process memory for GitHub PATs, AWS credentials, SSH keys, and Kubernetes secrets.
Four days later, the attack expanded. Checkmarx KICS GitHub Action fell next — 35 release tags hijacked between 12:58 and 16:50 UTC on March 23. Same day, two C2 domains were registered: models.litellm.cloud and checkmarx.zone. LiteLLM versions 1.82.7 and 1.82.8 hit PyPI on March 24. A package downloaded 3.4 million times per day. The exposure window was roughly 5.5 hours.
The .pth Trick
Version 1.82.7 used direct code injection — a base64 payload embedded in litellm/proxy/proxy_server.py, lines 128–139. It executes when the proxy server module imports. Version 1.82.8 took a harder approach.
A 34,628-byte file named litellm_init.pth was placed in the wheel root directory. Python's site.py processes all .pth files in site-packages during interpreter startup — not when LiteLLM is imported, but on every Python invocation. Running pip, running python -c, even IDE language servers. The .pth file survives package uninstallation because it sits outside the package's own directory. This is not a new technique (T1547.013 in MITRE), but seeing it weaponized at this scale against a top-50 PyPI package is something else.
Enrichment of models.litellm.cloud — the primary exfiltration endpoint — flagged MALICIOUS with 4 correlated threats on Threadlinqs Research.
332 Lines That Own Your Cloud
The credential harvester is 332 lines of Python. It methodically sweeps SSH keys (~/.ssh/id_rsa, id_ed25519), Git credentials, AWS configs (~/.aws/credentials), GCP service accounts, Azure managed identities, Kubernetes secrets (~/.kube/config), Docker registry tokens, npm tokens, Terraform state files, database credentials, and cryptocurrency wallet data. Environment variables matching API_KEY, TOKEN, or SECRET patterns get pulled too.
Everything is bundled into tpcp.tar.gz, encrypted with AES-256-CBC and wrapped with RSA-4096, then POST'd to https://models.litellm.cloud/. The X-Filename: tpcp.tar.gz header is a reliable network signature.
The third stage targets Kubernetes. The malware queries the local API endpoint, grabs the service account token from /var/run/secrets/kubernetes.io/serviceaccount/token, and deploys privileged pods to every node. The pod manifest requests hostPID: true, hostNetwork: true, securityContext.privileged: true, and mounts the host root filesystem at /host. Pod naming follows node-setup-{node_name} in kube-system namespace. restartPolicy: Never keeps forensic evidence minimal.
checkmarx.zone enriched on Threadlinqs Research — the persistence polling domain scores MALICIOUS with 10 OSINT pulses and 4 correlated threats.
The Vect Pivot
On March 26, TeamPCP made the jump from supply chain operator to ransomware affiliate. A BreachForums post announced partnership with Vect, an emerging ransomware-as-a-service operation. The deal: automatic affiliation for TeamPCP operators with 80–88% revenue share on ransomware deployments using the stolen credentials.
The numbers back the pivot. TeamPCP claims approximately 300GB of compressed credential data from 60,000+ compromised servers. The CanisterWorm npm component — a self-propagating worm that uses Internet Computer Protocol blockchain canisters as C2 — infected 64+ npm packages spanning 47+ unique package names. Stolen npm tokens feed the worm, which queries the npm API, discovers every publishable package, bumps the patch version, injects the payload, and republishes with --tag latest.
This is the part that shifts the calculus. A supply chain attack that steals credentials is bad. That same attack feeding a ransomware pipeline with proven access to 60,000 servers is an enterprise-scale incident. Organizations that ran Trivy v0.69.4, KICS GitHub Action, or LiteLLM 1.82.7/1.82.8 during the compromise windows need to assume credential theft and treat this as a breach — not a dependency update.
DNS enrichment cross-correlations on Threadlinqs — shared hosting infrastructure linking multiple threats across the TeamPCP campaign.
Detection
Threadlinqs Intelligence tracks 63 production-ready detections across the 7 TeamPCP campaign threats. Three key rules covering the most distinctive behaviors:
Splunk SPL — .pth Persistence, C2 Resolution, and Credential Harvesting
SPLindex=* (sourcetype=sysmon OR sourcetype=linux:audit OR sourcetype=osquery)
| search (file_path="*/site-packages/*.pth" AND file_name!="setuptools*" AND file_name!="distutils*")
OR (process_name="python*" AND (command_line="*subprocess.Popen*" OR command_line="*exec(base64*"))
OR (dest_ip IN ("models.litellm.cloud","checkmarx.zone") OR dns_query IN ("models.litellm.cloud","checkmarx.zone"))
| eval risk_score=case(
match(file_path,"\.pth$"), 90,
match(command_line,"exec.*base64"), 85,
isnotnull(dest_ip), 80,
true(), 60)
| stats count values(file_path) AS paths values(command_line) AS cmds values(dest_ip) AS ips by host src_ip
| where count > 0
Microsoft KQL — .pth File Creation, C2 Network Events, and Systemd Persistence
KQLlet c2_domains = dynamic(["models.litellm.cloud", "checkmarx.zone", "scan.aquasecurtiy.org"]);
let pth_events = DeviceFileEvents
| where FileName endswith ".pth" and FolderPath contains "site-packages"
| where FileName !startswith "setuptools" and FileName !startswith "distutils";
let c2_events = DeviceNetworkEvents
| where RemoteUrl has_any (c2_domains) or RemoteIP in ("83.142.209.11", "45.148.10.212");
let persistence = DeviceProcessEvents
| where ProcessCommandLine has_all ("systemctl", "--user", "enable")
and ProcessCommandLine has "sysmon";
union pth_events, c2_events, persistence
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, RemoteUrl, RemoteIP, ProcessCommandLine
Sigma — TeamPCP C2 Domain Resolution
SIGMAtitle: TeamPCP Supply Chain - Malicious .pth File and C2 Communication
id: 8f3a2b1c-4d5e-6f7a-8b9c-0d1e2f3a4b5c
status: experimental
description: Detects indicators of TeamPCP supply chain compromise including .pth file persistence, C2 domain resolution, and credential harvesting patterns
references:
- https://intel.threadlinqs.com/#threats/TL-2026-0288
author: Threadlinqs Intelligence
date: 2026/03/26
tags:
- attack.persistence.t1547.013
- attack.command_and_control.t1071.001
- attack.credential_access.t1552.001
logsource:
category: dns_query
detection:
selection_c2:
query|contains:
- 'models.litellm.cloud'
- 'checkmarx.zone'
- 'scan.aquasecurtiy.org'
condition: selection_c2
falsepositives:
- Legitimate checkmarx.com domains (note: checkmarx.zone is the malicious typosquat)
level: critical
Browse all 63 detection rules across 7 related threat reports: View on Threadlinqs Intelligence
Indicators of Compromise
Network Indicators
| Type | Value | Context |
|---|---|---|
| Domain | models.litellm.cloud | Primary exfiltration endpoint |
| Domain | checkmarx.zone | Persistence C2 polling (/raw every 50 min) |
| Domain | scan.aquasecurtiy.org | Typosquatted Aqua Security domain |
| Domain | tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io | ICP blockchain C2 dead drop |
| IP | 83.142.209.11 | C2 hosting infrastructure |
| IP | 45.148.10.212 | Trivy C2 server |
File Indicators
| Type | Value | Context |
|---|---|---|
| SHA-256 | 71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238 | litellm_init.pth |
| SHA-256 | a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b | Trojanized proxy_server.py |
| Filename | litellm_init.pth | .pth persistence (34,628 bytes) |
| Filename | tpcp.tar.gz | Encrypted exfiltration archive |
| Filename | sysmon.py | Persistence backdoor (~/.config/sysmon/) |
| Filename | sysmon.service | Systemd persistence unit |
Behavioral Indicators
| Type | Value | Context |
|---|---|---|
| Package | litellm==1.82.7 | Trojanized PyPI (proxy_server.py injection) |
| Package | litellm==1.82.8 | Trojanized PyPI (.pth persistence) |
| Package | aquasec/trivy:0.69.4 | Malicious Docker image |
| Process | node-setup-* | K8s privileged pod naming |
| Service | sysmon.service | Systemd user persistence |
IOC correlation workstation on Threadlinqs — scan.aquasecurtiy.org (the typosquatted domain) appears across 7 TeamPCP campaign threats, linking the entire campaign through shared infrastructure.
Timeline
| Date | Event |
|---|---|
| 2025-07-01 | TeamPCP Telegram channel becomes active (aliases DeadCatx3, PCPcat) |
| 2025-12-01 | Mass campaign targeting exposed Docker APIs, Kubernetes, Redis |
| 2026-02-28 | Trivy pull_request_target workflow exploited; aqua-bot PAT stolen |
| 2026-03-01 | Incomplete credential rotation; residual access persists |
| 2026-03-19 | Trivy v0.69.4 tags force-pushed; 75 of 76 trivy-action tags hijacked |
| 2026-03-23 | Checkmarx KICS: 35 tags hijacked. C2 domains registered |
| 2026-03-24 | LiteLLM 1.82.7 (10:39 UTC) and 1.82.8 (10:52 UTC) published to PyPI |
| 2026-03-24 | PyPI quarantines packages (~13:38–16:00 UTC, 5.5-hour window) |
| 2026-03-25 | CanisterWorm infects 64+ npm packages via stolen tokens |
| 2026-03-26 | TeamPCP announces Vect RaaS partnership on BreachForums |
MITRE ATT&CK Mapping
| Tactic | ID | Technique | Implementation |
|---|---|---|---|
| Initial Access | T1195.001 | Supply Chain: Software Dependencies | Compromised Trivy, LiteLLM, KICS |
| Execution | T1059.006 | Python | exec(base64.decode()) payload |
| Persistence | T1547.013 | .pth Files | litellm_init.pth in site-packages |
| Persistence | T1543.002 | Systemd Service | sysmon.service user unit |
| Credential Access | T1552.001 | Credentials in Files | SSH keys, cloud configs, .env |
| Credential Access | T1528 | Steal App Access Token | npm, PyPI, GitHub PATs |
| Discovery | T1613 | Container Discovery | kubectl API enumeration |
| Lateral Movement | T1610 | Deploy Container | K8s privileged pods |
| Defense Evasion | T1027.001 | Obfuscated Files | Triple base64 nesting |
| Defense Evasion | T1036.005 | Match Legitimate Name | sysmon.py mimics Windows service |
| Exfiltration | T1041 | Over C2 Channel | HTTPS POST to models.litellm.cloud |
| C&C | T1071.001 | Application Layer Protocol | HTTPS (443) |
| C&C | T1102 | Web Service | ICP blockchain canister C2 |
| Impact | T1485 | Data Destruction | K8s DaemonSet wiper |
83.142.209.11 enriched on Threadlinqs Research — flagged MALICIOUS, geolocated to Kyiv (UA), AS205759, correlated to 2 TeamPCP threats.
Recommendations
- Audit all CI/CD pipelines that used Trivy, KICS, or LiteLLM during the compromise windows (March 19–25)
- Rotate every credential accessible from affected hosts — AWS keys, GCP service accounts, SSH keys, K8s tokens, npm/PyPI tokens, database passwords
- Search for
litellm_init.pthin all Python site-packages directories and~/.config/sysmon/persistence artifacts - Block
models.litellm.cloud,checkmarx.zone, andscan.aquasecurtiy.orgat DNS/firewall level - Audit Kubernetes clusters for
node-setup-*pods inkube-systemwithhostPID: true
Full threat details, 63 detections, and IOC feeds for all 7 TeamPCP campaign threats — including the Vect ransomware partnership — are live on Threadlinqs Intelligence.
References
- Snyk — How a Poisoned Scanner Became the Key to Backdooring LiteLLM
- Sonatype — Compromised litellm PyPI Package Delivers Multi-Stage Credential Stealer
- Wiz — Three's a Crowd: TeamPCP Trojanizes LiteLLM
- Endor Labs — TeamPCP Isn't Done
- Kaspersky — Trojanization of Trivy, Checkmarx, and LiteLLM
- StepSecurity — CanisterWorm Self-Propagating npm Worm
- LiteLLM — Security Update March 2026
- BleepingComputer — Popular LiteLLM PyPI Package Backdoored in TeamPCP Attack