Skip to content

Instantly share code, notes, and snippets.

@sreejithbnaick
Last active March 27, 2026 16:54
Show Gist options
  • Select an option

  • Save sreejithbnaick/d548b10300fe73439c482b6a9067ea61 to your computer and use it in GitHub Desktop.

Select an option

Save sreejithbnaick/d548b10300fe73439c482b6a9067ea61 to your computer and use it in GitHub Desktop.
Shell script to check litellm 1.82.7/1.82.8 malware compromise on your machine
#!/usr/bin/env bash
# =============================================================================
# check_litellm_compromise.sh
# Detects compromised litellm versions (1.82.7 & 1.82.8) across:
# - System Python / pip / pyenv / conda / pipx / uv
# - Malware persistence artifacts (sysmon backdoor, litellm_init.pth)
# - [Optional] All venvs and PyPI modules under a given workspace directory
#
# Incident: TeamPCP supply-chain attack — March 24 2026
#
# USAGE:
# ./check_litellm_compromise.sh # system check only
# ./check_litellm_compromise.sh /path/to/workspace # system + workspace scan
# ./check_litellm_compromise.sh --help
# =============================================================================
set -euo pipefail
# ── Usage / help ──────────────────────────────────────────────────────────────
usage() {
echo ""
echo " Usage: $(basename "$0") [WORKSPACE_DIR]"
echo ""
echo " Arguments:"
echo " WORKSPACE_DIR (optional) Root folder to recursively scan for"
echo " Python venvs, pip packages, and dependency files."
echo " If omitted, only the system-level check is performed."
echo ""
echo " Examples:"
echo " $(basename "$0") # system check only"
echo " $(basename "$0") /Users/sreejith/workspace"
echo " $(basename "$0") ~/projects"
echo ""
exit 0
}
[[ "${1:-}" == "--help" || "${1:-}" == "-h" ]] && usage
# ── Arguments ─────────────────────────────────────────────────────────────────
WORKSPACE_DIR="${1:-}" # empty → system-only mode
SCAN_WORKSPACE=false
if [[ -n "$WORKSPACE_DIR" ]]; then
WORKSPACE_DIR="${WORKSPACE_DIR%/}" # strip trailing slash
SCAN_WORKSPACE=true
fi
# ── Colours ──────────────────────────────────────────────────────────────────
RED='\033[0;31m'
YLW='\033[1;33m'
GRN='\033[0;32m'
CYN='\033[0;36m'
BLD='\033[1m'
DIM='\033[2m'
RST='\033[0m'
COMPROMISED_VERSIONS=("1.82.7" "1.82.8")
# ── Tracking ─────────────────────────────────────────────────────────────────
FOUND_COMPROMISED=0
FOUND_LITELLM_LOCATIONS=()
COMPROMISED_LOCATIONS=()
# ── Helpers ──────────────────────────────────────────────────────────────────
banner() {
echo ""
echo -e "${CYN}${BLD}══════════════════════════════════════════════════════${RST}"
echo -e "${CYN}${BLD} $1${RST}"
echo -e "${CYN}${BLD}══════════════════════════════════════════════════════${RST}"
}
section_note() {
echo -e "${DIM} $1${RST}"
}
is_compromised() {
local ver="$1"
for cv in "${COMPROMISED_VERSIONS[@]}"; do
[[ "$ver" == "$cv" ]] && return 0
done
return 1
}
# check_litellm_in_pip LABEL PIP_BINARY
# Queries pip for litellm, prints result, updates global tracking arrays.
check_litellm_in_pip() {
local label="$1"
local pip_cmd="$2"
# Accept both commands on PATH and absolute paths
if ! command -v "$pip_cmd" &>/dev/null 2>&1 && [[ ! -x "$pip_cmd" ]]; then
return
fi
local raw
raw=$("$pip_cmd" show litellm 2>/dev/null || true)
if [[ -z "$raw" ]]; then
echo -e " ${GRN}✔ litellm not installed${RST} ${DIM}[${label}]${RST}"
return
fi
local ver location
ver=$(echo "$raw" | awk '/^Version:/{print $2}')
location=$(echo "$raw" | awk '/^Location:/{print $2}')
FOUND_LITELLM_LOCATIONS+=("${label} → v${ver} @ ${location}")
if is_compromised "$ver"; then
FOUND_COMPROMISED=1
COMPROMISED_LOCATIONS+=("${label} → v${ver} @ ${location}")
echo -e " ${RED}${BLD}✘ COMPROMISED litellm==${ver}${RST}"
echo -e " ${RED}Env : ${label}${RST}"
echo -e " ${RED}Path : ${location}${RST}"
else
echo -e " ${YLW}⚠ litellm==${ver} installed (not a known-bad version)${RST}"
echo -e " ${DIM}Env : ${label}${RST}"
echo -e " ${DIM}Path : ${location}${RST}"
fi
}
# check_pth_artifact SITE_PACKAGES_DIR
# Looks for litellm_init.pth — the auto-exec payload dropped by v1.82.8.
check_pth_artifact() {
local site_pkgs="$1"
local pth_file="${site_pkgs}/litellm_init.pth"
if [[ -f "$pth_file" ]]; then
FOUND_COMPROMISED=1
COMPROMISED_LOCATIONS+=("Malware .pth artifact: ${pth_file}")
echo -e " ${RED}${BLD}✘ MALWARE ARTIFACT: ${pth_file}${RST}"
echo -e " ${RED} Executes on EVERY Python startup — remove immediately!${RST}"
fi
}
# scan_venv LABEL VENV_ROOT
# Given a venv root, locates its pip, runs the litellm check, and checks
# site-packages for the .pth artifact.
scan_venv() {
local label="$1"
local venv_root="$2"
# Support both Unix (bin/) and Windows (Scripts/) layouts
local pip_bin=""
for candidate in "${venv_root}/bin/pip3" "${venv_root}/bin/pip" \
"${venv_root}/Scripts/pip3.exe" "${venv_root}/Scripts/pip.exe"; do
if [[ -x "$candidate" ]]; then
pip_bin="$candidate"
break
fi
done
[[ -z "$pip_bin" ]] && return # no pip found → not a usable venv
check_litellm_in_pip "$label" "$pip_bin"
# Also scan site-packages for .pth payload
for sp in "${venv_root}"/lib/python*/site-packages; do
[[ -d "$sp" ]] && check_pth_artifact "$sp"
done
}
# ─────────────────────────────────────────────────────────────────────────────
# Header
# ─────────────────────────────────────────────────────────────────────────────
#TOTAL_SECTIONS=$(($SCAN_WORKSPACE ? 3 : 2))
TOTAL_SECTIONS=2
if [ "$SCAN_WORKSPACE" = true ]; then
TOTAL_SECTIONS=3
fi
echo ""
echo -e "${BLD}╔══════════════════════════════════════════════════════╗${RST}"
echo -e "${BLD}║ litellm Compromise Detection Script ║${RST}"
echo -e "${BLD}║ Incident: TeamPCP supply-chain — 24 Mar 2026 ║${RST}"
echo -e "${BLD}╚══════════════════════════════════════════════════════╝${RST}"
echo -e " ${BLD}Compromised versions : ${COMPROMISED_VERSIONS[*]}${RST}"
if $SCAN_WORKSPACE; then
echo -e " ${BLD}Mode : System check + workspace scan${RST}"
echo -e " ${BLD}Workspace : ${WORKSPACE_DIR}${RST}"
else
echo -e " ${BLD}Mode : System check only${RST}"
section_note "Tip: pass a directory path to also scan projects → $0 /your/workspace"
fi
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 1 · System Python / pip / pyenv / conda / pipx / uv
# ══════════════════════════════════════════════════════════════════════════════
banner "1 / ${TOTAL_SECTIONS} · System Python installations"
declare -A seen_pips # deduplicates by resolved pip path
# --- PATH-visible pythons ----------------------------------------------------
for py_cmd in python python3 python3.9 python3.10 python3.11 python3.12 python3.13; do
command -v "$py_cmd" &>/dev/null 2>&1 || continue
py_path=$(command -v "$py_cmd")
pip_path="$(dirname "$py_path")/pip3"
[[ ! -x "$pip_path" ]] && pip_path="$(dirname "$py_path")/pip"
[[ ! -x "$pip_path" ]] && continue
real_pip=$(realpath "$pip_path" 2>/dev/null || echo "$pip_path")
[[ -n "${seen_pips[$real_pip]+_}" ]] && continue
seen_pips[$real_pip]=1
site=$("$py_cmd" -c "import site; print(site.getsitepackages()[0])" 2>/dev/null || true)
echo -e "\n${BLD}${py_cmd}${pip_path}${RST}"
check_litellm_in_pip "system:${py_cmd}" "$pip_path"
[[ -n "$site" ]] && check_pth_artifact "$site"
done
# --- Homebrew Python (macOS) -------------------------------------------------
for brew_py in /opt/homebrew/bin/python3 /usr/local/bin/python3; do
[[ -x "$brew_py" ]] || continue
brew_pip="${brew_py%python3}pip3"
[[ -x "$brew_pip" ]] || continue
real_pip=$(realpath "$brew_pip" 2>/dev/null || echo "$brew_pip")
[[ -n "${seen_pips[$real_pip]+_}" ]] && continue
seen_pips[$real_pip]=1
site=$("$brew_py" -c "import site; print(site.getsitepackages()[0])" 2>/dev/null || true)
echo -e "\n${BLD}▶ Homebrew Python → ${brew_pip}${RST}"
check_litellm_in_pip "homebrew:python3" "$brew_pip"
[[ -n "$site" ]] && check_pth_artifact "$site"
done
# --- pyenv -------------------------------------------------------------------
PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}"
if [[ -d "$PYENV_ROOT/versions" ]]; then
echo -e "\n${BLD}▶ pyenv versions under ${PYENV_ROOT}/versions${RST}"
while IFS= read -r pip_bin; do
real_pip=$(realpath "$pip_bin" 2>/dev/null || echo "$pip_bin")
[[ -n "${seen_pips[$real_pip]+_}" ]] && continue
seen_pips[$real_pip]=1
py_ver=$(echo "$pip_bin" | awk -F'/' '{
for(i=1;i<=NF;i++) if($i=="versions") {print $(i+1); exit}
}')
echo -e " ${BLD}pyenv:${py_ver}${pip_bin}${RST}"
check_litellm_in_pip "pyenv:${py_ver}" "$pip_bin"
for sp in "$(dirname "$(dirname "$pip_bin")")"/lib/python*/site-packages; do
[[ -d "$sp" ]] && check_pth_artifact "$sp"
done
done < <(find "$PYENV_ROOT/versions" \( -name "pip3" -o -name "pip" \) 2>/dev/null | sort -u)
else
section_note "pyenv not found at ${PYENV_ROOT}"
fi
# --- conda / mamba -----------------------------------------------------------
if command -v conda &>/dev/null 2>&1; then
echo -e "\n${BLD}▶ conda environments${RST}"
while IFS= read -r env_path; do
[[ -z "$env_path" || "$env_path" == "#"* ]] && continue
pip_bin="${env_path}/bin/pip3"
[[ ! -x "$pip_bin" ]] && pip_bin="${env_path}/bin/pip"
[[ ! -x "$pip_bin" ]] && continue
real_pip=$(realpath "$pip_bin" 2>/dev/null || echo "$pip_bin")
[[ -n "${seen_pips[$real_pip]+_}" ]] && continue
seen_pips[$real_pip]=1
env_name=$(basename "$env_path")
echo -e " ${BLD}conda env: ${env_name}${pip_bin}${RST}"
check_litellm_in_pip "conda:${env_name}" "$pip_bin"
for sp in "${env_path}"/lib/python*/site-packages; do
[[ -d "$sp" ]] && check_pth_artifact "$sp"
done
done < <(conda env list 2>/dev/null | awk 'NF && !/^#/ {print $NF}')
fi
# --- pipx --------------------------------------------------------------------
if command -v pipx &>/dev/null 2>&1; then
echo -e "\n${BLD}▶ pipx environment${RST}"
pipx_litellm=$(pipx list 2>/dev/null | grep -i "litellm" || true)
if [[ -n "$pipx_litellm" ]]; then
FOUND_LITELLM_LOCATIONS+=("pipx → ${pipx_litellm}")
echo -e " ${YLW}litellm found in pipx:${RST} ${pipx_litellm}"
for cv in "${COMPROMISED_VERSIONS[@]}"; do
if echo "$pipx_litellm" | grep -q "$cv"; then
FOUND_COMPROMISED=1
COMPROMISED_LOCATIONS+=("pipx → ${pipx_litellm}")
echo -e " ${RED}${BLD}✘ COMPROMISED version detected in pipx!${RST}"
fi
done
else
echo -e " ${GRN}✔ litellm not found in pipx${RST}"
fi
fi
# --- uv cache (stale .pth wheels) -------------------------------------------
echo -e "\n${BLD}▶ uv cache — stale litellm_init.pth check${RST}"
UV_CACHE="${HOME}/.cache/uv"
if [[ -d "$UV_CACHE" ]]; then
pth_hits=$(find "$UV_CACHE" -name "litellm_init.pth" 2>/dev/null || true)
if [[ -n "$pth_hits" ]]; then
FOUND_COMPROMISED=1
echo -e " ${RED}${BLD}✘ litellm_init.pth in uv cache — run: uv cache clean${RST}"
while IFS= read -r f; do
echo -e " ${RED}${f}${RST}"
COMPROMISED_LOCATIONS+=("uv cache artifact: $f")
done <<< "$pth_hits"
else
echo -e " ${GRN}✔ No litellm_init.pth in uv cache${RST}"
fi
else
section_note "uv cache not found at ${UV_CACHE}"
fi
# ══════════════════════════════════════════════════════════════════════════════
# SECTION 2 (optional) · Workspace recursive scan
# ══════════════════════════════════════════════════════════════════════════════
if $SCAN_WORKSPACE; then
banner "2 / ${TOTAL_SECTIONS} · Workspace scan: ${WORKSPACE_DIR}"
if [[ ! -d "$WORKSPACE_DIR" ]]; then
echo -e " ${RED}✘ Directory not found: ${WORKSPACE_DIR}${RST}"
else
# ── 2a. Virtual environments (any depth, any name) ────────────────────────
# Strategy: locate every pyvenv.cfg — the canonical marker of any Python
# venv regardless of folder name (venv / .venv / env / myenv / .dev / etc.)
echo -e "${BLD}▶ 2a · Virtual environments (detected via pyvenv.cfg)${RST}"
section_note "Catches venv / .venv / env / custom-named envs at any folder depth"
venv_count=0
declare -A seen_venvs
while IFS= read -r pyvenv_cfg; do
venv_root=$(dirname "$pyvenv_cfg")
real_venv=$(realpath "$venv_root" 2>/dev/null || echo "$venv_root")
# Deduplicate venv roots
[[ -n "${seen_venvs[$real_venv]+_}" ]] && continue
seen_venvs[$real_venv]=1
# Skip if this pip was already seen in the system scan
pip_candidate="${venv_root}/bin/pip3"
[[ ! -x "$pip_candidate" ]] && pip_candidate="${venv_root}/bin/pip"
if [[ -x "$pip_candidate" ]]; then
real_pip=$(realpath "$pip_candidate" 2>/dev/null || echo "$pip_candidate")
[[ -n "${seen_pips[$real_pip]+_}" ]] && continue
seen_pips[$real_pip]=1
fi
venv_count=$((venv_count + 1))
rel_path="${venv_root#"$WORKSPACE_DIR"/}"
project_dir=$(dirname "$venv_root")
echo -e "\n ${BLD}[${venv_count}] ${rel_path}${RST} ${DIM}$(basename "$project_dir")${RST}"
scan_venv "workspace:${rel_path}" "$venv_root"
done < <(find "$WORKSPACE_DIR" \
-name "pyvenv.cfg" \
! -path "*/.git/*" \
! -path "*/node_modules/*" \
! -path "*/__pycache__/*" \
2>/dev/null | sort)
echo ""
echo -e " ${BLD}Total virtual environments found: ${venv_count}${RST}"
# ── 2b. Bare installs — python/pip symlinks outside any venv ─────────────
# Catches projects that install into a shared Python with no venv
# (e.g. poetry --no-venv, scripts using system pip directly).
echo ""
echo -e "${BLD}▶ 2b · Bare Python installs (pip/python outside a venv)${RST}"
section_note "Looks for pip/python binaries not inside a pyvenv.cfg-marked venv"
bare_count=0
while IFS= read -r pip_bin; do
# Skip if the parent directory chain contains a pyvenv.cfg (it IS a venv)
venv_check=$(dirname "$(dirname "$pip_bin")")
[[ -f "${venv_check}/pyvenv.cfg" ]] && continue
real_pip=$(realpath "$pip_bin" 2>/dev/null || echo "$pip_bin")
[[ -n "${seen_pips[$real_pip]+_}" ]] && continue
seen_pips[$real_pip]=1
bare_count=$((bare_count + 1))
rel_path="${pip_bin#"$WORKSPACE_DIR"/}"
echo -e " ${BLD}${rel_path}${RST}"
check_litellm_in_pip "workspace-bare:${rel_path}" "$pip_bin"
done < <(find "$WORKSPACE_DIR" \
\( -name "pip3" -o -name "pip" \) \
-type f \
! -path "*/.git/*" \
! -path "*/node_modules/*" \
2>/dev/null | sort)
[[ $bare_count -eq 0 ]] && section_note "No bare pip binaries found outside venvs"
# ── 2c. Dependency file scan ──────────────────────────────────────────────
echo ""
echo -e "${BLD}▶ 2c · Dependency files — pinned version check${RST}"
section_note "Scans: requirements*.txt, pyproject.toml, setup.cfg, setup.py,"
section_note " Pipfile, Pipfile.lock, poetry.lock, uv.lock"
dep_file_count=0
compromised_dep_hits=0
while IFS= read -r dep_file; do
dep_file_count=$((dep_file_count + 1))
rel_dep="${dep_file#"$WORKSPACE_DIR"/}"
file_flagged=false
# Check for pinned compromised version
for cv in "${COMPROMISED_VERSIONS[@]}"; do
if grep -qiE "litellm[^a-zA-Z0-9_-].*${cv}|litellm==${cv}" "$dep_file" 2>/dev/null; then
if ! $file_flagged; then
echo -e " ${RED}${BLD}✘ Compromised version pinned: ${rel_dep}${RST}"
file_flagged=true
compromised_dep_hits=$((compromised_dep_hits + 1))
fi
grep -niE "litellm[^a-zA-Z0-9_-].*${cv}|litellm==${cv}" "$dep_file" \
| while read -r match; do echo -e " ${RED}line ${match}${RST}"; done
fi
done
# Non-compromised litellm reference (informational)
if ! $file_flagged && grep -qiE "litellm" "$dep_file" 2>/dev/null; then
first_match=$(grep -iE "litellm" "$dep_file" | head -1 | sed 's/^[[:space:]]*//')
echo -e " ${YLW}⚠ litellm reference: ${rel_dep}${RST}"
echo -e " ${DIM}${first_match}${RST}"
fi
done < <(find "$WORKSPACE_DIR" \
\( -name "requirements*.txt" \
-o -name "pyproject.toml" \
-o -name "setup.cfg" \
-o -name "setup.py" \
-o -name "Pipfile" \
-o -name "Pipfile.lock" \
-o -name "poetry.lock" \
-o -name "uv.lock" \) \
! -path "*/.git/*" \
! -path "*/node_modules/*" \
! -path "*/__pycache__/*" \
2>/dev/null | sort)
echo ""
echo -e " ${DIM}Dependency files scanned: ${dep_file_count}${RST}"
if [[ $compromised_dep_hits -eq 0 ]]; then
echo -e " ${GRN}✔ No pinned compromised versions in dependency files${RST}"
else
echo -e " ${RED}${BLD}${compromised_dep_hits} file(s) pin a compromised version${RST}"
fi
fi # end: workspace dir exists
fi # end: SCAN_WORKSPACE
# ══════════════════════════════════════════════════════════════════════════════
# SECTION (last) · Malware persistence artifacts
# ══════════════════════════════════════════════════════════════════════════════
banner "${TOTAL_SECTIONS} / ${TOTAL_SECTIONS} · Malware persistence artifacts"
# --- sysmon backdoor ---------------------------------------------------------
echo -e "${BLD}▶ sysmon backdoor files${RST}"
SYSMON_PY="${HOME}/.config/sysmon/sysmon.py"
SYSMON_SERVICE="${HOME}/.config/systemd/user/sysmon.service"
sysmon_clean=true
if [[ -f "$SYSMON_PY" ]]; then
sysmon_clean=false
FOUND_COMPROMISED=1
COMPROMISED_LOCATIONS+=("Backdoor script: ${SYSMON_PY}")
echo -e " ${RED}${BLD}✘ BACKDOOR FOUND: ${SYSMON_PY}${RST}"
echo -e " ${RED} Rotate ALL credentials, then: rm -rf ~/.config/sysmon${RST}"
fi
if [[ -f "$SYSMON_SERVICE" ]]; then
sysmon_clean=false
FOUND_COMPROMISED=1
COMPROMISED_LOCATIONS+=("Backdoor service: ${SYSMON_SERVICE}")
echo -e " ${RED}${BLD}✘ SYSTEMD PERSISTENCE: ${SYSMON_SERVICE}${RST}"
echo -e " ${RED} Disable with: systemctl --user disable --now sysmon${RST}"
fi
$sysmon_clean && echo -e " ${GRN}✔ No sysmon backdoor found${RST}"
# --- litellm_init.pth (global filesystem) ------------------------------------
echo ""
echo -e "${BLD}▶ litellm_init.pth global search${RST}"
search_roots=("/usr" "$HOME")
[[ -d "/opt/homebrew" ]] && search_roots+=("/opt/homebrew")
[[ -d "/usr/local" ]] && search_roots+=("/usr/local")
$SCAN_WORKSPACE && [[ -d "$WORKSPACE_DIR" ]] && search_roots+=("$WORKSPACE_DIR")
pth_hits_global=$(find "${search_roots[@]}" \
-name "litellm_init.pth" \
! -path "*/.Trash/*" \
2>/dev/null | sort || true)
if [[ -n "$pth_hits_global" ]]; then
FOUND_COMPROMISED=1
echo -e " ${RED}${BLD}✘ litellm_init.pth detected (auto-exec payload):${RST}"
while IFS= read -r f; do
echo -e " ${RED}${f}${RST}"
COMPROMISED_LOCATIONS+=("Malware .pth: $f")
done <<< "$pth_hits_global"
else
echo -e " ${GRN}✔ No litellm_init.pth found${RST}"
fi
# --- Active C2 connection ----------------------------------------------------
echo ""
echo -e "${BLD}▶ Live connection to C2 (models.litellm.cloud)${RST}"
c2_hits=""
if command -v lsof &>/dev/null 2>&1; then
c2_hits=$(lsof -i -n -P 2>/dev/null | grep -i "litellm.cloud" || true)
elif command -v ss &>/dev/null 2>&1; then
c2_hits=$(ss -tnp 2>/dev/null | grep -i "litellm.cloud" || true)
else
echo -e " ${YLW}⚠ Neither lsof nor ss available — skipping live connection check${RST}"
fi
if [[ -n "$c2_hits" ]]; then
FOUND_COMPROMISED=1
COMPROMISED_LOCATIONS+=("Active C2 connection detected")
echo -e " ${RED}${BLD}✘ ACTIVE C2 CONNECTION:${RST}"
while IFS= read -r line; do echo -e " ${RED}${line}${RST}"; done <<< "$c2_hits"
elif [[ -n "$c2_hits+x" ]]; then
echo -e " ${GRN}✔ No active connection to models.litellm.cloud${RST}"
fi
# ══════════════════════════════════════════════════════════════════════════════
# FINAL SUMMARY
# ══════════════════════════════════════════════════════════════════════════════
banner "SUMMARY REPORT"
if [[ ${#FOUND_LITELLM_LOCATIONS[@]} -eq 0 ]]; then
echo -e "${GRN}${BLD}✔ litellm is NOT installed anywhere checked.${RST}"
else
echo -e "${BLD}All litellm installations found (${#FOUND_LITELLM_LOCATIONS[@]}):${RST}"
for loc in "${FOUND_LITELLM_LOCATIONS[@]}"; do
echo -e "${loc}"
done
fi
echo ""
if [[ $FOUND_COMPROMISED -eq 1 ]]; then
echo -e "${RED}${BLD}╔══════════════════════════════════════════════════════╗${RST}"
echo -e "${RED}${BLD}║ ⚠ COMPROMISE DETECTED — IMMEDIATE ACTION REQUIRED ║${RST}"
echo -e "${RED}${BLD}╚══════════════════════════════════════════════════════╝${RST}"
echo ""
echo -e "${RED}${BLD}Affected items:${RST}"
for loc in "${COMPROMISED_LOCATIONS[@]}"; do
echo -e " ${RED}${loc}${RST}"
done
echo ""
echo -e "${BLD}REMEDIATION STEPS:${RST}"
echo -e " 1. In EVERY affected environment:"
echo -e " ${RED}pip uninstall litellm -y${RST}"
echo -e " ${RED}pip install 'litellm>=1.82.9'${RST} (first clean version)"
echo -e " 2. Purge package caches:"
echo -e " ${RED}pip cache purge${RST} && ${RED}uv cache clean${RST}"
echo -e " 3. Remove malware files (if found):"
echo -e " ${RED}rm -rf ~/.config/sysmon${RST}"
echo -e " ${RED}systemctl --user disable --now sysmon${RST} (Linux)"
echo -e " 4. ${BLD}ROTATE ALL CREDENTIALS IMMEDIATELY:${RST}"
echo -e " • AWS / GCP / Azure / cloud provider keys"
echo -e " • SSH private keys (~/.ssh/id_*)"
echo -e " • API keys in .env / config files"
echo -e " • GitHub / GitLab tokens"
echo -e " • Crypto wallet seed phrases"
echo -e " • Kubernetes service-account tokens"
echo -e " 5. Audit router/firewall logs for POSTs to models.litellm.cloud"
echo -e " 6. Kubernetes: inspect kube-system for pods matching 'node-setup-*'"
echo ""
echo -e " ${YLW}⚠ Do NOT simply upgrade litellm — the payload may already have run.${RST}"
echo -e " ${YLW} Consider rebuilding affected systems from a known-clean snapshot.${RST}"
else
echo -e "${GRN}${BLD}╔══════════════════════════════════════════════════╗${RST}"
echo -e "${GRN}${BLD}║ ✔ No compromised litellm versions detected ║${RST}"
echo -e "${GRN}${BLD}╚══════════════════════════════════════════════════╝${RST}"
echo ""
echo -e " ${DIM}Checked versions : ${COMPROMISED_VERSIONS[*]}${RST}"
echo -e " ${DIM}Checked : system Python, Homebrew, pyenv, conda, pipx, uv cache${RST}"
if $SCAN_WORKSPACE; then
echo -e " ${DIM} all venvs + bare installs + dep files under:${RST}"
echo -e " ${DIM} ${WORKSPACE_DIR}${RST}"
fi
echo -e " ${DIM}Artifacts checked : litellm_init.pth, sysmon backdoor, C2 connections${RST}"
fi
echo ""
echo -e "${CYN}${DIM}Incident ref : https://docs.litellm.ai/blog/security-update-march-2026${RST}"
echo -e "${CYN}${DIM}SNYK ID : SNYK-PYTHON-LITELLM-15762713${RST}"
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment