← All hunts high TLP:CLEAR Part 2 of 2

LiteLLM Supply Chain Poisoning and Persistence

An adversary has installed a poisoned version of the LiteLLM library to establish persistence via Python startup files and exfiltrate host credentials.

Based on research by ESET Research 2026-10-29 12 steps · 4 queries T1195.002 T1555 T1574.013

Brief

Why This Hunt Matters

ESET Research recently detailed several supply chain attacks in their Lessons from Cybersecurity Black Week (https://www.welivesecurity.com/en/business-security/lessons-cybersecurity-black-week/) report. One specific threat involves a poisoned version of the LiteLLM library, version 1.107.0. This malicious package contains code designed to establish persistence and exfiltrate sensitive host credentials. Because adversaries can rename packages or modify version strings, relying solely on package inventory lists is insufficient.

How the Hunt Flows

The hunt starts with a scoping phase focused on process history. It searches for pip installation commands or execution strings that contain the specific malicious version string. This identifies which hosts attempted the installation, capturing activity even on ephemeral build agents that might no longer be active in the current fleet inventory.

Once an analyst or automated agent confirms a valid lead, the hunt gates into a parallel deep-dive. One branch stacks .pth files across the environment. Malicious Python packages frequently drop these files into site-packages directories to hijack the Python startup process. By looking for files that appear on fewer than five hosts, the hunt isolates custom persistence mechanisms from legitimate library files.

Simultaneously, the hunt monitors for Python processes accessing sensitive file paths. It specifically looks for reads of AWS credentials, SSH private keys, and Docker configuration files. This behavioral signal confirms if the poisoned package has begun its primary objective of credential theft.

In the final phase, an agent correlates the initial installation lead with the observed persistence and file access patterns. This multi-surface approach allows for a high-confidence verdict. If a host shows the malicious package alongside rare .pth files or unauthorized secret access, the hunt triggers host isolation to stop the exfiltration.

Blind Spots and Limitations

This hunt relies heavily on process command-line auditing. If a host lacks process telemetry, the initial scoping query will not see the installation event. Furthermore, the file access monitoring targets standard paths for cloud and shell secrets. If an adversary targets non-standard credential stores or custom application secrets, the behavioral queries will not observe the theft.

Steps

  1. Detect malicious LiteLLM installation events

    Query · scoping

    Identify hosts that attempted to install the compromised version of LiteLLM, including ephemeral hosts.

    reads hb_process_activitysql
    SELECT device_hostname, process_name, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%pip%install%litellm%{{malicious_version}}%' OR LOWER(process_cmd_line) LIKE '%litellm-{{malicious_version}}%') AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. Rows showing pip or python commands installing the malicious version. Silence means no recorded installation events in the window.

  2. Assess lead evidence

    Agent triage

    Read only the lead query to confirm if any host is affected.

  3. Gate on installation activity

    Decision

    Open the expensive behavioral queries only when the lead is real.

  4. Rare .pth file persistence

    Query · baseline

    Stack-count .pth file creations to find rare startup hijacks and attribute them to a process.

    reads hb_file_activitysql
    SELECT file_name, file_path, process_name, process_cmd_line, COUNT(DISTINCT device_hostname) as host_count, MIN(time) as first_seen FROM hb_file_activity WHERE LOWER(file_name) LIKE '%.pth' AND (LOWER(file_path) LIKE '%site-packages%' OR LOWER(file_path) LIKE '%dist-packages%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY file_name, file_path, process_name, process_cmd_line HAVING host_count < 5

    What a hit looks like. Rare .pth files created on one or two hosts. Persistent malicious startup files will stand out from common library files.

  5. Check current package inventory

    Query · enrichment

    Confirm if the malicious version is currently installed on any disk in the fleet.

    reads hb_software_inventorysql
    SELECT device_hostname, package_name, package_version, install_path FROM hb_software_inventory WHERE LOWER(package_name) = 'litellm' AND package_version = '{{malicious_version}}' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)

    What a hit looks like. A snapshot of hosts with the vulnerable version currently installed.

  6. Python credential file access

    Query · detection candidate

    Find Python processes reading sensitive local secret stores.

    reads hb_file_activitysql
    SELECT device_hostname, process_name, process_cmd_line, file_path, time FROM hb_file_activity WHERE activity_id = 2 AND LOWER(process_name) LIKE '%python%' AND (LOWER(file_path) LIKE '%/.aws/credentials' OR LOWER(file_path) LIKE '%/.ssh/id_%' OR LOWER(file_path) LIKE '%/config.json' OR LOWER(file_path) LIKE '%/.dockercfg' OR LOWER(file_path) LIKE '%/.npmrc') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. Reads of cloud or SSH credentials by a Python interpreter. Legitimate tools may exist, but those matching the scope hosts are high risk.

  7. Triage compromise and impact

    Agent triage

    A second agent weighs the installation lead with the behavioral evidence.

  8. Route on triage verdict

    Decision

    Direct confirmed hits to containment and uncertain ones to review.

  9. Isolate compromised host

    Response action

    Stop the RAT and credential exfiltration immediately.

  10. Analyst review

    Analyst task

    Verify behavioral findings and identify exposed secrets.

  11. Close out

    Analyst task

    Final reporting and health check.

Coverage

Scenario coverage

StageCoveredHow, or why not
Supply Chain Poisoning
T1195.002
Yes litellm-install-lead, litellm-inventory-check
Python Startup Hijack
T1574.013
Yes pth-persistence-prevalence
Credential Theft and RAT Activity
T1555 · T1071.001
Yes python-credential-theft
Vulnerable Service Exposure
T1190
Out of scope Belongs to another part of the 'Lessons from Cybersecurity Black Week' series.
Memory Safety Exploitation
T1203
Out of scope Belongs to another part of the 'Lessons from Cybersecurity Black Week' series.
Intermediate CA Trust Compromise
T1553.004
Out of scope Belongs to another part of the 'Lessons from Cybersecurity Black Week' series.

Blind spots

  • Needs hb_process_activity with command-line auditing. A host that installs the package but does not report process history will be missed by the lead query. It would answer whether the installation occurred on a host without process logging.
  • Needs hb_file_activity with activity_id = 2 (Read) auditing for sensitive directories. If the RAT targets non-standard secret locations, the behavior will be invisible. It would answer whether the RAT accessed secrets not in the standard searched paths.

Parameters & data

Parameters

ParameterTypeDefaultWhat it is
lookback_daysnumber14Days of history to examine for installation and behavior.
malicious_versionstring1.107.0The specific compromised version of LiteLLM.
scope_hostslist[host]—Optional list of hostnames to focus on after the scoping step.

Telemetry

SourceCategoryTelemetry
Endpoint telemetry (hb_ surfaces)endpointendpoint

Source

Download hunt.md Definition (JSON) An open hunt.md file; it runs anywhere that reads the format.
---
analysis: A static rule for package versions is bypassed by renaming. This hunt uses
  prevalence counting across .pth files and correlates it with specific Python-based
  file access behavioral patterns, providing a cross-surface signal that simple version
  checks miss.
blind_spots:
- id: no-process-telemetry
  question: whether the installation occurred on a host without process logging
  requires: hb_process_activity with command-line auditing
  risk: A host that installs the package but does not report process history will
    be missed by the lead query.
  stage: malicious-dependency-installation
- id: insufficient-file-logs
  question: whether the RAT accessed secrets not in the standard searched paths
  requires: hb_file_activity with activity_id = 2 (Read) auditing for sensitive directories
  risk: If the RAT targets non-standard secret locations, the behavior will be invisible.
  stage: credential-theft-and-rat-c2
coverage:
- stage: malicious-dependency-installation
  status: covered
  steps:
  - litellm-install-lead
  - litellm-inventory-check
- stage: python-startup-persistence
  status: covered
  steps:
  - pth-persistence-prevalence
- stage: credential-theft-and-rat-c2
  status: covered
  steps:
  - python-credential-theft
- reason: Belongs to another part of the 'Lessons from Cybersecurity Black Week' series.
  stage: rce-vulnerability-exposure
  status: out_of_scope
- reason: Belongs to another part of the 'Lessons from Cybersecurity Black Week' series.
  stage: memory-safety-exploitation-crashes
  status: out_of_scope
- reason: Belongs to another part of the 'Lessons from Cybersecurity Black Week' series.
  stage: compromised-certificate-trust
  status: out_of_scope
guardrails:
  claims: no_unsupported
  evidence: citation_required
  missing_data: not_benign
  telemetry: untrusted
hunt:
  applicability: campaign-specific
  handoff: promote-to-detection
  justification: The LiteLLM supply chain compromise allows for unauthenticated RCE
    and credential theft at scale. Verifying the integrity of Python environments
    is critical for preventing unauthorized cloud access.
  methodology: model-assisted
  trigger: intel-report
hypothesis: An adversary has installed a poisoned version of the LiteLLM library to
  establish persistence via Python startup files and exfiltrate host credentials.
labels:
- hunt
- attack.t1195.002
- attack.t1574.013
- attack.t1555
- credential access
- execution
- initial access
- persistence
name: LiteLLM Supply Chain Poisoning and Persistence
parameters:
  lookback_days:
    default: '14'
    description: Days of history to examine for installation and behavior.
    from:
      kind: manual
      observed: '2026-10-21'
      ref: incident-window
    type: number
  malicious_version:
    default: 1.107.0
    description: The specific compromised version of LiteLLM.
    from:
      kind: article
      observed: '2026-10-21'
      ref: https://www.welivesecurity.com/en/business-security/lessons-cybersecurity-black-week/
    type: string
  scope_hosts:
    default: []
    description: Optional list of hostnames to focus on after the scoping step.
    from:
      kind: manual
      observed: '2026-10-28'
      ref: analyst-defined
    type: list[host]
provenance:
  authors:
  - name: Huntbase hunt generation
    org: huntbase.io
  generated:
    by: huntbase-hunt-generation
    from: https://www.welivesecurity.com/en/business-security/lessons-cybersecurity-black-week/
    gates:
    - dry-run
    - lint
    model: hb_google/gemini-3-flash-preview
rationale: Focus on developers and CI/CD runners where LiteLLM is likely used. ephemeral
  build agents are high priority.
references:
- name: "ESET Research \u2014 Lessons from Cybersecurity Black Week"
  url: https://www.welivesecurity.com/en/business-security/lessons-cybersecurity-black-week/
related:
- hunt: python-startup-hijack-general
  reason: This hunt is specific to the LiteLLM incident; a broader hunt for all .pth
    hijacks is a sibling.
  relation: out-of-scope-alternative
- hunt: cybersecurity-black-week-vulnerability-trust-audit
  relation: follows
scenario:
  stages:
  - name: Vulnerable Service Exposure
    observables:
    - PHP versions 5.4 through 8.5.11
    - GitLab CE/EE versions 7.0 to 19.5
    - Node.js 24.22.0
    - Linux kernel KSMBD SMB Direct (ZDI-CAN-33549, ZDI-CAN-33774)
    - CVE-2026-109618
    - CVE-2026-109619
    - CVE-2026-110264
    - CVE-2026-111497
    slug: rce-vulnerability-exposure
    tactic: initial-access
    techniques:
    - T1190
  - name: Memory Safety Exploitation
    observables:
    - PHP out-of-memory (OOM) errors
    - Node.js Brotli decoder crashes
    - GitLab integer overflow during regex compilation
    - Application Error 1000
    - Exception code c0000005
    slug: memory-safety-exploitation-crashes
    tactic: execution
    techniques:
    - T1203
  - name: Supply Chain Poisoning
    observables:
    - litellm==1.107.0
    - 'Docker tags: v1.107.0, latest, main-latest, main-stable'
    - Malicious images pushed to official container registries
    slug: malicious-dependency-installation
    tactic: initial-access
    techniques:
    - T1195.002
  - name: Python Startup Hijack
    observables:
    - .pth files created in Python site-packages
    - Abuse of Python startup mechanisms
    slug: python-startup-persistence
    tactic: persistence
    techniques:
    - T1574.013
  - name: Credential Theft and RAT Activity
    observables:
    - Credential stealer payload
    - Remote Access Trojan (RAT) execution
    - Outbound C2 connections from Python processes
    - Access to sensitive local secret stores
    slug: credential-theft-and-rat-c2
    tactic: credential-access
    techniques:
    - T1555
    - T1071.001
  - name: Intermediate CA Trust Compromise
    observables:
    - eMudhra/emSign intermediate CAs with insecure private keys
    - certifi < 2026.10.19
    - pip < 26.2.2
    - uv < 0.13.4
    slug: compromised-certificate-trust
    tactic: initial-access
    techniques:
    - T1553.004
  summary: A barrage of critical vulnerabilities in October 2026 including PHP2Shell,
    Node2Shell, and GitLab RCE enabled unauthenticated remote code execution, while
    a supply-chain compromise of the LiteLLM package facilitated persistent credential
    theft. Attackers poisoned LiteLLM's PyPI and Docker distributions with a payload
    that hijacks Python startup via .pth files and establishes a remote access trojan
    (RAT).
series:
  index: 2
  slug: lessons-from-cybersecurity-black-week
  title: Lessons from Cybersecurity Black Week
  total: 2
severity: high
targets:
  analyst:
    name: Tier-2 analyst
    role: analyst
  endpoint:
    category: endpoint
    name: Endpoint telemetry (hb_ surfaces)
    telemetry:
    - endpoint
  hunter:
    agent: true
    name: Hunt agent
tlp: clear
type: investigation
---


# LiteLLM Supply Chain Poisoning and Persistence

This hunt targets the Cybersecurity Black Week compromise of the LiteLLM library (v1.107.0). It follows a gated flow, starting with a lead query that identifies the malicious package installation in process history to capture activity on ephemeral hosts. If the lead is positive, the hunt fans out to find rare Python persistence (.pth files) and sensitive credential access across the fleet. An agent then correlates these signals to confirm a compromise and trigger containment.

## litellm-install-lead
<!-- Detect malicious LiteLLM installation events -->
Identify hosts that attempted to install the compromised version of LiteLLM, including ephemeral hosts.

```sqlite target=endpoint role=scoping params=(malicious_version=malicious_version, lookback_days=lookback_days)
~~~yaml
expected: Rows showing pip or python commands installing the malicious version. Silence
  means no recorded installation events in the window.
reads:
- device_hostname
- process_name
- process_cmd_line
- user_name
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT device_hostname, process_name, process_cmd_line, user_name, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%pip%install%litellm%{{malicious_version}}%' OR LOWER(process_cmd_line) LIKE '%litellm-{{malicious_version}}%') AND time >= datetime('now', '-{{lookback_days}} days')
```

## assess-lead
<!-- Assess lead evidence -->
```agent target=hunter
cite: required
context:
- litellm-install-lead
max_iterations: 3
objective: Identify hosts that have LiteLLM version 1.107.0 installation activity
  in their process logs.
success_criteria: A verdict naming the affected hosts.
tools:
- endpoint
```

## gate-on-install
<!-- Gate on installation activity -->
if~: "the agent confirms at least one host has activity relating to version 1.107.0 of LiteLLM" (confidence: high, judge=hunter)
then: → deep-dive
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: no-process-telemetry)
else: → close-out

## deep-dive
<!-- Examine behavior and persistence -->
parallel:
- → pth-persistence-prevalence
- → litellm-inventory-check
- → python-credential-theft
join: → final-triage

## pth-persistence-prevalence
<!-- Rare .pth file persistence -->
Stack-count .pth file creations to find rare startup hijacks and attribute them to a process.

```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
  compare: first_seen
  window: '{{lookback_days}}d'
expected: Rare .pth files created on one or two hosts. Persistent malicious startup
  files will stand out from common library files.
prevalence:
  by: device_hostname
  key:
  - file_name
  - file_path
  rare_below: 5
reads:
- file_name
- file_path
- device_hostname
- process_name
- process_cmd_line
- time
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT file_name, file_path, process_name, process_cmd_line, COUNT(DISTINCT device_hostname) as host_count, MIN(time) as first_seen FROM hb_file_activity WHERE LOWER(file_name) LIKE '%.pth' AND (LOWER(file_path) LIKE '%site-packages%' OR LOWER(file_path) LIKE '%dist-packages%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY file_name, file_path, process_name, process_cmd_line HAVING host_count < 5
```

## litellm-inventory-check
<!-- Check current package inventory -->
Confirm if the malicious version is currently installed on any disk in the fleet.

```sqlite target=endpoint role=enrichment params=(malicious_version=malicious_version, scope_hosts=scope_hosts)
~~~yaml
expected: A snapshot of hosts with the vulnerable version currently installed.
reads:
- device_hostname
- package_name
- package_version
- install_path
silence: not_evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT device_hostname, package_name, package_version, install_path FROM hb_software_inventory WHERE LOWER(package_name) = 'litellm' AND package_version = '{{malicious_version}}' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)
```

## python-credential-theft
<!-- Python credential file access -->
Find Python processes reading sensitive local secret stores.

```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Reads of cloud or SSH credentials by a Python interpreter. Legitimate tools
  may exist, but those matching the scope hosts are high risk.
reads:
- device_hostname
- process_name
- process_cmd_line
- file_path
- time
- activity_id
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-10-29'
~~~
SELECT device_hostname, process_name, process_cmd_line, file_path, time FROM hb_file_activity WHERE activity_id = 2 AND LOWER(process_name) LIKE '%python%' AND (LOWER(file_path) LIKE '%/.aws/credentials' OR LOWER(file_path) LIKE '%/.ssh/id_%' OR LOWER(file_path) LIKE '%/config.json' OR LOWER(file_path) LIKE '%/.dockercfg' OR LOWER(file_path) LIKE '%/.npmrc') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```

## final-triage
<!-- Triage compromise and impact -->
```agent target=hunter
cite: required
context:
- assess-lead
- pth-persistence-prevalence
- litellm-inventory-check
- python-credential-theft
max_iterations: 6
objective: Determine if the compromised LiteLLM package has achieved persistence or
  attempted credential theft on the affected hosts.
success_criteria: A per-host verdict of malicious | suspicious | benign.
tools:
- endpoint
```

## route-on-verdict
<!-- Route on triage verdict -->
if~: "the triage verdict is malicious for at least one host, showing both package presence and either persistence (.pth) or credential access" (confidence: high, judge=hunter)
then: → contain-host
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: insufficient-file-logs)
else: → close-out

## contain-host
<!-- Isolate compromised host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host and initiate the supply-chain incident response protocol, specifically focusing on credential rotation.
```
→ analyst-review

## analyst-review
<!-- Analyst review -->
```manual target=analyst
Review the identified .pth files and the Python processes that read sensitive files. Confirm if LiteLLM was the primary actor and determine which cloud or SSH credentials were leaked.
```
→ close-out

## close-out
<!-- Close out -->
```manual target=analyst
Document the absence of malicious LiteLLM versions or the containment of any hits. Ensure all containers and virtual environments are updated to 1.107.1 or higher.
```
→ end

Run it

Take this hunt into your environment.

Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.

Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.

Locked pack

Sign up to unlock

Pack contents are available to Huntbase users.