TL-2026-0288 CRITICAL 2026-03-26 Campaign Report

TeamPCP — From LiteLLM Supply Chain Compromise to Vect Ransomware Partnership

Threadlinqs Intel Team 14 min
teampcplitellmsupply-chainvect-ransomwaretrivycve-2026-33634pypicredential-theftcanisterwormkubernetes-wiper

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 threat actor profile on Threadlinqs Intelligence showing 6 threats, 85 MITRE techniques, aliases PCPcat, Persy_PCP, ShellForce, DeadCatx3 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.

IOC enrichment of models.litellm.cloud on Threadlinqs Research showing MALICIOUS verdict with 4 threat correlations 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.

Threat intelligence enrichment of checkmarx.zone showing MALICIOUS verdict and 10 OSINT pulses across Threadlinqs Research 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 showing shared infrastructure groups across TeamPCP threats on Threadlinqs 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

TypeValueContext
Domainmodels.litellm.cloudPrimary exfiltration endpoint
Domaincheckmarx.zonePersistence C2 polling (/raw every 50 min)
Domainscan.aquasecurtiy.orgTyposquatted Aqua Security domain
Domaintdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.ioICP blockchain C2 dead drop
IP83.142.209.11C2 hosting infrastructure
IP45.148.10.212Trivy C2 server

File Indicators

TypeValueContext
SHA-25671e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238litellm_init.pth
SHA-256a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120bTrojanized proxy_server.py
Filenamelitellm_init.pth.pth persistence (34,628 bytes)
Filenametpcp.tar.gzEncrypted exfiltration archive
Filenamesysmon.pyPersistence backdoor (~/.config/sysmon/)
Filenamesysmon.serviceSystemd persistence unit

Behavioral Indicators

TypeValueContext
Packagelitellm==1.82.7Trojanized PyPI (proxy_server.py injection)
Packagelitellm==1.82.8Trojanized PyPI (.pth persistence)
Packageaquasec/trivy:0.69.4Malicious Docker image
Processnode-setup-*K8s privileged pod naming
Servicesysmon.serviceSystemd user persistence
Cross-threat IOC correlation on Threadlinqs showing 175 shared indicators across 176 actors with scan.aquasecurtiy.org linked to 7 TeamPCP threats 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

DateEvent
2025-07-01TeamPCP Telegram channel becomes active (aliases DeadCatx3, PCPcat)
2025-12-01Mass campaign targeting exposed Docker APIs, Kubernetes, Redis
2026-02-28Trivy pull_request_target workflow exploited; aqua-bot PAT stolen
2026-03-01Incomplete credential rotation; residual access persists
2026-03-19Trivy v0.69.4 tags force-pushed; 75 of 76 trivy-action tags hijacked
2026-03-23Checkmarx KICS: 35 tags hijacked. C2 domains registered
2026-03-24LiteLLM 1.82.7 (10:39 UTC) and 1.82.8 (10:52 UTC) published to PyPI
2026-03-24PyPI quarantines packages (~13:38–16:00 UTC, 5.5-hour window)
2026-03-25CanisterWorm infects 64+ npm packages via stolen tokens
2026-03-26TeamPCP announces Vect RaaS partnership on BreachForums

MITRE ATT&CK Mapping

TacticIDTechniqueImplementation
Initial AccessT1195.001Supply Chain: Software DependenciesCompromised Trivy, LiteLLM, KICS
ExecutionT1059.006Pythonexec(base64.decode()) payload
PersistenceT1547.013.pth Fileslitellm_init.pth in site-packages
PersistenceT1543.002Systemd Servicesysmon.service user unit
Credential AccessT1552.001Credentials in FilesSSH keys, cloud configs, .env
Credential AccessT1528Steal App Access Tokennpm, PyPI, GitHub PATs
DiscoveryT1613Container Discoverykubectl API enumeration
Lateral MovementT1610Deploy ContainerK8s privileged pods
Defense EvasionT1027.001Obfuscated FilesTriple base64 nesting
Defense EvasionT1036.005Match Legitimate Namesysmon.py mimics Windows service
ExfiltrationT1041Over C2 ChannelHTTPS POST to models.litellm.cloud
C&CT1071.001Application Layer ProtocolHTTPS (443)
C&CT1102Web ServiceICP blockchain canister C2
ImpactT1485Data DestructionK8s DaemonSet wiper
IOC enrichment of 83.142.209.11 showing MALICIOUS verdict geolocated to Kyiv Ukraine with 2 correlated threats 83.142.209.11 enriched on Threadlinqs Research — flagged MALICIOUS, geolocated to Kyiv (UA), AS205759, correlated to 2 TeamPCP threats.

Recommendations


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

HB

Threadlinqs Intel Team

Threat Intelligence Analyst

Hatim covers supply chain security, CI/CD pipeline threats, and adversary infrastructure at Threadlinqs Intelligence. He tracks threat actor campaigns across package ecosystems and cloud-native environments, translating observed tradecraft into production-ready detection engineering.