Back to Intelligence

LiteLLM 1.107.0 Supply Chain Compromise: Detecting Python Startup Hook Credential Theft and Remediating Exposed AI Pipelines

SA
Security Arsenal Team
October 23, 2026
14 min read

Microsoft Threat Intelligence has published a detailed analysis of a supply chain compromise affecting LiteLLM 1.107.0, one of the most widely deployed Python libraries for routing calls to large language model providers (OpenAI, Anthropic, Azure OpenAI, Bedrock, and dozens more). The trojanized release abuses Python startup hooks — the interpreter's own auto-execution mechanisms — to run malicious code every time Python starts, harvest credentials, and pull down second-stage payloads from attacker-controlled infrastructure.

This is not a theoretical risk. LiteLLM sits at the exact intersection attackers want: it is almost always deployed with high-value secrets in its environment variables — LLM API keys, cloud credentials, database connection strings — and it is deeply embedded in CI/CD pipelines, AI agent frameworks, and production inference services. Any organization that installed or upgraded to LiteLLM 1.107.0 during the exposure window must treat every secret present in that process environment as compromised until proven otherwise.

What makes this campaign technically interesting — and dangerous — is the persistence and execution vector. Rather than a simple import-time payload, the attackers leveraged Python's startup machinery (.pth files and interpreter initialization paths), meaning the malicious code executes on every Python interpreter launch, not just when LiteLLM itself is imported. That dramatically widens the blast radius: any Python process on a contaminated host becomes a collection point.

This post breaks down the attack chain from a defender's perspective and provides concrete Sigma, KQL, and Velociraptor detections, plus a verification and remediation script you can run today.

Technical Analysis

Affected Products and Platforms

  • Package: LiteLLM (PyPI), version 1.107.0 specifically
  • Ecosystem: Any Python environment that resolved litellm==1.107.0 (or an unpinned litellm install/upgrade during the exposure window) from PyPI
  • Platforms: Cross-platform — Linux, macOS, and Windows; Docker images, CI/CD runners, virtual environments, and serverless build agents are all in scope
  • Downstream exposure: AI application backends, LangChain/LlamaIndex-adjacent stacks, AI agent orchestrators, Jupyter/notebook environments, and any container built from a base image that pip-installed the malicious version

No CVE identifier has been published for this compromise at the time of writing — this is a package-level trojanization event, not a traditional software vulnerability. Do not wait for a CVE to act; the unit of compromise here is the package artifact itself.

Attack Chain Breakdown

Based on Microsoft Threat Intelligence's analysis, the compromise unfolds as follows:

  1. Poisoned distribution. Version 1.107.0 of LiteLLM published to PyPI contained malicious additions not present in the legitimate upstream source. Developers and automated pipelines pulling pip install litellm or pip install -U litellm during the exposure window received the trojanized artifact.

  2. Python startup hook execution (T1546 / T1059.006). Rather than burying execution solely in an import-time code path, the payload registers itself via Python interpreter startup mechanisms — most notably .pth files dropped into site-packages, which the interpreter processes at every launch, and tampering consistent with sitecustomize/usercustomize initialization behavior. A .pth file containing a line beginning with import executes arbitrary code each time Python starts. This means:

    • The payload runs even if the victim never imports LiteLLM again
    • Every Python process — build scripts, cron jobs, unrelated applications — triggers the hook
    • Removal requires more than pip uninstall litellm; the dropped artifacts must be found and deleted
  3. Credential harvesting (T1552.001 — Credentials in Files / T1552.005 — Cloud Instance Metadata). The payload enumerates and exfiltrates:

    • Environment variables — the crown jewels in any LiteLLM deployment: OPENAI_API_KEY, ANTHROPIC_API_KEY, AZURE_API_KEY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DATABASE_URL, and any other secret injected into the process environment
    • Well-known credential files — ~/.aws/credentials, ~/.config/gcloud/, ~/.ssh/, .env files, Kubernetes service account tokens
    • LLM provider configuration and keys stored alongside LiteLLM deployments
  4. Remote code loading (T1105). The startup hook reaches out to attacker-controlled infrastructure to fetch and execute a second-stage payload, giving the operators an upgrade path from credential theft to full remote code execution on the victim host.

Exploitation Status

  • Confirmed in-the-wild compromise — this is an observed, analyzed campaign documented by Microsoft Threat Intelligence, not a proof-of-concept
  • Active risk window: any environment that resolved the malicious version during the period it was live on PyPI, plus any artifact (container image, wheel cache, vendored dependency directory, lockfile-resolved mirror) built during that window
  • No CISA KEV entry is applicable — this is a package compromise, not a CVE-tracked vulnerability — but the operational urgency is equivalent to a KEV-listed item

Why the Startup Hook Technique Matters

Traditional dependency-confusion and typosquatting payloads fire at import time. Blue teams have gotten reasonably good at hunting those (e.g., alerting on setup.py post-install behavior). The startup-hook approach is more insidious:

  • Interpreter-global execution: .pth files with import lines execute in every Python process on the system
  • Stealth: no suspicious process spawning is required — the malicious code runs inside trusted Python processes, inheriting their environment, network egress permissions, and identity (including cloud metadata service access)
  • Persistence that survives uninstall: pip uninstall litellm removes the package, but dropped .pth files or modified sitecustomize.py may persist if they were written outside the package's manifest

Detection & Response

The detections below target the observable behaviors described in Microsoft's analysis: suspicious .pth/sitecustomize artifacts, Python processes accessing credential stores, and interpreter-launched network egress to non-standard destinations.

Sigma Rules

YAML
---
title: Python Startup Hook Artifact Created in Site-Packages
id: 3f8a1c94-6d2b-4e71-9a05-7c4b2d8e1f60
status: experimental
description: Detects creation of .pth files or modification of sitecustomize.py/usercustomize.py inside Python site-packages, a persistence and execution technique used in the LiteLLM 1.107.0 supply chain compromise.
references:
  - https://www.microsoft.com/en-us/security/blog/2026/10/23/inside-the-litellm-1-107-0-supply-chain-compromise-credential-theft-through-python-startup-hooks/
  - https://attack.mitre.org/techniques/T1546/
author: Security Arsenal
date: 2026/10/24
tags:
  - attack.persistence
  - attack.execution
  - attack.t1546
  - attack.t1059.006
logsource:
  category: file_event
  product: linux
detection:
  selection_path:
    TargetFilename|contains:
      - '/site-packages/'
      - '/dist-packages/'
  selection_artifact:
    TargetFilename|endswith:
      - '.pth'
      - 'sitecustomize.py'
      - 'usercustomize.py'
  filter_legit_pth:
    TargetFilename|contains:
      - 'distutils-precedence.pth'
      - '__editable__'
  condition: selection_path and selection_artifact and not filter_legit_pth
falsepositives:
  - Legitimate packages shipping .pth files (e.g. setuptools, editable installs) - baseline per package manager activity
level: high
---
title: Python Process Reading Cloud or SSH Credential Stores
id: 91c2e7d4-3a85-4f19-b6c2-8d1e4a7f5c30
status: experimental
description: Detects Python interpreter processes accessing cloud credential files, SSH private keys, or .env files, consistent with the credential harvesting behavior in the LiteLLM 1.107.0 compromise.
references:
  - https://www.microsoft.com/en-us/security/blog/2026/10/23/inside-the-litellm-1-107-0-supply-chain-compromise-credential-theft-through-python-startup-hooks/
  - https://attack.mitre.org/techniques/T1552/001/
author: Security Arsenal
date: 2026/10/24
tags:
  - attack.credential_access
  - attack.t1552.001
  - attack.t1552.004
logsource:
  category: file_event
  product: linux
detection:
  selection:
    TargetFilename|contains:
      - '/.aws/credentials'
      - '/.aws/config'
      - '/.ssh/id_rsa'
      - '/.ssh/id_ed25519'
      - '/.config/gcloud/'
      - '/.azure/'
      - '/.kube/config'
      - '/.env'
      - '/run/secrets/'
  condition: selection
falsepositives:
  - Legitimate cloud SDK and boto3 credential loading - tune by process tree and frequency; investigate any read not paired with an expected workload
level: medium
---
title: Curl or Wget Launched from Python Process Tree
id: 5b17d8a2-9f43-4c68-a1d7-2e9b3c6f8041
status: experimental
description: Detects download utilities spawned by Python interpreters, consistent with the remote code loading stage of the LiteLLM 1.107.0 compromise where a second-stage payload is fetched from attacker infrastructure.
references:
  - https://www.microsoft.com/en-us/security/blog/2026/10/23/inside-the-litellm-1-107-0-supply-chain-compromise-credential-theft-through-python-startup-hooks/
  - https://attack.mitre.org/techniques/T1105/
author: Security Arsenal
date: 2026/10/24
tags:
  - attack.command_and_control
  - attack.t1105
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/python'
      - '/python3'
      - '/python3.10'
      - '/python3.11'
      - '/python3.12'
      - '/python3.13'
  selection_child:
    Image|endswith:
      - '/curl'
      - '/wget'
  condition: selection_parent and selection_child
falsepositives:
  - Build scripts and installers legitimately chaining curl from Python - review destination URLs and parent command lines
level: high

KQL — Microsoft Sentinel / Defender

This hunt looks across file events and process telemetry for the three core behaviors: startup-hook artifact creation, credential store access, and Python-initiated outbound connections to hosts outside your expected package/LLM endpoints. Run it over the full exposure window plus 30 days of lookback.

KQL — Microsoft Sentinel / Defender
// Hunt 1: Suspicious .pth / sitecustomize artifacts and credential store access on endpoints
let Lookback = 30d;
let CredPaths = dynamic(["/.aws/credentials", "/.ssh/id_rsa", "/.ssh/id_ed25519", "/.config/gcloud", "/.azure", "/.kube/config", "/.env"]);
union isfuzzy=true
(
    DeviceFileEvents
    | where Timestamp > ago(Lookback)
    | where FolderPath has_any ("/site-packages/", "/dist-packages/")
    | where FileName endswith ".pth"
       or FileName in~ ("sitecustomize.py", "usercustomize.py")
    | where ActionType has_any ("FileCreated", "FileModified")
    | extend HuntSignal = "StartupHookArtifact"
    | project Timestamp, DeviceName, HuntSignal, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
),
(
    DeviceFileEvents
    | where Timestamp > ago(Lookback)
    | where FolderPath has_any (CredPaths)
    | where InitiatingProcessFileName has_any ("python", "python3")
    | extend HuntSignal = "PythonCredStoreAccess"
    | project Timestamp, DeviceName, HuntSignal, FileName, FolderPath, InitiatingProcessFileName, InitiatingProcessCommandLine, SHA256
)
| order by Timestamp desc;

// Hunt 2: Python processes making network connections to non-standard destinations
let Lookback = 30d;
let ExpectedDestinations = dynamic(["pypi.org", "files.pythonhosted.org", "api.openai.com", "api.anthropic.com", "openai.azure.com", "amazonaws.com", "googleapis.com", "huggingface.co"]);
DeviceNetworkEvents
| where Timestamp > ago(Lookback)
| where InitiatingProcessFileName has_any ("python", "python3")
| where ActionType == "ConnectionSuccess"
| where not(RemoteUrl has_any (ExpectedDestinations))
| summarize Connections = count(), DistinctDestinations = dcount(RemoteUrl), Destinations = make_set(RemoteUrl, 25), Ports = make_set(RemotePort, 10) by DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Connections desc;

Velociraptor VQL

This artifact inventories Python startup-hook artifacts across endpoints and flags anything that is not a known-good package artifact. Review the Content field for import lines — that is where executable payloads hide.

VQL — Velociraptor
-- Hunt for malicious Python startup hooks (.pth files, sitecustomize/usercustomize tampering)
-- associated with the LiteLLM 1.107.0 supply chain compromise
LET site_package_globs = SELECT * FROM parse_csv(filename='''/usr/lib/python*/site-packages/*.pth
/usr/local/lib/python*/site-packages/*.pth
/usr/lib/python*/dist-packages/*.pth
/opt/**/lib/python*/site-packages/*.pth
/home/*/.local/lib/python*/site-packages/*.pth
/root/.local/lib/python*/site-packages/*.pth''', accessor='data')

SELECT FullPath AS PthFile,
       Mtime,
       Size,
       read_file(filename=FullPath, length=2048) AS Content,
       hash(path=FullPath).SHA256 AS SHA256
FROM glob(globs=site_package_globs.Line, accessor='file')
WHERE Content =~ 'import '
   OR FullPath =~ 'sitecustomize|usercustomize'

Remediation and Verification Script

Run this on any Linux/macOS host, container image, or CI runner that may have installed LiteLLM 1.107.0. It checks installed versions, hunts for startup-hook artifacts, and flags unexpected .pth files for review. This script detects — it does not auto-delete — because .pth removal decisions should be reviewed against your baseline.

Bash / Shell
#!/usr/bin/env bash
# LiteLLM 1.107.0 compromise verification script - Security Arsenal
# Checks for the malicious package version and Python startup hook artifacts

set -u
echo "=== [1] Installed LiteLLM version check ==="
python3 -m pip list 2>/dev/null | grep -i litellm || echo "litellm not found via pip"
pip3 list 2>/dev/null | grep -i litellm

# Check all discovered site-packages / dist-packages directories
echo ""
echo "=== [2] Locating site-packages directories ==="
SITE_DIRS=$(python3 -c "import site, sys; print(' '.join(site.getsitepackages() + [site.getusersitepackages()]))" 2>/dev/null)
SITE_DIRS="$SITE_DIRS $(find /usr /usr/local /opt /home -maxdepth 8 -type d \( -name 'site-packages' -o -name 'dist-packages' \) 2>/dev/null)"
SITE_DIRS=$(echo "$SITE_DIRS" | tr ' ' '\n' | sort -u)

echo ""
echo "=== [3] Inventorying .pth files and flagging executable import lines ==="
for dir in $SITE_DIRS; do
  [ -d "$dir" ] || continue
  for pth in "$dir"/*.pth; do
    [ -f "$pth" ] || continue
    if grep -qE '^[[:space:]]*import ' "$pth" 2>/dev/null; then
      echo "[ALERT] Executable .pth found: $pth"
      echo "        Contents: $(head -c 300 "$pth")"
      echo "        SHA256:   $(sha256sum "$pth" | awk '{print $1}')"
    else
      echo "[INFO]  Benign-looking .pth: $pth"
    fi
  done
done

echo ""
echo "=== [4] Checking sitecustomize.py / usercustomize.py for anomalies ==="
for dir in $SITE_DIRS; do
  for f in "$dir/sitecustomize.py" "$dir/usercustomize.py"; do
    [ -f "$f" ] || continue
    echo "[REVIEW] $f"
    grep -nE 'urllib|requests|socket|subprocess|base64|environ|getenv|exec|eval' "$f" && echo "[ALERT] Suspicious imports/calls in $f"
  done
done

echo ""
echo "=== [5] litellm package directory integrity ==="
for dir in $SITE_DIRS; do
  [ -d "$dir/litellm" ] || continue
  echo "[INFO] litellm found in $dir - checking for unexpected files"
  find "$dir/litellm" -name '*.pth' -o -name '*.so' -newer "$dir/litellm/__init__.py" 2>/dev/null
done

echo ""
echo "=== [6] Recent pip install events for litellm 1.107.0 (shell history / logs) ==="
grep -rh "litellm==1.107.0\|litellm-1.107.0" /var/log/pip*.log ~/.pip 2>/dev/null | tail -20
grep -h "pip install.*litellm" ~/.bash_history /root/.bash_history 2>/dev/null | tail -20

echo ""
echo "Done. Any [ALERT] line requires immediate investigation and credential rotation."

Remediation

Treat this as a credential exposure incident, not a routine package update. The sequence matters — rotating credentials before evicting the implant risks re-harvesting the new secrets.

1. Identify exposure immediately.

  • Query your software composition analysis (SCA), SBOM, artifact registry, and lockfiles (requirements.txt, poetry.lock, Pipfile.lock, uv.lock) for litellm==1.107.0
  • Audit container image builds and CI/CD job logs for installs during the exposure window — remember that an unpinned pip install -U litellm resolves silently
  • Check private PyPI mirrors/caches (Artifactory, Nexus, devpi, bandersnatch) — the malicious artifact may still be cached internally even after PyPI removal

2. Quarantine before you clean.

  • Isolate affected hosts and runners from network egress where feasible
  • Do not simply pip uninstall litellm and move on — startup-hook artifacts (.pth files, sitecustomize.py) may survive uninstallation. Run the verification script above and manually remove flagged artifacts
  • Rebuild container images from clean bases with a pinned, known-good LiteLLM version rather than patching running images

3. Downgrade/pin to a known-good version.

  • Pin LiteLLM to the last verified clean release prior to 1.107.0 (or the fixed version identified in the vendor/registry advisory), and pin with hashes (pip install --require-hashes) going forward
  • Verify package integrity against the upstream GitHub source tree before redeploying — Microsoft's analysis and the LiteLLM maintainers' advisory should be your source of truth for which versions are clean

4. Rotate everything that was in scope. This is the most critical step.

  • Rotate all LLM provider API keys (OpenAI, Anthropic, Azure OpenAI, Google, Bedrock) present on affected systems
  • Rotate cloud IAM credentials: AWS access keys, GCP service account keys, Azure service principal secrets — and review CloudTrail / audit logs for use of those keys from unrecognized principals or ASNs
  • Rotate database credentials, .env secrets, SSH keys, and any Kubernetes service account tokens accessible to the affected process
  • Invalidate active sessions/tokens, not just static keys

5. Hunt for post-exploitation.

  • Review egress logs from affected hosts for connections to non-standard destinations during and after the exposure window (the remote code loading stage)
  • Check cloud provider logs for anomalous API key usage — LLM API keys are monetizable and are typically abused quickly (fraudulent inference, crypto-jacking via hosted models, resale)
  • Inspect for persistence beyond the Python hooks: new cron entries, systemd units, modified shell profiles

6. Harden the pipeline so this doesn't recur.

  • Enforce hash-pinning for all Python dependencies (pip-tools, Poetry, or uv with hash checking)
  • Gate dependency upgrades behind a quarantine period (24–72 hours) for non-security-critical bumps — most trojanized packages are pulled from registries within hours
  • Deploy egress filtering on build runners and production Python workloads: they should reach PyPI and your approved LLM endpoints — nothing else
  • Adopt an internal package proxy that only serves approved, scanned artifacts
  • Monitor PyPI/OpenSSF advisories and Microsoft Threat Intelligence for follow-on packages from the same operators; supply chain actors rarely stop at one package

Official references: Microsoft Threat Intelligence's full analysis (IoCs, file hashes, and infrastructure indicators) is published at the Microsoft Security Blog post linked in this article's source. Cross-reference their published IoCs against your proxy, DNS, and EDR telemetry for the full exposure window.

Closing

The LiteLLM 1.107.0 compromise is a textbook example of why AI infrastructure is now a first-class attack surface. The library that brokers access to your most expensive API keys was turned into the exfiltration channel for those exact keys — and the Python startup hook technique means the payload fired in every interpreter on the box, not just the AI workload. The defenders who come out of this well are the ones who treat dependency provenance as seriously as endpoint protection: hash-pinned dependencies, egress-constrained runners, and a credential-rotation runbook that can execute in hours, not days.

If your team needs help validating exposure, hunting for post-compromise activity, or hardening your AI/ML supply chain, Security Arsenal's incident response and offensive security teams can help you get there.

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.