The compromise of LiteLLM — the widely adopted open-source LLM proxy and gateway — is shaping up to be one of the most consequential supply-chain incidents of 2026. According to reporting by Security Affairs and sector-impact analysis from Resecurity, the attack (attributed to the TeamPCP actor and tracked via an unauthorized access mechanism dubbed SANDCLOCK) resulted in credential exposure across 2,038 code repositories, with technology, banking/finance, healthcare, and retail organizations bearing the brunt.
This is not a theoretical risk. When a dependency as deeply embedded as LiteLLM is poisoned, every organization that pip-installed it into an LLM gateway, an internal AI service, or a CI/CD pipeline inherited the adversary's foothold. LiteLLM sits in a uniquely privileged position: it brokers API keys for OpenAI, Anthropic, Azure OpenAI, Bedrock, and dozens of other providers, and it frequently runs adjacent to .env files, cloud metadata endpoints, and enterprise identity material. A compromise here is not a compromised package — it is a compromised credential vault.
Defenders need to act on three fronts immediately: (1) determine whether any build or runtime environment pulled a tainted LiteLLM artifact, (2) assume credential compromise and rotate everything LiteLLM ever touched, and (3) hunt for the post-compromise behaviors SANDCLOCK enables — environment harvesting and outbound exfiltration.
Technical Analysis
What was compromised
Based on the reporting, the attack chain follows the classic TeamPCP playbook — a group with a documented history of targeting open-source ecosystems and developer infrastructure:
- Repository compromise: The attacker gained unauthorized access to the LiteLLM code repository / release pipeline. This is the SANDCLOCK access mechanism — persistent unauthorized access to the source of truth for the package.
- Poisoned distribution: Malicious code was introduced into artifacts that downstream consumers pulled automatically (via
pip install litellm, CI builds, Docker base images, or auto-updating dependency bots). Because LiteLLM is a transitive dependency in many AI/ML stacks, most victims never installed it by name. - Credential harvesting at install/runtime: The planted code targeted the exact assets LiteLLM deployments custody — LLM provider API keys, cloud credentials (AWS keys, Azure service principals, GCP service account JSON), database connection strings, and environment variables.
- Exfiltration and downstream use: Exposed credentials surfaced across 2,038 repositories — meaning harvested secrets were leveraged to access private repos, and those repos in turn leaked further secrets, creating a cascading compromise. This is why experts are warning of long-lasting consequences: the blast radius extends to every system those credentials could reach.
Why the sector impact skews to tech, banking, and healthcare
Resecurity's sector breakdown maps directly to LiteLLM adoption patterns:
- Technology: Heavy early adopters of LLM gateways; highest density of LiteLLM deployments; secrets exposed often include cloud root-adjacent keys and source code access tokens.
- Banking/Finance: LLM proxies deployed for document processing, fraud triage, and internal copilots — frequently with access to core banking data stores and PCI-adjacent environments.
- Healthcare: Rapid LLM adoption for clinical documentation and claims processing, often running LiteLLM with credentials that touch PHI-bearing systems — creating direct HIPAA exposure.
Exploitation status
This is confirmed active, in-the-wild exploitation — not a proof of concept. Credential exposure at scale (2,038 repos) has already occurred, and the secondary use of harvested credentials means exploitation is ongoing. Organizations should treat this as an incident, not a vulnerability-management backlog item. Any LiteLLM installation present during the exposure window should be considered compromised until proven otherwise.
What makes this dangerous from a defender's seat
- No traditional IOCs at the perimeter: The malicious code arrives over TLS from a trusted package registry inside a legitimate install workflow.
- Inheritance through dependency trees:
pip freezeon your application may not show LiteLLM even when it's present three layers deep. - Credential half-life: Rotating the LiteLLM config keys is insufficient — the attacker's repo access means any secret ever committed, logged, or environment-injected in affected pipelines is burned.
Detection & Response
The detections below target the observable behaviors of a poisoned Python package in this class: anomalous network egress from Python/pip processes, access to credential stores and environment files by unexpected processes, and indicators of secret harvesting in CI runners. Tune thresholds to your environment — build servers and AI/ML hosts will need scoped exceptions.
SIGMA Rules
---
title: Python or Pip Process Suspicious Outbound Network Connection
description: Detects Python, pip, or Poetry processes initiating outbound network connections to non-standard destinations. Poisoned packages in the LiteLLM/SANDCLOCK supply-chain attack class exfiltrate harvested credentials immediately after install or at first import. Baseline known package mirrors (pypi.org, files.pythonhosted.org, internal artifactories) and alert on everything else.
references:
- https://attack.mitre.org/techniques/T1195/002/
- https://attack.mitre.org/techniques/T1041/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.exfiltration
- attack.t1041
- attack.t1195.002
logsource:
category: network_connection
product: windows
detection:
selection_image:
Image|endswith:
- '\python.exe'
- '\python3.exe'
- '\pip.exe'
- '\poetry.exe'
- '\conda.exe'
filter_known_dest:
DestinationHostname|endswith:
- 'pypi.org'
- 'pythonhosted.org'
- 'anaconda.com'
- 'github.com'
- 'microsoft.com'
- 'openai.com'
- 'anthropic.com'
condition: selection_image and not filter_known_dest
falsepositives:
- Legitimate LLM API calls from application runtimes (tune DestinationHostname list to your approved providers)
- Internal package mirrors not yet added to the filter
level: high
---
title: Suspicious Access to Environment and Credential Files by Scripting Runtimes
description: Detects Python or related scripting runtimes reading .env files, cloud CLI credential stores, or SSH material outside of expected application context. Credential harvesting is the core objective of the SANDCLOCK/TeamPCP supply-chain mechanism embedded in compromised LiteLLM artifacts.
references:
- https://attack.mitre.org/techniques/T1552/001/
- https://attack.mitre.org/techniques/T1552/004/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.credential_access
- attack.t1552.001
- attack.t1552.004
logsource:
category: file_event
product: windows
detection:
selection_target:
TargetFilename|contains:
- '\.env'
- '\.aws\credentials'
- '\.azure\'
- '\.config\gcloud\'
- '\.ssh\id_'
- '\.npmrc'
- '\.pypirc'
- '\.docker\config.json'
- '\.git-credentials'
- 'netrc'
selection_image:
Image|endswith:
- '\python.exe'
- '\python3.exe'
- '\pip.exe'
- '\node.exe'
condition: selection_target and selection_image
falsepositives:
- Applications that legitimately load .env at startup (scope to install-time windows and non-application directories)
- Developer workstations during local debugging
level: high
---
title: CI/CD Runner or Build Agent Executing Credential Enumeration Commands
description: Detects build agents and shell interpreters enumerating environment variables or secrets on Linux/macOS runners — a hallmark of poisoned dependency behavior during pip install in CI pipelines, consistent with the LiteLLM repository compromise tradecraft.
references:
- https://attack.mitre.org/techniques/T1552/
- https://attack.mitre.org/techniques/T1059/006/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.credential_access
- attack.t1552
- attack.execution
- attack.t1059.006
logsource:
category: process_creation
product: linux
detection:
selection:
CommandLine|contains:
- 'env |'
- 'printenv'
- 'cat /proc/1/environ'
- 'cat ~/.aws/credentials'
- 'cat .env'
- 'curl http://169.254.169.254'
- 'metadata.google.internal'
condition: selection
falsepositives:
- Legitimate pipeline steps dumping non-secret environment configuration (allow-list by pipeline name)
- Cloud-init and bootstrap scripts (scoped to first-boot windows)
level: medium
KQL — Microsoft Sentinel / Defender
// Hunt: Python/pip processes with anomalous outbound connections (non-approved destinations)
// Targets post-install exfiltration behavior from poisoned packages (LiteLLM/SANDCLOCK class)
let ApprovedDestinations = dynamic(["pypi.org","files.pythonhosted.org","api.openai.com","api.anthropic.com","github.com"]);
DeviceNetworkEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName has_any ("python", "python3", "pip", "poetry", "conda", "uv")
| where RemoteUrl !has_any (ApprovedDestinations) or isempty(RemoteUrl)
| extend RemoteIPType = iff(ipv4_is_private(RemoteIP), "Private", "Public")
| where RemoteIPType == "Public"
| summarize FirstSeen=min(Timestamp), LastSeen=max(Timestamp), Connections=count(),
RemoteIPs=make_set(RemoteIP, 20), RemoteUrls=make_set(RemoteUrl, 20)
by DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessAccountName
| where Connections > 0
| sort by FirstSeen asc;
// Companion hunt: processes accessing credential/env artifacts on endpoints
DeviceFileEvents
| where Timestamp > ago(14d)
| where FileName has_any (".env", "credentials", "id_rsa", "id_ed25519", ".pypirc", ".npmrc", "config.json")
or FolderPath has_any (".aws", ".azure", ".config\\gcloud", ".ssh", ".docker")
| where InitiatingProcessFileName has_any ("python", "pip", "node", "sh", "bash")
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FolderPath, FileName, InitiatingProcessAccountName
| sort by Timestamp desc;
// Linux fleets via Syslog ingestion: credential-file access and env enumeration on runners
Syslog
| where TimeGenerated > ago(14d)
| where SyslogMessage has_any ("printenv", "169.254.169.254", ".aws/credentials", "cat .env", "/proc/1/environ")
| project TimeGenerated, Computer, ProcessName, SyslogMessage
| sort by TimeGenerated desc;
Velociraptor VQL
-- Artifact: SecurityArsenal.SupplyChain.LiteLLMHunt
-- Hunt for LiteLLM presence, suspicious Python outbound connections, and credential-file touches
-- 1) Identify installed LiteLLM versions across the fleet (pip metadata)
SELECT FullPath, Size, Mtime
FROM glob(globs=['/**/site-packages/litellm*/version.py', '/**/site-packages/litellm-*/METADATA', 'C:/Users/*/AppData/**/site-packages/litellm*/**'])
-- 2) Python processes with active outbound connections (candidate exfil)
SELECT Pid, Name, CommandLine, Exe, Username
FROM pslist()
WHERE Name =~ '(?i)python|pip|poetry|uv'
-- 3) Correlate to live network connections from those processes
SELECT Pid, Name, Status, LocalAddr, LocalPort, RemoteAddr, RemotePort
FROM netstat()
WHERE Name =~ '(?i)python|pip'
AND Status =~ 'ESTAB'
-- 4) Recent modification on credential stores (harvest staging)
SELECT FullPath, Size, Mtime, Atime
FROM glob(globs=['/**/.env', '/**/.aws/credentials', '/**/.docker/config.json', '/**/.pypirc', 'C:/Users/*/.aws/credentials', 'C:/Users/*/.docker/config.json'])
WHERE Mtime > now() - 1209600
Verification and Hardening Script
#!/usr/bin/env bash
# LiteLLM / SANDCLOCK supply-chain exposure triage — run on app hosts, CI runners, and AI gateways
set -euo pipefail
REPORT="litellm_triage_$(hostname)_$(date +%Y%m%d_%H%M%S).txt"
{
echo "=== [1] LiteLLM installations detected ==="
find / -type d -name "litellm*" -path "*site-packages*" 2>/dev/null | head -50
pip list 2>/dev/null | grep -i litellm || echo "litellm not in default pip env"
echo "=== [2] All python environments with litellm ==="
for py in $(which -a python3 python 2>/dev/null); do
echo "-- $py --"
$py -m pip show litellm 2>/dev/null | grep -E '^(Name|Version|Location)' || echo "not installed"
done
echo "=== [3] Docker images containing litellm ==="
if command -v docker >/dev/null; then
for img in $(docker images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null); do
docker run --rm --entrypoint sh "$img" -c 'pip show litellm 2>/dev/null | grep Version' 2>/dev/null \
&& echo "FOUND in $img" || true
done
fi
echo "=== [4] Lockfiles / manifests referencing litellm ==="
grep -rIl --include='requirements*.txt' --include='pyproject.toml' --include='poetry.lock' --include='Pipfile.lock' --include='uv.lock' 'litellm' /srv /opt /home 2>/dev/null | head -50
echo "=== [5] Suspicious outbound connections from python (last 24h, journal) ==="
journalctl --since "-24h" 2>/dev/null | grep -iE 'python.*(curl|wget|requests|urllib)' | head -20 || true
echo "=== [6] Credential-file access timestamps (harvest check) ==="
for f in ~/.env ~/.aws/credentials ~/.docker/config.json ~/.pypirc ~/.git-credentials; do
[ -f "$f" ] && stat -c '%y %n' "$f"
done
} | tee "$REPORT"
echo "Report written to $REPORT — ship to your IR lead before rotating secrets."
# Windows endpoint / build agent triage for LiteLLM exposure
$report = "litellm_triage_$env:COMPUTERNAME.txt"
# 1) Locate litellm in all Python site-packages
Get-ChildItem -Path C:\, D:\ -Recurse -Directory -Filter "litellm*" -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match 'site-packages' } |
Select-Object FullName, LastWriteTime | Tee-Object -FilePath $report
# 2) Installed versions per interpreter
Get-Command python, python3, py -ErrorAction SilentlyContinue | ForEach-Object {
"--- $($_.Source) ---" | Tee-Object -Append $report
& $_.Source -m pip show litellm 2>$null | Tee-Object -Append $report
}
# 3) Credential artifact access times (harvest check)
$paths = @("$env:USERPROFILE\.env", "$env:USERPROFILE\.aws\credentials",
"$env:USERPROFILE\.docker\config.json", "$env:USERPROFILE\.pypirc",
"$env:USERPROFILE\.git-credentials", "$env:APPDATA\gcloud")
foreach ($p in $paths) {
if (Test-Path $p) { Get-Item $p | Select-Object FullName, LastAccessTime, LastWriteTime | Tee-Object -Append $report }
}
# 4) Python processes with established external connections right now
Get-NetTCPConnection -State Established -ErrorAction SilentlyContinue |
Where-Object { (Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName -match 'python|pip' } |
Select-Object LocalAddress, LocalPort, RemoteAddress, RemotePort, OwningProcess | Tee-Object -Append $report
Write-Host "Triage complete -> $report. Preserve before remediation."
Remediation
Given that the compromise vector is unauthorized access to the repository itself, there is no single "patch version" that closes this — remediation is incident response, not patch Tuesday. Execute in this order:
1. Establish exposure (hours, not days).
- Inventory every location LiteLLM exists: application virtualenvs, containers, serverless layers, CI runners, data-science notebooks, and AI gateway appliances. Use the scripts above plus your SBOM if you have one. Remember transitive inclusion — scan lockfiles (
poetry.lock,uv.lock,Pipfile.lock,package-lock.jsonfor wrappers), not just top-level requirements. - Determine which LiteLLM versions were installed and when, from package manager logs and CI artifacts. Cross-reference install dates against the public compromise disclosure window from the LiteLLM maintainers' advisory and the Security Affairs reporting.
2. Rotate everything LiteLLM could see — assume breach.
- Rotate all LLM provider keys (OpenAI, Anthropic, Azure OpenAI, Bedrock, Vertex) present in LiteLLM configs,
.envfiles, or environment variables on affected hosts. - Rotate cloud IAM credentials, database connection strings, Redis/Postgres passwords for LiteLLM's virtual-key store, and any secret mounted into the same container or runner.
- Revoke and reissue CI/CD secrets: the 2,038-repository exposure means GitHub/GitLab tokens, deploy keys, and artifact-registry credentials in affected pipelines are burned. Audit SCM audit logs for anomalous clone/access activity preceding rotation.
- Invalidate LiteLLM proxy master keys and virtual keys; force re-authentication for downstream consumers.
3. Remove and rebuild clean.
- Purge the tainted package: uninstall LiteLLM, clear pip caches (
pip cache purge), and rebuild containers from scratch — do not layer a fix on top of a compromised image. - Pin to a verified-clean version per the LiteLLM maintainers' official advisory (GitHub:
BerriAI/litellmsecurity advisories), verified by hash (pip install --require-hashes). If the advisory's clean-version guidance is still evolving, temporarily pin to a version released before the compromise window and gate re-upgrade on maintainer confirmation. - Rebuild CI runner images and revoke any runner registration tokens that were active during the exposure window.
4. Hunt for downstream use of stolen credentials.
- Review cloud audit logs (CloudTrail, Azure Activity Log, GCP Audit Logs) for API calls from the rotated keys — any use after your estimated compromise date is adversary activity.
- Review LLM provider billing/usage dashboards for anomalous spend; stolen AI API keys are monetized quickly.
- For the 2,038-repository exposure specifically: search your org's repos and CI logs for committed secrets (truffleHog / gitleaks across full history), because anything committed was visible to the actor's repo access.
5. Structural hardening so the next TeamPCP campaign hurts less.
- Egress control: AI gateways and build runners should have default-deny egress with an allow-list of package mirrors and approved LLM API endpoints. This single control would have broken the exfiltration leg of this attack.
- Dependency hygiene: enforce hash-pinning, use an internal artifact proxy (Artifactory/Nexus) with quarantine and manual promotion for new package versions, and disable auto-updates for security-critical dependencies.
- Secret isolation: never place long-lived cloud credentials in the same environment as a proxy that handles third-party SDK traffic; use workload identity / instance roles with short-lived tokens, and scope LLM provider keys per-service with spend caps.
- CI/CD segmentation: secrets required at build time should be injected just-in-time with minimal scope; runners should be ephemeral.
- Monitor the CISA Known Exploited Vulnerabilities catalog and your sector ISAC for follow-on guidance as the scope of this campaign is further enumerated.
Compliance note for healthcare and finance readers: if LiteLLM ran with credentials touching PHI or cardholder data environments, treat this as a reportable security incident under your HIPAA breach-notification and PCI-DSS incident-response obligations. The sector impact data in this reporting — banking and healthcare at the top of the list — means regulators will be asking. Document your exposure determination even if the answer is "not affected."
The long-tail consequence warning from researchers is accurate: credentials harvested in a supply-chain event like this get resold, replayed, and re-leveraged for months. Rotation is the beginning of this IR, not the end.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.