TeamPCP Backdoors litellm on PyPI: Inside the Multi-Ecosystem Supply Chain Campaign
On March 24, 2026, the threat group TeamPCP published two backdoored versions of litellm to PyPI, embedding a multi-stage credential stealer into a package downloaded roughly 3.4 million times per day [2]. The compromised versions, 1.82.7 and 1.82.8, were live for approximately three hours before being removed [3][4].
litellm is a unified interface for applications interacting with LLMs from providers including OpenAI, Anthropic, and Google [3]. That architectural position is precisely what makes it so dangerous as an attack vector. Because litellm typically sits directly between applications and multiple AI service providers, it often has access to API keys, environment variables, and other sensitive configuration data [6]. With over 95 million downloads in the past month [7], the blast radius of even a brief compromise is enormous.
The Campaign: From Trivy to litellm
This was not an isolated incident. TeamPCP executed a cascading supply chain campaign across multiple ecosystems, moving from project to project, siphoning credentials, and using them to expand the operation [5].
The timeline is clear. On March 19, TeamPCP compromised Trivy, Aqua Security's open-source vulnerability scanner [5]. On March 23, they hit Checkmarx's KICS project [5]. On March 24, they used credentials stolen from the Trivy breach to access litellm's PyPI publishing pipeline [1]. The attack bypassed official CI/CD workflows entirely. A maintainer's PyPI account was compromised and used to distribute malicious code directly [1].
The broader campaign spans five ecosystems: GitHub Actions, Docker Hub, npm, OpenVSX, and PyPI [15]. TeamPCP also deployed a self-propagating component called CanisterWorm across 141 malicious npm packages [11].
Technical Breakdown: The .pth Execution Trick
The core of the attack centers on a malicious .pth file. Version 1.82.8 contained a file called litellm_init.pth (34,628 bytes) embedded in the wheel package [10]. Python's .pth file mechanism is designed for path configuration, but it also supports code execution. This file executes automatically whenever Python is run, even when litellm itself isn't imported [7]. That means any Python process on the compromised system triggers the payload.
The payload is double base64-encoded [10] and implements a three-stage credential stealer.
Stage 1: Credential Harvesting
The stealer targets a wide range of sensitive data [4]:
- SSH keys:
~/.ssh/id_rsa,~/.ssh/id_ed25519, and related files - Cloud credentials: AWS (
~/.aws/credentials), Google Cloud service account keys, Azure tokens - Kubernetes configs: kubeconfig files and mounted service account tokens
- API keys:
.envfiles and environment variables - Database passwords: Configuration files containing connection strings
The stolen data is encrypted using a hybrid RSA-4096 and AES-256 scheme before exfiltration [9].
Stage 2: Exfiltration
Credentials are sent to models.litellm.cloud, a domain registered just one day before the attack [9]. This is not an official BerriAI or litellm domain [10]. The GitHub issue that first raised the alarm stated it plainly: "Anyone who installed litellm==1.82.8 via pip has had all environment variables, SSH keys, cloud credentials, and other secrets collected and sent to an attacker-controlled server" [10].
Callum McMahon at FutureSearch first raised the alarm after loading the malicious payload caused havoc on his machine [6].
Stage 3: Persistence and Backdoor
The payload installs a persistent backdoor through two mechanisms [4][7]:
- A Python script at
~/.config/sysmon/sysmon.py - A systemd user service at
~/.config/systemd/user/sysmon.service
The naming mimics legitimate system monitoring tools. The backdoor downloads a binary to /tmp/pglog and uses /tmp/.pg_state as a state file for C2 polling [7]. A separate persistent backdoor polls checkmarx.zone every 50 minutes for second-stage payloads [9]. Each polling cycle fetches a URL from the C2 and compares it against the local state file.
The campaign includes destructive wiper functionality (via kamikaze.sh) targeting Kubernetes clusters [11].
C2 Infrastructure
TeamPCP uses a distributed C2 architecture that complicates takedown efforts:
models.litellm.cloudfor primary data exfiltration [1][10]checkmarx.zonefor second-stage payload delivery and persistence polling [9][12]scan.aquasecurtiy.org(typosquat of aquasecurity.org) from the Trivy wave [12]plug-tab-protective-relay.trycloudflare.comvia Cloudflare Tunnel [16]- An ICP blockchain canister at
tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io[11]
The blockchain-based C2 is a decentralized smart contract with no single takedown point [12], making it particularly resilient to law enforcement action.
IOC Table
| Type | Value | Context | Source |
|---|---|---|---|
| domain | models.litellm.cloud |
Primary exfiltration endpoint | [1][10] |
| domain | checkmarx.zone |
Second-stage payload C2 (50-min polling) | [9][12] |
| domain | scan.aquasecurtiy.org |
Trivy wave C2 (typosquat) | [12] |
| domain | plug-tab-protective-relay.trycloudflare.com |
Cloudflare Tunnel C2 | [16] |
| domain | tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io |
ICP blockchain canister C2 | [11] |
| IP | 45.148.10.212 |
Associated C2 infrastructure | [11] |
| URL | checkmarx.zone/static/checkmarx-util-1.0.4.tgz |
Second-stage payload | [12] |
| URL | checkmarx.zone/vsx |
Exfiltration endpoint | [12] |
| URL | checkmarx.zone/raw |
Persistence polling endpoint | [12] |
| filename | litellm_init.pth |
Malicious .pth file (34,628 bytes) | [1][10] |
| filename | ~/.config/sysmon/sysmon.py |
Persistent backdoor script | [4][7] |
| filename | ~/.config/systemd/user/sysmon.service |
Systemd persistence service | [4][7] |
| filename | /tmp/pglog |
Downloaded backdoor binary | [7] |
| filename | /tmp/.pg_state |
C2 polling state file | [7] |
| filename | tpcp.tar.gz |
Stolen credential archive | [2] |
| filename | kamikaze.sh |
Kubernetes cluster wiper script | [11] |
| filename | /tmp/runner_collected_ |
Credential collection file prefix | [16] |
| malware | TeamPCP Cloud Stealer |
Multi-stage CI/CD credential harvester | [2] |
MITRE ATT&CK Mapping
| Technique ID | Name | Relevance |
|---|---|---|
| T1195.001 | Compromise Software Dependencies and Development Tools | Backdoored PyPI package distributed via compromised maintainer credentials [1] |
| T1546.018 | Python Startup Hooks | Malicious .pth file executes on every Python process startup [4][7] |
| T1552 | Unsecured Credentials | Harvesting of SSH keys, cloud creds, kubeconfigs, .env files [4] |
| T1555 | Credentials from Password Stores | Targeting stored cloud provider credentials and API keys [4] |
| T1053.006 | Systemd Timers | Persistence via systemd user service [4][7] |
| T1567.002 | Exfiltration to Cloud Storage | Data sent to attacker-controlled cloud domain [9][10] |
| T1059.001 | PowerShell | Secondary execution mechanisms in campaign [12] |
| T1608.001 | Upload Malware | Staging second-stage payloads on C2 infrastructure [12] |
Detection and Hunting
File-based indicators: Search all systems for the presence of litellm_init.pth in any Python site-packages directory. Check for ~/.config/sysmon/sysmon.py, ~/.config/systemd/user/sysmon.service, /tmp/pglog, and /tmp/.pg_state [4][7].
Network-based hunting: Microsoft published detection queries for Defender XDR [16]:
CloudProcessEvents
| where ProcessCommandLine has_any ('scan.aquasecurtiy.org','45.148.10.212','plug-tab-protective-relay.trycloudflare.com')
Also hunt for DNS queries to models.litellm.cloud, checkmarx.zone, and the ICP canister domain. Any outbound connection to 45.148.10.212 should be treated as confirmed compromise [11][16].
Process-based detection: Look for Python processes making unexpected network connections, particularly to the domains listed above. The .pth mechanism means the malicious code runs in every Python process, not just litellm-related ones [7]. Monitor for pglog and pgmon process names, which masquerade as PostgreSQL components [11].
CI/CD pipeline review: Check pip install logs for litellm versions 1.82.7 or 1.82.8. Sonatype's automated tooling detected and blocked these versions within seconds of publication [3], so organizations using Sonatype's repository firewall may have been protected. Verify your dependency management tooling's response.
Credential file access: Monitor for unusual read access to ~/.ssh/, ~/.aws/credentials, kubeconfig files, and .env files, particularly from Python processes.
Analysis
TeamPCP has demonstrated a level of operational sophistication that puts them in a different category from typical supply chain attackers. The cascading credential theft model, where each compromise funds the next intrusion, creates a self-sustaining attack chain that's difficult to interrupt once it's in motion [5].
TeamPCP's selection of litellm demonstrated strategic planning. AI gateway libraries occupy a privileged position in modern infrastructure stacks, sitting between applications and the LLM providers they depend on [6]. Compromising this layer grants access to every API key, every environment variable, and every credential that passes through it. The three-hour exposure window, combined with 3.4 million daily downloads, means the realistic exposure count is in the hundreds of thousands of installations.
The use of ICP blockchain infrastructure for C2 [11] is a practical problem for defenders. Traditional domain takedown procedures don't apply to decentralized smart contracts. This is likely to become a recurring pattern in sophisticated campaigns.
Red Sheep Assessment
Confidence: High
The TeamPCP campaign represents a maturation of supply chain attack methodology. Several elements distinguish it from prior art.
First, the attack chain is genuinely self-amplifying. By compromising a security scanner (Trivy) first, TeamPCP obtained credentials used in security-focused CI/CD pipelines, exactly the pipelines most likely to have elevated privileges across an organization's software supply chain. The jump from Trivy to Checkmarx to litellm was not opportunistic. It followed the credential graph.
Second, the use of blockchain-based C2 infrastructure signals a shift in persistence techniques that will complicate future incident response efforts.
Third, the credential rotation burden alone will be staggering for affected organizations.
Contrarian view: Some of the more dramatic claims from TeamPCP ("hundreds of thousands of devices" [7]) may be exaggerated for psychological impact. The group has incentive to inflate impact numbers. The actual blast radius, while severe, may be smaller than the headline figures suggest. Three hours of exposure on a package with high daily downloads is significant, but automated CI/CD systems that pin exact versions and use lockfiles may have been insulated.
Defender's Checklist
- ▢[ ] Hunt for
litellm_init.pthacross all Python environments:find / -name "litellm_init.pth" 2>/dev/nulland check pip logs for installation of litellm 1.82.7 or 1.82.8 - ▢[ ] Query DNS logs for C2 domains:
index=dns query IN ("models.litellm.cloud", "checkmarx.zone", "scan.aquasecurtiy.org", "plug-tab-protective-relay.trycloudflare.com") - ▢[ ] Check for persistence artifacts:
ls -la ~/.config/sysmon/sysmon.py ~/.config/systemd/user/sysmon.service /tmp/pglog /tmp/.pg_state 2>/dev/null - ▢[ ] Rotate all credentials on any system where litellm 1.82.7 or 1.82.8 was installed: SSH keys, AWS/GCP/Azure credentials, Kubernetes service account tokens, and all API keys stored in environment variables or
.envfiles - ▢[ ] Block
45.148.10.212and all identified C2 domains at the network perimeter, and deploy the Microsoft Defender XDR query from [16] for ongoing monitoring
References
- Security Update: Suspected Supply Chain Incident | liteLLM
- How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM | Snyk
- Compromised litellm PyPI Package Delivers Multi-Stage Credential Stealer | Sonatype
- Supply Chain Attack in litellm 1.82.8 on PyPI | FutureSearch
- LiteLLM compromised on PyPI: Tracing the March 2026 TeamPCP supply chain campaign | Datadog Security Labs
- LiteLLM PyPI packages compromised in expanding TeamPCP supply chain attacks | Help Net Security
- Popular LiteLLM PyPI package backdoored to steal credentials, auth tokens | BleepingComputer
- When the Security Scanner Became the Weapon: Inside the TeamPCP Supply Chain Campaign | SANS Institute
- Your AI Gateway Was a Backdoor: Inside the LiteLLM Supply Chain Compromise | Trend Micro
- CRITICAL: Malicious litellm_init.pth in litellm 1.82.8: credential stealer | GitHub Issue #24512
- TeamPCP Injects Credential Stealer Into Trivy Releases and Spreads to npm via CanisterWorm | CyberSec Sentinel
- CVE-2026-33634 IOC Collection | GitHub
- TeamPCP Attack day 6 Backdoors LiteLLM | Phoenix Security
- CISA sounds alarm on Langflow RCE, Trivy supply chain compromise after rapid exploitation | Help Net Security
- TeamPCP Expands Supply Chain Campaign With LiteLLM PyPI Compromise | Infosecurity Magazine
- Guidance for detecting, investigating, and defending against the Trivy supply chain compromise | Microsoft
Visual Intelligence
Timeline (4 events)
Entity Graph (16 entities, 18 relationships)
Diamond Model
---
Hunt Guide: Hunt Report: TeamPCP Supply Chain Campaign - litellm PyPI Backdoor
Hypothesis: If TeamPCP threat actors are active in our environment through compromised litellm packages, we expect to observe malicious .pth files, C2 communications to specific domains, credential harvesting from sensitive directories, and persistence mechanisms disguised as system monitoring services in Sysmon, DNS logs, and file system artifacts.
Intelligence Summary: TeamPCP executed a cascading supply chain attack compromising litellm versions 1.82.7 and 1.82.8 on PyPI, embedding a multi-stage credential stealer that harvested SSH keys, cloud credentials, and API tokens via malicious .pth files. The campaign leveraged credentials stolen from prior compromises of Trivy and Checkmarx to access litellm's publishing pipeline, demonstrating a self-amplifying attack methodology across multiple ecosystems.
Confidence: High | Priority: Critical
Scope
- Networks: All systems with Python installed, particularly CI/CD infrastructure, developer workstations, and production systems using AI/LLM libraries
- Timeframe: March 24, 2026 00:00 UTC - Present (focus on 3-hour window when malicious packages were live)
- Priority Systems: CI/CD pipeline servers, Python development environments, systems with access to cloud credentials, Kubernetes control plane nodes
MITRE ATT&CK Techniques
T1195.001 — Compromise Software Dependencies and Development Tools (Initial Access) [P1]
TeamPCP backdoored litellm PyPI packages using compromised maintainer credentials, distributing malicious versions 1.82.7 and 1.82.8
Splunk SPL:
index=* sourcetype=pip_log OR sourcetype=python_package ("litellm==1.82.7" OR "litellm==1.82.8" OR "litellm 1.82.7" OR "litellm 1.82.8") | stats count by host, _time, package_version | where count > 0
Elastic KQL:
(message:"litellm==1.82.7" OR message:"litellm==1.82.8" OR package.name:"litellm" AND (package.version:"1.82.7" OR package.version:"1.82.8"))
Sigma Rule:
title: TeamPCP litellm Malicious Package Installation
id: a7b3c5d2-8e4f-4a6b-9c1d-2e3f4a5b6c7d
status: experimental
description: Detects installation of compromised litellm versions 1.82.7 or 1.82.8
author: NADCOE Hunt Team
date: 2026/03/25
references:
- https://docs.litellm.ai/blog/security-update-march-2026
logsource:
category: package_manager
product: python
detection:
selection:
- package_name: 'litellm'
package_version:
- '1.82.7'
- '1.82.8'
- CommandLine|contains:
- 'pip install litellm==1.82.7'
- 'pip install litellm==1.82.8'
condition: selection
falsepositives:
- None expected
level: critical
tags:
- attack.initial_access
- attack.t1195.001
Focus on installations between March 24, 2026 00:00-03:00 UTC. Check pip cache directories for presence of malicious wheel files.
T1546.018 — Event Triggered Execution: .pth Files (Persistence) [P1]
Malicious litellm_init.pth file executes automatically on every Python process startup, even without importing litellm
Splunk SPL:
index=* sourcetype=sysmon EventCode=11 TargetFilename="*litellm_init.pth" | eval file_size=if(file_size=34628, "MALICIOUS", "CHECK") | table _time, ComputerName, TargetFilename, file_size, User
Elastic KQL:
event.code:11 AND file.name:"litellm_init.pth" AND file.size:34628
Sigma Rule:
title: TeamPCP Malicious .pth File Creation
id: b8c4d6e3-9f5a-4b7c-8d2e-3f5g7h9i0j1k
status: experimental
description: Detects creation of malicious litellm_init.pth file (34,628 bytes)
author: NADCOE Hunt Team
date: 2026/03/25
logsource:
product: windows
service: sysmon
detection:
selection_file:
EventID: 11
TargetFilename|endswith: 'litellm_init.pth'
selection_size:
FileSize: 34628
condition: all of selection_*
falsepositives:
- Unlikely
level: critical
The malicious .pth file is exactly 34,628 bytes. Any Python process startup will execute this code, making it highly persistent.
T1552.001 — Unsecured Credentials: Credentials In Files (Credential Access) [P1]
TeamPCP stealer harvests SSH keys, AWS credentials, kubeconfig files, and .env files from standard locations
Splunk SPL:
index=* sourcetype=sysmon EventCode=10 (TargetImage="*python*" OR TargetImage="*python3*") (CallTrace="*/.ssh/*" OR CallTrace="*/.aws/credentials" OR CallTrace="*kubeconfig*" OR CallTrace="*.env") | stats count by SourceImage, TargetImage, CallTrace, ComputerName | where count > 5
Elastic KQL:
event.code:10 AND (process.name:"python" OR process.name:"python3") AND (process.thread.Ext.call_stack_summary:"*/.ssh/*" OR process.thread.Ext.call_stack_summary:"*/.aws/credentials" OR process.thread.Ext.call_stack_summary:"*kubeconfig*")
Sigma Rule:
title: Python Process Accessing Sensitive Credential Files
id: c9d5e7f4-1a6b-4c8d-9e2f-4a6b8c9d0e1f
status: experimental
description: Detects Python processes accessing sensitive credential locations
author: NADCOE Hunt Team
date: 2026/03/25
logsource:
product: linux
service: auditd
detection:
selection_process:
type: 'SYSCALL'
exe|endswith:
- '/python'
- '/python3'
selection_files:
name|contains:
- '/.ssh/id_rsa'
- '/.ssh/id_ed25519'
- '/.aws/credentials'
- 'kubeconfig'
- '.env'
condition: all of selection_*
falsepositives:
- Legitimate automation scripts
- Development tools
level: high
Monitor for bulk file reads from Python processes. TeamPCP reads multiple credential files in rapid succession.
T1567.002 — Exfiltration Over Web Service: Exfiltration to Cloud Storage (Exfiltration) [P1]
Stolen credentials are exfiltrated to models.litellm.cloud and checkmarx.zone using hybrid RSA-4096/AES-256 encryption
Splunk SPL:
index=* sourcetype=dns (query="models.litellm.cloud" OR query="checkmarx.zone" OR query="scan.aquasecurtiy.org" OR query="plug-tab-protective-relay.trycloudflare.com" OR query="tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io") | eval threat="TeamPCP_C2" | stats count by query, src_ip, _time | where count > 0
Elastic KQL:
dns.question.name:("models.litellm.cloud" OR "checkmarx.zone" OR "scan.aquasecurtiy.org" OR "plug-tab-protective-relay.trycloudflare.com" OR "tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io")
Sigma Rule:
title: TeamPCP C2 Domain Communication
id: d0e6f8g5-2b7c-4d9e-0f3g-5b7c9e0f2g3h
status: experimental
description: Detects DNS queries to TeamPCP C2 infrastructure
author: NADCOE Hunt Team
date: 2026/03/25
logsource:
category: dns
detection:
selection:
query:
- 'models.litellm.cloud'
- 'checkmarx.zone'
- 'scan.aquasecurtiy.org'
- 'plug-tab-protective-relay.trycloudflare.com'
- 'tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io'
condition: selection
falsepositives:
- None expected
level: critical
tags:
- attack.exfiltration
- attack.t1567.002
models.litellm.cloud was registered one day before the attack. This is NOT an official litellm domain.
T1053.006 — Scheduled Task/Job: Systemd Timers (Persistence) [P2]
TeamPCP installs persistent backdoor via systemd user service at ~/.config/systemd/user/sysmon.service
Splunk SPL:
index=* (sourcetype=sysmon EventCode=11 TargetFilename="*/.config/systemd/user/sysmon.service") OR (sourcetype=linux_audit comm="systemctl" AND (a0="*sysmon.service*" OR a1="*sysmon.service*")) | table _time, host, user, TargetFilename, comm
Elastic KQL:
(file.path:"*/.config/systemd/user/sysmon.service" OR file.path:"*/.config/sysmon/sysmon.py") OR (process.name:"systemctl" AND process.args:"*sysmon.service*")
Sigma Rule:
title: TeamPCP Systemd Persistence Installation
id: e1f7g9h6-3c8d-4e0f-1g4h-6c8d0f1g4h5i
status: experimental
description: Detects creation of malicious systemd service masquerading as sysmon
author: NADCOE Hunt Team
date: 2026/03/25
logsource:
product: linux
service: sysmon
detection:
selection_file:
EventID: 11
TargetFilename|contains:
- '/.config/systemd/user/sysmon.service'
- '/.config/sysmon/sysmon.py'
selection_process:
EventID: 1
Image|endswith: '/systemctl'
CommandLine|contains: 'sysmon.service'
condition: 1 of selection_*
falsepositives:
- Legitimate sysmon installations (verify path)
level: high
The service name 'sysmon' is chosen to blend in with legitimate Windows Sysmon references in mixed environments.
T1059.001 — Command and Scripting Interpreter: PowerShell (Execution) [P2]
Secondary execution mechanisms in the broader TeamPCP campaign
Splunk SPL:
index=* sourcetype=WinEventLog:Microsoft-Windows-PowerShell/Operational EventCode=4104 (ScriptBlockText="*checkmarx.zone*" OR ScriptBlockText="*models.litellm.cloud*" OR ScriptBlockText="*45.148.10.212*") | table _time, ComputerName, UserID, ScriptBlockText
Elastic KQL:
event.code:4104 AND powershell.file.script_block_text:("*checkmarx.zone*" OR "*models.litellm.cloud*" OR "*45.148.10.212*")
Sigma Rule:
title: PowerShell Accessing TeamPCP Infrastructure
id: f2g8h0i7-4d9e-5f1g-2h5i-7d9f1h5i7j6k
status: experimental
description: Detects PowerShell scripts contacting TeamPCP C2 domains
author: NADCOE Hunt Team
date: 2026/03/25
logsource:
product: windows
service: powershell
detection:
selection:
EventID: 4104
ScriptBlockText|contains:
- 'checkmarx.zone'
- 'models.litellm.cloud'
- '45.148.10.212'
condition: selection
falsepositives:
- Unlikely
level: high
Part of the broader campaign toolkit, may indicate lateral movement or secondary payload execution.
Indicators of Compromise
| Type | Value | Context |
|---|---|---|
| domain | models.litellm.cloud |
Primary exfiltration endpoint for stolen credentials, registered one day before attack |
| domain | checkmarx.zone |
Second-stage payload C2 with 50-minute polling interval for persistence |
| domain | scan.aquasecurtiy.org |
Typosquat domain from Trivy compromise wave |
| domain | plug-tab-protective-relay.trycloudflare.com |
Cloudflare Tunnel C2 infrastructure |
| domain | tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io |
ICP blockchain canister C2, resilient to takedown |
| ip | 45.148.10.212 |
TeamPCP C2 infrastructure IP, any connection indicates compromise |
| url | checkmarx.zone/static/checkmarx-util-1.0.4.tgz |
Second-stage payload download URL |
| url | checkmarx.zone/vsx |
Exfiltration endpoint on checkmarx.zone |
| url | checkmarx.zone/raw |
Persistence polling endpoint checked every 50 minutes |
| filename | litellm_init.pth |
Malicious .pth file (34,628 bytes) that executes on Python startup |
| filename | ~/.config/sysmon/sysmon.py |
Persistent backdoor Python script masquerading as sysmon |
| filename | ~/.config/systemd/user/sysmon.service |
Systemd persistence service for backdoor |
| filename | /tmp/pglog |
Downloaded backdoor binary masquerading as PostgreSQL log |
| filename | /tmp/.pg_state |
C2 polling state file for backdoor |
| filename | tpcp.tar.gz |
Archive containing stolen credentials before exfiltration |
| filename | kamikaze.sh |
Kubernetes cluster wiper script |
| filename | /tmp/runner_collected_ |
Credential collection file prefix used by stealer |
IOC Sweep Queries (Splunk):
index=* (dest="models.litellm.cloud" OR query="models.litellm.cloud" OR url="*models.litellm.cloud*") | stats count by index, sourcetype, src_ip | where count > 0
index=* (dest="checkmarx.zone" OR query="checkmarx.zone" OR url="*checkmarx.zone*") | stats count by index, sourcetype, src_ip | where count > 0
index=* (dest="scan.aquasecurtiy.org" OR query="scan.aquasecurtiy.org" OR url="*scan.aquasecurtiy.org*") | stats count by index, sourcetype, src_ip | where count > 0
index=* (dest="plug-tab-protective-relay.trycloudflare.com" OR query="plug-tab-protective-relay.trycloudflare.com" OR url="*plug-tab-protective-relay.trycloudflare.com*") | stats count by index, sourcetype, src_ip | where count > 0
index=* (dest="tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io" OR query="tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io" OR url="*tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io*") | stats count by index, sourcetype, src_ip | where count > 0
index=* (dest_ip="45.148.10.212" OR src_ip="45.148.10.212" OR dest="45.148.10.212") | stats count by index, sourcetype, src_ip, dest_port | where count > 0
index=* (url="*checkmarx.zone/static/checkmarx-util-1.0.4.tgz*" OR uri_path="/static/checkmarx-util-1.0.4.tgz") | stats count by src_ip, dest_ip, user_agent | where count > 0
index=* (url="*checkmarx.zone/vsx*" OR uri_path="/vsx" dest="checkmarx.zone") | stats count by src_ip, dest_ip, bytes_out | where count > 0
index=* (url="*checkmarx.zone/raw*" OR uri_path="/raw" dest="checkmarx.zone") | stats count by src_ip, _time | timechart span=1h count
index=* (filename="litellm_init.pth" OR file_name="litellm_init.pth" OR TargetFilename="*litellm_init.pth") | eval malicious=if(file_size=34628,"YES","CHECK") | table _time, host, filename, file_size, malicious
index=* (filename="sysmon.py" OR TargetFilename="*/.config/sysmon/sysmon.py" OR file_path="*/.config/sysmon/sysmon.py") | stats count by host, user | where count > 0
index=* (filename="sysmon.service" OR TargetFilename="*/.config/systemd/user/sysmon.service" OR file_path="*/.config/systemd/user/sysmon.service") | stats count by host, user | where count > 0
index=* (filename="pglog" OR TargetFilename="/tmp/pglog" OR Image="/tmp/pglog" OR process_name="pglog") | stats count by host, hash_sha256 | where count > 0
index=* (filename=".pg_state" OR TargetFilename="/tmp/.pg_state" OR file_path="/tmp/.pg_state") | stats count by host, file_modify_time | where count > 0
index=* (filename="tpcp.tar.gz" OR file_name="tpcp.tar.gz" OR TargetFilename="*tpcp.tar.gz") | stats count by host, file_create_time, file_size | where count > 0
index=* (filename="kamikaze.sh" OR CommandLine="*kamikaze.sh*" OR ScriptName="kamikaze.sh") | stats count by host, user | where count > 0
index=* (filename="runner_collected_*" OR TargetFilename="/tmp/runner_collected_*") | stats count by host, filename | where count > 0
YARA Rules
TeamPCP_litellm_pth_backdoor — Detects the malicious litellm_init.pth file by size and content patterns
rule TeamPCP_litellm_pth_backdoor {
meta:
author = "NADCOE Hunt Team"
description = "Detects TeamPCP malicious .pth file in litellm package"
date = "2026-03-25"
severity = "critical"
reference = "https://github.com/BerriAI/litellm/issues/24512"
strings:
$pth_name = "litellm_init.pth" ascii
$b64_pattern = /[A-Za-z0-9+\/]{50,}=*/
$exec_marker = "exec(" ascii
$import_base64 = "import base64" ascii
$models_domain = "models.litellm.cloud" ascii
$checkmarx_domain = "checkmarx.zone" ascii
condition:
filesize == 34628 and $pth_name and
(#b64_pattern > 2) and
any of ($exec_marker, $import_base64, $models_domain, $checkmarx_domain)
}
TeamPCP_credential_stealer_artifacts — Detects TeamPCP credential stealer components and artifacts
rule TeamPCP_credential_stealer_artifacts {
meta:
author = "NADCOE Hunt Team"
description = "Detects TeamPCP credential stealer files and patterns"
date = "2026-03-25"
severity = "high"
strings:
$sysmon_py = "~/.config/sysmon/sysmon.py" ascii
$sysmon_service = "sysmon.service" ascii
$pglog = "/tmp/pglog" ascii
$pg_state = "/tmp/.pg_state" ascii
$tpcp_archive = "tpcp.tar.gz" ascii
$runner_prefix = "/tmp/runner_collected_" ascii
$ssh_harvest = "/.ssh/id_rsa" ascii
$aws_harvest = "/.aws/credentials" ascii
$rsa_4096 = "RSA-4096" ascii
$aes_256 = "AES-256" ascii
condition:
2 of ($sysmon_py, $sysmon_service, $pglog, $pg_state, $tpcp_archive, $runner_prefix) or
(1 of ($ssh_harvest, $aws_harvest) and 1 of ($rsa_4096, $aes_256))
}
TeamPCP_kamikaze_wiper — Detects TeamPCP Kubernetes cluster wiper script
rule TeamPCP_kamikaze_wiper {
meta:
author = "NADCOE Hunt Team"
description = "Detects kamikaze.sh Kubernetes wiper"
date = "2026-03-25"
severity = "critical"
strings:
$filename = "kamikaze.sh" ascii
$kubectl_delete = "kubectl delete" ascii
$rm_rf = "rm -rf" ascii
$kubernetes_api = "/api/v1/namespaces" ascii
$destroy_pattern = /destroy|wipe|nuke|obliterate/i
condition:
$filename and 2 of ($kubectl_delete, $rm_rf, $kubernetes_api, $destroy_pattern)
}
Suricata Rules
SID 3026001 — TeamPCP C2 communication to models.litellm.cloud
alert dns any any -> any any (msg:"TeamPCP C2 Domain Query - models.litellm.cloud"; dns.query; content:"models.litellm.cloud"; nocase; reference:url,docs.litellm.ai/blog/security-update-march-2026; classtype:trojan-activity; sid:3026001; rev:1;)
SID 3026002 — TeamPCP C2 communication to checkmarx.zone
alert dns any any -> any any (msg:"TeamPCP C2 Domain Query - checkmarx.zone"; dns.query; content:"checkmarx.zone"; nocase; reference:url,github.com/ugurrates/teampcp-supply-chain-attack; classtype:trojan-activity; sid:3026002; rev:1;)
SID 3026003 — TeamPCP typosquat domain scan.aquasecurtiy.org
alert dns any any -> any any (msg:"TeamPCP Typosquat Domain - scan.aquasecurtiy.org"; dns.query; content:"scan.aquasecurtiy.org"; nocase; reference:url,www.sans.org/blog/when-security-scanner-became-weapon-inside-teampcp-supply-chain-campaign; classtype:trojan-activity; sid:3026003; rev:1;)
SID 3026004 — TeamPCP C2 IP 45.148.10.212
alert ip any any -> 45.148.10.212 any (msg:"TeamPCP C2 IP Communication - 45.148.10.212"; flow:to_server,established; reference:url,cybersecsentinel.com/teampcp-injects-credential-stealer-into-trivy-releases-and-spreads-to-npm-via-canisterworm; classtype:trojan-activity; sid:3026004; rev:1;)
SID 3026005 — TeamPCP second-stage payload download
alert http any any -> any any (msg:"TeamPCP Second-Stage Payload Download"; flow:to_server,established; content:"GET"; http_method; content:"/static/checkmarx-util-1.0.4.tgz"; http_uri; content:"checkmarx.zone"; http_host; reference:url,github.com/ugurrates/teampcp-supply-chain-attack; classtype:trojan-activity; sid:3026005; rev:1;)
SID 3026006 — TeamPCP blockchain C2 communication
alert dns any any -> any any (msg:"TeamPCP Blockchain C2 Domain - ICP Canister"; dns.query; content:"tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io"; nocase; reference:url,cybersecsentinel.com/teampcp-injects-credential-stealer-into-trivy-releases-and-spreads-to-npm-via-canisterworm; classtype:trojan-activity; sid:3026006; rev:1;)
SID 3026007 — TeamPCP data exfiltration to checkmarx.zone/vsx
alert http any any -> any any (msg:"TeamPCP Data Exfiltration Endpoint"; flow:to_server,established; content:"POST"; http_method; content:"/vsx"; http_uri; content:"checkmarx.zone"; http_host; flow:to_server,established; classtype:trojan-activity; sid:3026007; rev:1;)
Data Source Requirements
| Source | Required For | Notes |
|---|---|---|
| Sysmon | T1546.018, T1552.001, T1053.006 | EventID 1 (Process Create), EventID 10 (Process Access), EventID 11 (File Create) required |
| DNS Logs | T1567.002 | Required for C2 domain detection. Enable DNS query logging on all resolvers. |
| Python/pip logs | T1195.001 | pip install logs, Python audit logs, or package manager telemetry |
| Linux Audit (auditd) | T1552.001, T1053.006 | File access auditing for credential locations, systemd service creation |
| PowerShell Logs | T1059.001 | ScriptBlock Logging (Event ID 4104) and Module Logging |
| Network Flow/Proxy | T1567.002 | HTTP/HTTPS traffic to C2 domains, data volume monitoring |
Sources
- Security Update: Suspected Supply Chain Incident | liteLLM
- How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM | Snyk
- Compromised litellm PyPI Package Delivers Multi-Stage Credential Stealer | Sonatype
- Supply Chain Attack in litellm 1.82.8 on PyPI | FutureSearch
- LiteLLM compromised on PyPI: Tracing the March 2026 TeamPCP supply chain campaign | Datadog Security Labs
- LiteLLM PyPI packages compromised in expanding TeamPCP supply chain attacks | Help Net Security
- Popular LiteLLM PyPI package backdoored to steal credentials, auth tokens | BleepingComputer
- When the Security Scanner Became the Weapon: Inside the TeamPCP Supply Chain Campaign | SANS Institute
- Your AI Gateway Was a Backdoor: Inside the LiteLLM Supply Chain Compromise | Trend Micro
- CRITICAL: Malicious litellm_init.pth in litellm 1.82.8: credential stealer | GitHub Issue #24512
- TeamPCP Injects Credential Stealer Into Trivy Releases and Spreads to npm via CanisterWorm | CyberSec Sentinel
- CVE-2026-33634 IOC Collection | GitHub
- TeamPCP Attack day 6 Backdoors LiteLLM | Phoenix Security
- CISA sounds alarm on Langflow RCE, Trivy supply chain compromise after rapid exploitation | Help Net Security
- TeamPCP Expands Supply Chain Campaign With LiteLLM PyPI Compromise | Infosecurity Magazine
- Guidance for detecting, investigating, and defending against the Trivy supply chain compromise | Microsoft