Back to Intelligence

LiteLLM 1.107.0 Supply Chain Compromise: PyPI Credential Stealer and Backdoor — Detection and Remediation Guide

SA
Security Arsenal Team
October 21, 2026
10 min read

The LiteLLM Python package — one of the most widely deployed libraries for routing calls to large language model APIs — has been compromised in a software supply chain attack. According to reporting from SafeDep, Socket, StepSecurity, and Upwind, LiteLLM version 1.107.0, distributed through both PyPI wheels and Docker images, was poisoned to deliver a credential stealer and a backdoor mechanism granting unauthorized access to compromised systems.

PyPI has since quarantined the malicious package, but the damage window matters: any developer workstation, CI/CD runner, container build, or production host that installed or pulled LiteLLM 1.107.0 during the exposure window must be treated as compromised. This is not a vulnerability to patch — it is an intrusion to investigate. LiteLLM sits in an especially sensitive position: it is typically deployed with API keys for OpenAI, Anthropic, Azure, AWS Bedrock, and dozens of other providers, and it frequently runs alongside cloud credentials and secrets in environment variables. A credential stealer embedded in this package has direct line-of-sight to high-value secrets.

This post gives defenders the attack-chain breakdown, detection content for your SOC, and a concrete remediation sequence.

Technical Analysis

Affected Products and Distribution Channels

AttributeDetail
PackageLiteLLM
Malicious version1.107.0
DistributionPyPI wheels (pip install) and Docker images
PayloadCredential stealer + unauthorized access (backdoor) mechanism
Current statusQuarantined on PyPI; investigate any prior installs
ReportingSafeDep, Socket, StepSecurity, Upwind

Attack Chain (Defender's View)

Supply chain compromises of PyPI packages follow a well-understood execution model, and defenders should hunt against each stage:

  1. Ingestion. A developer, CI pipeline, or Docker build executes pip install litellm (unpinned or pinned to 1.107.0), or pulls a poisoned Docker image. Wheel install-time code execution or import-time code execution runs the payload with the installing user's privileges.
  2. Credential harvesting. The stealer enumerates high-value secret stores: environment variables (where LLM API keys and cloud tokens live by convention in LiteLLM deployments), ~/.aws/credentials and ~/.aws/config, ~/.ssh/, ~/.config/gcloud/, ~/.azure/, .env files in working directories, Kubernetes service account tokens at /var/run/secrets/kubernetes.io/serviceaccount/, and CI/CD environment variables (GitHub Actions, GitLab CI, Jenkins secrets are all exposed to the build agent).
  3. Exfiltration. Harvested material is sent outbound over HTTPS to attacker-controlled infrastructure. In a container or CI context this is a single egress connection — easy to miss without egress monitoring.
  4. Persistence / unauthorized access. The backdoor component provides a mechanism for the attacker to re-enter or retain access independent of the credential theft — treat any host that ran the malicious package as having an active adversary foothold until proven otherwise.

Exploitation Status

This is a confirmed, in-the-wild malicious package — not a theoretical weakness. Multiple independent security research firms (SafeDep, Socket, StepSecurity, Upwind) analyzed and corroborated the compromise. PyPI's quarantine confirms the package index itself treats 1.107.0 as malicious. Any installation during the exposure window should be handled as an incident, not a hygiene issue.

Why LiteLLM Is a High-Impact Target

LiteLLM's entire purpose is brokering authenticated access to LLM providers. Its operational pattern requires API keys and cloud credentials to be present in the process environment. Compromising this package is roughly equivalent to compromising a secrets broker: the stealer doesn't need to search hard — the secrets are handed to the process by design. Additionally, LiteLLM is frequently deployed as a proxy/gateway service in enterprise AI stacks, meaning the backdoor may sit on an internally trusted network segment.

Detection & Response

Sigma Rules

YAML
---
title: Installation of Compromised LiteLLM 1.107.0 Package
id: 9c2e4a71-5b3d-4f6a-8e1c-2d7a9b0c3e5f
status: experimental
description: Detects pip/pip3/uv/poetry installation activity referencing the compromised LiteLLM 1.107.0 package distributed via PyPI in a supply chain attack delivering a credential stealer and backdoor.
references:
  - https://thehackernews.com/2026/10/litellm-compromised-in-supply-chain.html
author: Security Arsenal
date: 2026/10/21
tags:
  - attack.initial_access
  - attack.t1195.002
logsource:
  category: process_creation
  product: linux
detection:
  selection_tool:
    Image|endswith:
      - '/pip'
      - '/pip3'
      - '/uv'
      - '/poetry'
      - '/python'
      - '/python3'
  selection_cmd:
    CommandLine|contains:
      - 'litellm==1.107.0'
      - 'litellm-1.107.0'
      - 'litellm 1.107.0'
  condition: selection_tool and selection_cmd
falsepositives:
  - None expected; version 1.107.0 is confirmed malicious and quarantined
level: critical
---
title: Python Process Accessing Cloud or SSH Credential Stores
id: 4f8b1d63-7a2e-4c9b-b5d3-1e6f8a0c2d4b
status: experimental
description: Detects Python processes accessing AWS, GCP, Azure, or SSH credential files — consistent with the credential stealer payload delivered via the compromised LiteLLM 1.107.0 package. LiteLLM deployments hold API keys in-process, making secret-store access by python interpreters high signal.
references:
  - https://thehackernews.com/2026/10/litellm-compromised-in-supply-chain.html
author: Security Arsenal
date: 2026/10/21
tags:
  - attack.credential_access
  - attack.t1552.001
  - attack.t1552.004
logsource:
  category: file_event
  product: linux
detection:
  selection_paths:
    TargetFilename|contains:
      - '/.aws/credentials'
      - '/.aws/config'
      - '/.ssh/id_rsa'
      - '/.ssh/id_ed25519'
      - '/.config/gcloud/'
      - '/.azure/'
      - '/.kube/config'
      - '/var/run/secrets/kubernetes.io/serviceaccount/token'
  selection_env:
    TargetFilename|endswith:
      - '/.env'
  condition: 1 of selection_*
falsepositives:
  - Legitimate cloud SDK or infrastructure-as-code tooling; tune per-host baselines and investigate python-initiated access specifically
level: high
---
title: Python Interpreter Spawning Shell or Network Utility After Package Install
id: 2d6c9e14-8b4f-4a7d-9c2e-5f1a3b7d0e6c
status: experimental
description: Detects python/pip processes spawning shells or data-transfer utilities (curl, wget, base64, nc) — consistent with post-install staging and exfiltration behavior observed in PyPI supply chain compromises such as the LiteLLM 1.107.0 incident.
references:
  - https://thehackernews.com/2026/10/litellm-compromised-in-supply-chain.html
author: Security Arsenal
date: 2026/10/21
tags:
  - attack.execution
  - attack.exfiltration
  - attack.t1059.006
  - attack.t1041
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/python'
      - '/python3'
      - '/pip'
      - '/pip3'
  selection_child:
    Image|endswith:
      - '/sh'
      - '/bash'
      - '/curl'
      - '/wget'
      - '/nc'
      - '/ncat'
      - '/base64'
      - '/openssl'
  condition: selection_parent and selection_child
falsepositives:
  - Some legitimate build tooling spawns subprocesses; correlate with recent litellm install events and outbound connections
level: high

KQL (Microsoft Sentinel / Defender)

Hunt across both Windows endpoints (developer workstations) and Linux hosts ingested via Syslog/CEF for any execution of the compromised package version, then pivot to post-install network activity from python interpreters.

KQL — Microsoft Sentinel / Defender
// Hunt 1: Any installation or execution referencing compromised LiteLLM 1.107.0
let WindowStart = ago(30d);
union isfuzzy=true
(DeviceProcessEvents
| where TimeGenerated >= WindowStart
| where ProcessCommandLine has_any ("litellm==1.107.0", "litellm-1.107.0", "litellm 1.107.0")
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessCommandLine, Source="DeviceProcessEvents"),
(Syslog
| where TimeGenerated >= WindowStart
| where SyslogMessage has_any ("litellm==1.107.0", "litellm-1.107.0")
| project TimeGenerated, HostName, ProcessName, SyslogMessage, Source="Syslog")
| sort by TimeGenerated desc;
// Hunt 2: Python processes accessing credential stores (Linux via auditd/Syslog, Windows via Defender)
union isfuzzy=true
(DeviceFileEvents
| where TimeGenerated >= ago(14d)
| where InitiatingProcessFileName in~ ("python", "python3", "python.exe", "pip", "pip3")
| where FolderPath has_any (".aws\\credentials", ".aws/credentials", ".ssh\\id_", ".ssh/id_", ".azure", ".config/gcloud")
   or FileName =~ ".env"
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessCommandLine, FolderPath, FileName),
(Syslog
| where TimeGenerated >= ago(14d)
| where SyslogMessage has_any (".aws/credentials", ".ssh/id_rsa", ".ssh/id_ed25519", ".config/gcloud", "serviceaccount/token")
| where SyslogMessage has_any ("python", "pip")
| project TimeGenerated, HostName, ProcessName, SyslogMessage);
// Hunt 3: Outbound connections from python interpreters following a litellm install — exfil pivot
DeviceNetworkEvents
| where TimeGenerated >= ago(14d)
| where InitiatingProcessFileName in~ ("python", "python3", "python.exe")
| where RemotePort in (443, 80)
| where RemoteUrl !has_any ("pypi.org", "files.pythonhosted.org", "openai.com", "anthropic.com", "azure.com", "amazonaws.com")
| summarize Connections=count(), RemoteIPs=make_set(RemoteIP), URLs=make_set(RemoteUrl) by DeviceName, InitiatingProcessCommandLine, bin(TimeGenerated, 1h)
| order by Connections desc;

Velociraptor VQL

Use this artifact across your Linux fleet to (a) confirm whether the compromised version is present in any site-packages, and (b) identify running LiteLLM processes that may have loaded the backdoor.

VQL — Velociraptor
-- LiteLLM 1.107.0 Compromise Triage: installed version + running processes
SELECT * FROM foreach(
  row={
    SELECT FullPath
    FROM glob(globs=['/usr/lib/python3*/site-packages/litellm-*/METADATA',
                     '/usr/local/lib/python3*/site-packages/litellm-*/METADATA',
                     '/opt/**/site-packages/litellm-*/METADATA',
                     '/home/*/.local/lib/python3*/site-packages/litellm-*/METADATA',
                     '/home/*/*/site-packages/litellm-*/METADATA',
                     '/usr/lib/python3*/dist-packages/litellm-*/METADATA'])
  },
  query={
    SELECT FullPath,
           read_file(filename=FullPath, length=4096) AS Metadata,
           parse_regex(source=FullPath, regex='litellm-(?P<Version>[0-9.]+)').Version AS InstalledVersion
    FROM scope()
    WHERE InstalledVersion =~ '1\\.107\\.0'
  })
VQL — Velociraptor
-- Running python processes with litellm loaded or in command line, plus their network connections
SELECT Pid, Name, CommandLine, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ 'litellm'
   OR Exe =~ 'python'

SELECT Pid, Name, CommandLine,
       netstat().RemoteIP AS RemoteIP,
       netstat().RemotePort AS RemotePort,
       netstat().Status AS ConnStatus
FROM pslist()
WHERE CommandLine =~ 'litellm'

Remediation Script (Bash)

Run this across developer workstations, CI runners, and container hosts. It verifies exposure, removes the malicious package, and flags follow-up actions. Credential rotation is manual and mandatory — no script can do it for you.

Bash / Shell
#!/bin/bash
# LiteLLM 1.107.0 Supply Chain Compromise - Verify & Remediate
set -euo pipefail

echo "=== [1/5] Checking installed LiteLLM versions (all interpreters) ==="
for PY in python3 python; do
  if command -v "$PY" >/dev/null 2>&1; then
    VER=$("$PY" -m pip show litellm 2>/dev/null | awk '/^Version:/{print $2}')
    if [ -n "${VER:-}" ]; then
      echo "  $PY -> litellm $VER"
      if [ "$VER" == "1.107.0" ]; then
        echo "  *** COMPROMISED VERSION DETECTED - removing ***"
        "$PY" -m pip uninstall -y litellm
      fi
    fi
  fi
done

echo "=== [2/5] Scanning virtualenvs and site-packages for litellm 1.107.0 ==="
find / -type d -name "litellm-1.107.0*" 2>/dev/null | while read -r d; do
  echo "  FOUND: $d  -- remove after review"
done

echo "=== [3/5] Checking Docker images for compromised package ==="
if command -v docker >/dev/null 2>&1; then
  docker images --format '{{.Repository}}:{{.Tag}} {{.ID}}' | while read -r img id; do
    if docker run --rm --entrypoint sh "$id" -c 'pip show litellm 2>/dev/null | grep -q "1.107.0"' 2>/dev/null; then
      echo "  *** COMPROMISED IMAGE: $img ($id) ***"
    fi
  done
fi

echo "=== [4/5] Auditing python persistence points for backdoor tampering ==="
for PYDIR in /usr/lib/python3*/site-packages /usr/local/lib/python3*/site-packages; do
  for f in sitecustomize.py usercustomize.py; do
    [ -f "$PYDIR/$f" ] && echo "  REVIEW: $PYDIR/$f (mtime: $(stat -c %y "$PYDIR/$f"))"
  done
done
grep -rn "litellm" ~/.bashrc ~/.bash_profile ~/.profile /etc/profile.d/ 2>/dev/null || echo "  No shell-profile references found"

echo "=== [5/5] MANUAL ACTIONS REQUIRED ==="
cat <<'EOF'
  1. ROTATE all credentials present on any host that ran litellm 1.107.0:
     - LLM provider API keys (OpenAI, Anthropic, Azure OpenAI, Bedrock, etc.)
     - AWS/GCP/Azure credentials, SSH keys, kubeconfig service accounts
     - CI/CD secrets accessible to build agents during the exposure window
  2. Review egress logs from affected hosts for unexpected HTTPS destinations.
  3. Rebuild affected container images from a clean base; do not patch in place.
  4. Preserve evidence (pip logs, shell history, image layers) before cleanup
     if forensic investigation is in scope.
EOF

Remediation

Treat this as an incident response engagement, not a package update:

  1. Identify exposure immediately. Enumerate every host, virtualenv, CI pipeline, lockfile, and Docker image that resolved LiteLLM 1.107.0. Check requirements.txt, poetry.lock, Pipfile.lock, and image build logs — unpinned installs (pip install litellm) during the window are the most dangerous and the least visible.
  2. Remove the malicious package. Uninstall LiteLLM 1.107.0 everywhere and reinstall a known-good version from the now-quarantined PyPI listing. Verify the package maintainers' (BerriAI) official guidance on the LiteLLM GitHub repository before selecting a replacement version — confirm which versions the maintainers attest as clean.
  3. Rotate every secret in blast radius — assume theft. This is the non-negotiable step. Any host that executed the malicious package must have all accessible credentials rotated: LLM provider API keys, cloud IAM credentials and session tokens, SSH private keys, Kubernetes service account tokens, .env file contents, and CI/CD pipeline secrets. Where possible, audit provider-side logs (AWS CloudTrail, Azure sign-in logs, OpenAI/Anthropic usage dashboards) for use of the old keys from unrecognized sources.
  4. Rebuild, don't patch, container workloads. Any Docker image built with the compromised layer should be rebuilt from a clean base. Review running containers for unexpected processes and outbound connections; recycle pods/hosts rather than cleaning in place.
  5. Hunt for persistence. The backdoor component means removal of the package alone does not remove adversary access. Audit sitecustomize.py/usercustomize.py, shell profiles, cron, systemd units, and any new local accounts or authorized_keys entries on affected hosts.
  6. Add preventive controls going forward. Pin dependencies by hash (pip install --require-hashes), route builds through a vetted internal package proxy or artifact repository, deploy Socket/StepSecurity-style package analysis in CI, enforce egress filtering on build agents and AI gateway hosts, and consider running LLM proxy workloads in isolated network segments with no ambient cloud credentials.

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.