Skip to content

Instantly share code, notes, and snippets.

@AnmolTomer
Created March 25, 2026 09:41
Show Gist options
  • Select an option

  • Save AnmolTomer/4450a64fc96bff15dcb1249f13d47649 to your computer and use it in GitHub Desktop.

Select an option

Save AnmolTomer/4450a64fc96bff15dcb1249f13d47649 to your computer and use it in GitHub Desktop.
detection script that checks all of the following in one pass: installed litellm versions (pip, poetry, uv), the malicious `.pth` file in site-packages and uv cache, TeamPCP persistence artifacts, network IOCs in logs, Kubernetes lateral movement pods, and unpinned litellm in your local dependency files.
#!/usr/bin/env bash
# detect-litellm-compromise.sh
# Checks whether your system was affected by the LiteLLM supply chain attack (March 24, 2026).
# Run this on any machine where Python packages may have been installed during the exposure window.
#
# Usage: bash detect-litellm-compromise.sh
# No arguments, no dependencies beyond Python 3 and standard unix tools.
set -euo pipefail
RED='\033[0;31m'
YELLOW='\033[0;33m'
GREEN='\033[0;32m'
NC='\033[0m'
found_issues=0
section() { printf "\n${YELLOW}[%s]${NC} %s\n" "$1" "$2"; }
critical() { printf " ${RED}CRITICAL:${NC} %s\n" "$1"; found_issues=1; }
ok() { printf " ${GREEN}OK:${NC} %s\n" "$1"; }
warn() { printf " ${YELLOW}WARN:${NC} %s\n" "$1"; }
# --- Step 1: Check installed litellm version ---
section "1/6" "Checking installed litellm versions"
check_version() {
local ver="$1" src="$2"
case "$ver" in
1.82.7|1.82.8) critical "litellm==$ver found via $src — system is COMPROMISED" ;;
"") ;;
*) ok "litellm==$ver ($src) — not a compromised version" ;;
esac
}
if command -v pip &>/dev/null; then
ver=$(pip show litellm 2>/dev/null | awk '/^Version:/{print $2}')
check_version "${ver:-}" "pip"
[ -z "$ver" ] && ok "litellm not installed in current pip environment"
fi
if command -v poetry &>/dev/null && [ -f poetry.lock ]; then
ver=$(poetry show litellm 2>/dev/null | awk '/version/{print $3; exit}')
check_version "${ver:-}" "poetry"
fi
if command -v uv &>/dev/null; then
ver=$(uv pip freeze 2>/dev/null | grep -i '^litellm==' | cut -d= -f3)
check_version "${ver:-}" "uv"
fi
# --- Step 2: Search for malicious .pth file ---
section "2/6" "Searching for litellm_init.pth"
pth_found=0
if command -v python3 &>/dev/null; then
while IFS= read -r line; do
if [ -n "$line" ]; then
critical "$line"
pth_found=1
fi
done < <(python3 -c "
import site, pathlib
paths = set(site.getsitepackages() + [site.getusersitepackages()])
for p in paths:
f = pathlib.Path(p) / 'litellm_init.pth'
if f.exists():
print(f)
" 2>/dev/null || true)
fi
# Check uv cache
if [ -d "$HOME/.cache/uv" ]; then
while IFS= read -r f; do
critical "Found in uv cache: $f"
pth_found=1
done < <(find "$HOME/.cache/uv" -name "litellm_init.pth" 2>/dev/null)
fi
[ "$pth_found" -eq 0 ] && ok "litellm_init.pth not found"
# --- Step 3: Check for persistence artifacts ---
section "3/6" "Checking for TeamPCP persistence artifacts"
persistence_found=0
for f in \
"$HOME/.config/sysmon/sysmon.py" \
"$HOME/.config/systemd/user/sysmon.service" \
"/root/.config/sysmon/sysmon.py" \
"/tmp/pglog" \
"/tmp/.pg_state"; do
if [ -e "$f" ]; then
critical "Found: $f"
persistence_found=1
fi
done
if systemctl --user is-active sysmon.service &>/dev/null; then
critical "sysmon.service is running"
persistence_found=1
fi
[ "$persistence_found" -eq 0 ] && ok "No persistence artifacts found"
# --- Step 4: Check network IOCs in DNS/hosts ---
section "4/6" "Checking for network indicators"
printf " Check your DNS, proxy, and egress logs for:\n"
printf " - models.litellm.cloud (exfiltration endpoint)\n"
printf " - checkmarx.zone (C2 / payload delivery)\n"
for domain in models.litellm.cloud checkmarx.zone; do
if grep -qr "$domain" /var/log/ 2>/dev/null; then
critical "Found $domain in /var/log/"
fi
done
# --- Step 5: Check Kubernetes (if kubectl available) ---
section "5/6" "Checking Kubernetes (if applicable)"
if command -v kubectl &>/dev/null && kubectl cluster-info &>/dev/null 2>&1; then
if kubectl get pods -n kube-system 2>/dev/null | grep -q node-setup; then
critical "Found node-setup pods in kube-system"
else
ok "No node-setup pods in kube-system"
fi
priv_alpine=$(kubectl get pods -n kube-system -o json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
count = 0
for pod in data.get('items', []):
for c in pod['spec'].get('containers', []):
if 'alpine' in c.get('image', ''):
sc = c.get('securityContext', {})
if sc.get('privileged'):
ns = pod['metadata']['namespace']
name = pod['metadata']['name']
print(f'{ns}/{name} image={c[\"image\"]} privileged=true')
count += 1
print(f'__count={count}', file=sys.stderr)
" 2>&1)
if echo "$priv_alpine" | grep -q "privileged=true"; then
while IFS= read -r line; do
[ -n "$line" ] && critical "Privileged alpine pod: $line"
done <<< "$priv_alpine"
else
ok "No privileged alpine pods found"
fi
else
ok "kubectl not available or no cluster connection — skipping"
fi
# --- Step 6: Scan local dependency files ---
section "6/6" "Scanning local dependency files for unpinned litellm"
if [ -d "." ]; then
while IFS= read -r f; do
if grep -qiE 'litellm[^=]|litellm>=' "$f" 2>/dev/null; then
warn "Unpinned or loosely pinned litellm in: $f"
fi
done < <(find . \( \
-name 'requirements*.txt' \
-o -name 'pyproject.toml' \
-o -name 'Pipfile' \
-o -name 'Dockerfile*' \
\) -not -path '*/node_modules/*' -not -path '*/.git/*' 2>/dev/null)
fi
# --- Summary ---
printf "\n"
if [ "$found_issues" -eq 1 ]; then
printf "${RED}=== ISSUES FOUND ===${NC}\n"
printf "Review the CRITICAL items above. If litellm 1.82.7 or 1.82.8 was installed,\n"
printf "treat this as a full security incident. See the incident response guide:\n"
printf "<link-to-blog>\n"
else
printf "${GREEN}=== NO ISSUES FOUND ===${NC}\n"
printf "No indicators of compromise detected on this system.\n"
fi
exit $found_issues
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment