CognitiveSand/scan-supply-chain
GitHub: CognitiveSand/scan-supply-chain
一款数据驱动的 PyPI/npm 供应链攻击 IOC 扫描器,通过 TOML 威胁特征定义实现零代码扩展,帮助安全团队在投毒事件发生后快速排查系统受影响程度并输出分级修复建议。
Stars: 1 | Forks: 1
# 供应链妥协扫描器
一个数据驱动的扫描器,用于检测已知 **PyPI 和 npm 供应链攻击** 中的威胁指标 (IOC)。威胁特征通过 TOML 文件定义,并且支持用户自定义扩展——无需编写代码即可添加您自己的特征。
**内置威胁特征:**
| ID | 包名 | 生态系统 | 受感染版本 | 日期 |
|----|---------|-----------|---------------------|------|
| `litellm-2026-03` | litellm | PyPI | 1.82.7, 1.82.8 | 2026-03-24 |
| `axios-2026-03` | axios | npm | 1.14.1, 0.30.4 | 2026-03-31 |
## 快速入门
### 选项 A:从 PyPI 安装
**Linux / macOS:**
```
# 推荐 — 安装在隔离环境中
pipx install scan-supply-chain
scan-supply-chain
```
如果您没有 pipx:`sudo apt install pipx` (Debian/Ubuntu) 或 `brew install pipx` (macOS)。
或者,在虚拟环境中使用 pip:
```
python3 -m venv /tmp/scanner && /tmp/scanner/bin/pip install scan-supply-chain
/tmp/scanner/bin/scan-supply-chain
```
**Windows:**
```
pip install scan-supply-chain
scan-supply-chain
```
或者使用虚拟环境:
```
py -m venv %TEMP%\scanner && %TEMP%\scanner\Scripts\pip install scan-supply-chain
%TEMP%\scanner\Scripts\scan-supply-chain
```
### 选项 B:从源码运行(无需安装)
**Linux / macOS:**
```
git clone https://github.com/CognitiveSand/scan-supply-chain.git
cd scan-supply-chain
python3 run_scan.py
```
**Windows** — 双击 **`run_scan.bat`**,或者在终端中运行:
```
git clone https://github.com/CognitiveSand/scan-supply-chain.git
cd scan-supply-chain
py run_scan.py
```
无需任何依赖 —— 仅使用 Python 3.11+ 标准库。
### 命令行选项
| 标志 | 描述 |
|------|-------------|
| *(无标志)* | **扫描所有已知威胁**(默认行为)。 |
| `--threat ID` | 仅扫描特定威胁(例如 `--threat litellm-2026-03`)。 |
| `--threat-file PATH` | 从 TOML 文件加载自定义威胁特征。 |
| `--list-threats` | 列出所有可用的威胁特征并退出。 |
| `--resolve-c2` | 启用对 C2 域名的实时 DNS 查询(默认:仅使用已知 IP)。 |
| `--help` | 显示用法信息。 |
**示例:**
```
# 扫描所有已知威胁(默认)
scan-supply-chain
# 仅扫描特定威胁
scan-supply-chain --threat axios-2026-03
# 列出可用的 threat profiles
scan-supply-chain --list-threats
# 使用自定义 threat profile
scan-supply-chain --threat-file ./my-threat.toml
```
## 威胁库
威胁特征是 TOML 文件,定义了特定供应链攻击的所有信息:包名、生态系统、受感染版本、C2 基础设施、IOC 文件路径以及修复步骤。
### 内置威胁
随包一起安装在 `scan_supply_chain/threats/` 目录中。可通过 `pip install --upgrade` 进行更新。
### 用户定义的威胁
将 `.toml` 文件放入以下目录:
- **Linux/macOS:** `~/.config/scan-supply-chain/threats/`
- **Windows:** `%LOCALAPPDATA%\scan-supply-chain\threats\`
用户特征会覆盖具有相同 `id` 的内置特征。
### 编写威胁特征
```
[threat]
id = "mypackage-2026-04"
name = "MyPackage Supply Chain Attack"
date = "2026-04-01"
ecosystem = "pypi" # "pypi" or "npm"
package = "mypackage"
compromised = ["1.0.1"]
safe = "1.0.0"
advisory = "https://example.com/advisory"
description = "Description of what happened."
[c2]
domains = ["evil.example.com"]
ports = []
[c2.ips]
"evil.example.com" = ["1.2.3.4"]
[[ioc.known_paths]]
description = "backdoor payload"
linux = ["/tmp/backdoor.py"]
darwin = ["/tmp/backdoor.py"]
windows = ['%TEMP%\backdoor.exe']
[ioc.phantom_deps]
names = ["malicious-dep"] # should NEVER exist
[ioc.kubernetes]
pod_patterns = []
namespace = ""
[ioc.windows]
registry_keywords = []
schtask_keywords = []
[remediation]
rotate_secrets = true
install_command = "pip install mypackage==1.0.0"
[remediation.remove_artifacts]
linux = ["Remove /tmp/backdoor.py"]
darwin = ["Remove /tmp/backdoor.py"]
windows = ['Remove %TEMP%\backdoor.exe']
[remediation.check_persistence]
linux = ["Check crontab -l"]
darwin = ["Check launchctl list"]
windows = ["Check Task Scheduler"]
```
## 视频概述
此扫描器的灵感来源于 [Fahd Mirza 的视频](https://www.youtube.com/watch?v=YoClPk7KqZc),该视频重点介绍了 LiteLLM 事件——感谢他引起人们的关注。
## 已知攻击
### LiteLLM PyPI 妥协事件(2026 年 3 月 24 日)
两个植入了后门的 `litellm` Python 包版本被发布到 PyPI:
- **v1.82.7** — 恶意代码被注入到 `proxy_server.py` 中(Base64 编码)
- **v1.82.8** — 添加了一个 `litellm_init.pth` 文件,该文件在每次 Python 解释器启动时执行
在 PyPI 对其进行隔离之前,这些版本可供下载了约 3 小时。LiteLLM 每月的下载量约为 9500 万次。攻击者通过污染 `aquasecurity/trivy-action` GitHub Action 窃取了 BerriAI 的 PyPI token。
**三阶段载荷:** 凭证窃取、通过 AES-256+RSA-4096 加密向 C2 渗透数据,以及每 50 分钟轮询一次的持久性后门。如果发现 K8s 凭证,则会部署特权 `node-setup-*` Pod 以进行横向移动。
### Axios npm 妥协事件(2026 年 3 月 31 日)
`axios` npm 包(每周下载量达 1 亿次)通过维护者账户被盗而遭到入侵,该事件归因于朝鲜的 UNC1069/BlueNoroff:
- **v1.14.1**(标记为 `latest`)和 **v0.30.4**(标记为 `legacy`)
- 注入了幽灵依赖 `plain-crypto-js@4.2.1`,该依赖带有 postinstall RAT 投递器
- 跨平台 RAT 载荷:PowerShell (Windows)、编译后的二进制文件、Python 脚本
- 具有双层混淆能力的自删除投递器
可供下载了约 3 小时。C2 地址为 `sfrclak.com:8000`。
## 此扫描器能检测什么
扫描器为每个威胁特征运行一个 **5 阶段流水线**。每个阶段都在本文档末尾的 [工作原理](#how-it-works) 部分进行了详细说明。简而言之:
| 阶段 | 目的 |
|-------|---------|
| 1 — 发现 | 在整个文件系统中查找该包的每一次安装 |
| 2 — 版本检查 | 读取元数据以识别受感染的版本 |
| 3 — 证据收集 | 搜索 IOC 产物、C2 连接、持久性机制、缓存和安装历史 |
| 4 — 源码与配置扫描 | 查找导入了该包的源文件以及固定了其版本的依赖配置 |
| 5 — 结论与修复 | 计算置信度等级并输出可操作的后续步骤 |
### 证据评分
扫描结果不是简单的“干净/被感染”二元结论,而是分为四个置信度等级进行评分:
| 等级 | 含义 | 触发条件 |
|------|---------|----------|
| **严重 (CRITICAL)** | 存在活跃妥协并与 C2 通信 | 受感染版本 + 活跃 C2 连接 |
| **高危 (HIGH)** | 存在强烈的妥协指标 | 受感染版本 + IOC 文件或幽灵依赖 |
| **中危 (MEDIUM)** | 很可能被感染 | 仅存在受感染版本,或持久性产物 |
| **低危 (LOW)** | 存在间接证据 | 仅存在源码引用、缓存痕迹或安装历史 |
## 局限性
- **无法检测已被窃取的秘密。** 无论扫描结果如何,都必须进行凭证轮换。
- **相关产物可能已被清理。** 未发现 IOC 文件并不能证明系统从未被感染过。
- **不检查 Docker 镜像层、CI/CD 运行器缓存或远程系统。**
- **排除了 Root 用户拥有的路径(Linux 上的 `/root`)。**
- **Windows 注册表扫描属于尽力而为的操作。**
- **设计上采用广泛的文件系统遍历。** 扫描器会遍历系统级的搜索根目录(例如 `/home`、`/opt`、`/usr`)以最大化检测覆盖率。快速路径过滤器会在逐行扫描之前跳过不包含该包名的文件,但在拥有庞大文件系统的机器上,初始遍历仍可能较慢。这对于事件响应是可以接受的;它不适用于持续的集群监控。
## 平台支持
| 功能 | Linux | macOS | Windows 10/11 |
|---------|-------|-------|---------------|
| 包检测 | `/home`, `/opt`, `/usr`, `/srv`, `/var` | `/Users`, `/opt/homebrew`, `/usr/local`, `/Library` | `%USERPROFILE%`, `%APPDATA%`, `Program Files` |
| Conda/pipx/nvm 检测 | `/opt/conda`, `~/.local/share/pipx` | Homebrew Caskroom, `~/.local/share/pipx` | `%LOCALAPPDATA%\Miniconda3`, `%LOCALAPPDATA%\pipx` |
| 网络 (C2 检测) | 包含 PID 的 `ss -tnp` + `/proc` 信息补充 | 包含 PID 的 `lsof -i -P -n` | `netstat -ano` |
| 持久性扫描 | crontab, shell rc, systemd user, XDG autostart, `/tmp` | crontab, shell rc, LaunchAgents | 注册表 Run 键, 计划任务 |
| 缓存扫描 | `~/.cache/pip`, `~/.npm/_cacache`, pnpm store | 相同 | `%LOCALAPPDATA%\pip\Cache` |
| 历史记录扫描 | `.bash_history`, `.zsh_history` | 相同 | 相同 |
| Python 检测 | 基于 AST(不会因字符串字面量产生误报) | 相同 | 相同 |
| ANSI 终端颜色 | 原生支持 | 原生支持 | 通过 Virtual Terminal Processing 自动启用 |
## 如果检测到妥协
**假定受影响机器上的所有秘密均已泄露。** 扫描器会提供针对特定威胁的修复步骤。一般性指南如下:
1. **立即轮换凭证** — SSH 密钥、云凭证、API 密钥、数据库密码、CI/CD token
2. **移除恶意产物** — 请参阅扫描器输出的具体路径
3. **修复受影响的包** — 安装扫描器指示的安全版本
4. **更新固定版本** — 检查所有依赖文件中是否有受感染版本的固定记录
5. **封堵 C2 域名** — 在 DNS/防火墙层面进行拦截
6. **审计云提供商日志** — 检查是否有未经授权的 API 调用
## 安全通告与参考
### LiteLLM
| ID | 来源 |
|----|--------|
| PYSEC-2026-2 | [Python Packaging Authority (PyPA)](https://github.com/pypa/advisory-database/blob/main/vulns/litellm/PYSEC-2026-2.yaml) |
| SNYK-PYTHON-LITELLM-15762713 | [Snyk](https://security.snyk.io/vuln/SNYK-PYTHON-LITELLM-15762713) |
| GitHub Issue #24512 | [首次披露](https://github.com/BerriAI/litellm/issues/24512) |
**研究:** [Endor Labs](https://www.endorlabs.com/learn/teampcp-isnt-done), [Datadog Security Labs](https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/), [Snyk](https://snyk.io/articles/poisoned-security-scanner-backdooring-litellm/), [Sonatype](https://www.sonatype.com/blog/compromised-litellm-pypi-package-delivers-multi-stage-credential-stealer), [StepSecurity](https://www.stepsecurity.io/blog/trivy-compromised-a-second-time---malicious-v0-69-4-release)
### Axios
| ID | 来源 |
|----|--------|
| GHSA-fw8c-xr5c-95f9 | [GitHub Advisory](https://github.com/advisories/GHSA-fw8c-xr5c-95f9) |
| MAL-2026-2306 | 恶意包标识符 |
**研究:** [Snyk](https://snyk.io/blog/axios-npm-package-compromised-supply-chain-attack-delivers-cross-platform/), [Socket](https://socket.dev/blog/axios-npm-package-compromised), [Huntress](https://www.huntress.com/blog/supply-chain-compromise-axios-npm-package), [Datadog Security Labs](https://securitylabs.datadoghq.com/articles/axios-npm-supply-chain-compromise/), [Wiz](https://www.wiz.io/blog/axios-npm-compromised-in-supply-chain-attack)
### 已知 IOC 哈希值 (SHA-256)
| 文件 | SHA-256 |
|------|---------|
| `litellm_init.pth` (v1.82.8) | `71e35aef03099cd1f2d6446734273025a163597de93912df321ef118bf135238` |
| `proxy_server.py` (v1.82.7) | `a0d229be8efcb2f9135e2ad55ba275b76ddcfeb55fa4370e0a522a5bdee0120b` |
| `sysmon.py` (dropped backdoor) | `6cf223aea68b0e8031ff68251e30b6017a0513fe152e235c26f248ba1e15c92a` |
## 项目结构
```
scan_supply_chain/
threats/ Threat profile TOML files (user-extensible)
litellm-2026-03.toml LiteLLM PyPI compromise
axios-2026-03.toml Axios npm compromise
threat_profile.py ThreatProfile dataclass + TOML loader
ecosystem_base.py EcosystemPlugin protocol + factory
ecosystem_pypi.py PyPI: dist-info, METADATA, Python patterns
ecosystem_npm.py npm: node_modules, package.json, JS/TS patterns
scanner.py Orchestrator (multi-threat CLI)
config.py Generic constants (skip dirs, file helpers)
models.py Data structures + Confidence/Finding enums
scoring.py Evidence scoring (findings → confidence tier)
formatting.py Terminal output (ANSI with Windows support)
platform_policy.py Platform abstraction (Strategy pattern)
platform_linux.py Linux paths and commands
platform_darwin.py macOS paths and commands
platform_windows.py Windows paths and commands
search_roots.py Deduplicated search root computation
discovery.py Phase 1 — find package metadata
version_checker.py Phase 2 — read package version
ioc_scanner.py Phase 3 — IOC orchestrator
ioc_windows.py Windows-only IOC checks (Registry, Tasks)
network_scanner.py Structured ss/lsof parsing with PID correlation
persistence_scanner.py Crontab, shell rc, systemd, LaunchAgents
cache_scanner.py pip/npm/pnpm cache scanning
history_scanner.py Shell history for install commands
ast_scanner.py AST-based Python import detection
subprocess_utils.py Safe subprocess execution helper
source_scanner.py Phase 4 — source/config file scanning
report.py Phase 5 — summary with confidence tiers
tests/ pytest test suite (355 tests)
run_scan.py Direct entry point
run_scan.bat Double-click launcher for Windows
```
## 工作原理
本节解释了扫描器在每个步骤中执行的操作、执行原因以及设计上的权衡。它面向评估该工具的安全工程师、贡献者以及任何希望了解在运行该工具时其机器上发生了什么的人。
### 概述
扫描器围绕两个抽象概念进行组织:
- **威胁特征** — 描述特定供应链攻击的 TOML 文件:哪个包、哪些版本受损、攻击者使用的 C2 基础设施、载荷释放的 IOC 文件以及应采取的修复步骤。扫描器内置了两个特征(LiteLLM,Axios)并支持用户自定义特征。
- **生态系统插件** — 了解各个包管理器如何在磁盘上存储元数据的 Python 类。PyPI 插件了解 `dist-info`/`egg-info` 目录和 `METADATA` 文件。npm 插件了解 `node_modules/*/package.json`。每个插件还提供用于检测源文件和配置文件中导入和依赖关系的正则表达式模式。
对于每个威胁特征,扫描器都会运行一个 5 阶段的流水线。每个阶段都会将数据输入到一个共享的 `ScanResults` 对象中,该对象用于累积安装信息、IOC、源码引用、配置引用和分类后的发现。最后,会对结果进行评分并输出最终结论。
### 流水线之前:搜索根目录计算
在任何扫描开始之前,扫描器会构建一个 **去重后的文件系统根目录** 以进行遍历。每个生态系统只进行一次此操作,并且所有阶段都共享相同的根目录列表。
根目录的来源包括:
1. **平台策略** — 包可能被安装的特定于操作系统的顶级目录集。在 Linux 上:`/home`、`/opt`、`/usr`、`/srv`、`/var`。在 macOS 上:`/Users`、`/opt/homebrew`、`/usr/local`、`/Library`。在 Windows 上:`%USERPROFILE%`、`%APPDATA%`、`Program Files` 等。
2. **Conda/pipx/nvm 目录** — 隔离环境所在的 `$HOME` 下的已知位置(`~/miniconda3`、`~/.local/share/pipx`、`~/.nvm` 等),如果它们存在则会被添加。
3. **生态系统额外目录** — npm 插件会添加全局 `node_modules` 目录(通过 `npm root -g` 找到);PyPI 插件不添加额外目录。
4. **`$HOME` 本身** — 始终包含在内,以便源代码/配置扫描能覆盖用户的个人项目目录。
在收集了所有候选路径之后,列表会 **通过包含关系进行去重**:如果 `/home` 已经是一个根目录,那么 `/home/me` 就会被丢弃,因为它本来就会被遍历到。这可以防止重复扫描。
**为什么这样工作:** 供应链攻击会将包安装到虚拟环境、conda 环境、nvm 前缀和全局 site-packages 中。没有哪个单一目录能涵盖所有情况。遍历广泛的根目录集是捕获异常位置(例如 `/srv` 下 CI 运行器的自定义 venv)安装的唯一方法。去重操作使这种做法变得切实可行。
### 阶段 1:发现
**目标:** 找到文件系统上目标包的每一次安装。
扫描器使用带有 **目录修剪** 的 `os.walk()` 遍历每一个搜索根目录——它会跳过 `__pycache__`、`.git`、`.tox`、`dist`、`build` 和其他从不包含包元数据的目录。对于遇到的每一个目录名:
- **PyPI:** 检查目录名是否匹配模式 `{package}-*.dist-info` 或 `{package}-*.egg-info`(不区分大小写,根据 PEP 503 标准化规则,`-` 和 `_` 被视为等效)。
- **npm:** 检查当前目录是否为 `node_modules`,如果是,则查找 `{package}/package.json` 子目录。
每次匹配都会被记录为一个元数据目录路径。解析为相同真实路径(通过符号链接)的重复项将被移除。
**为什么这样工作:** 包管理器不会维护一个关于“什么安装在哪里”的集中式注册表。查找所有安装的唯一可靠方法就是遍历文件系统。修剪使其保持快速——仅跳过 `__pycache__` 和 `.git` 就能消除目录树中的很大一部分。模式匹配故意设计得较为宽松(不区分大小写,破折号/下划线等效),因为包管理器对名称的规范化处理并不一致。
### 阶段 2:版本检查
**目标:** 确定每次被发现的安装的版本,并标记出受感染的版本。
对于在阶段 1 中找到的每个元数据目录:
- **PyPI:** 读取 `METADATA` 文件(如果是 egg-info 则读取 `PKG-INFO`)并提取 `Version:` 头信息。
- **npm:** 读取 `package.json` 并提取 `"version"` 字段。
每次安装都会与其路径和版本一起被记录。如果版本与威胁特征中列出的受感染版本之一匹配,则会对其进行标记。输出会为每个安装显示 `! COMPROMISED` 或 `+ clean`。
**为什么这样工作:** 版本检查是最高信号量的指标。如果您安装了 `litellm==1.82.7`,您几乎肯定受到了影响。扫描器执行的其他所有操作都是为了收集支持性证据——这是核心问题。
### 阶段 3:证据收集
**目标:** 搜索表明存在活跃或过去妥协迹象的产物、网络活动和持久性机制。
阶段 3 运行多个独立的子扫描器。每个扫描器都将其发现附加到共享的 `ScanResults` 中。它们按顺序运行,但在逻辑上是独立的。
#### IOC 文件遍历
遍历所有搜索根目录,查找威胁特征中定义的特定文件名。例如,LiteLLM 特征会搜索 `litellm_init.pth`——即 v1.82.8 释放在 `site-packages/` 中的自动执行后门文件。当特征中提供了 SHA-256 哈希值时,找到的文件会与之进行比对验证——一个名为 `litellm_init.pth` 但哈希值不同的文件不会被标记(它可能是不同项目中的合法文件)。当文件无法读取(权限被拒绝)时,它仍会被报告为可疑。
**原因:** IOC 文件是载荷被释放的最直接证据。哈希检查减少了误报。遍历覆盖了所有搜索根目录,因为攻击者的载荷可能会最终停留在机器上的任何 Python 环境中。
#### 已知路径检查
检查威胁特征中的一系列绝对路径。这些是特定攻击已知会释放文件的路径——例如,`~/.config/sysmon/sysmon.py`(LiteLLM 持久性后门)或 `%APPDATA%\sysmon\sysmon.py`(Windows 等效路径)。路径是特定于平台的,并在运行时进行扩展(`~` 和 `%VAR%`)。
**原因:** 与 IOC 文件遍历(按文件名搜索)不同,此检查查看的是确切位置。某些载荷会在特定目录中释放具有通用名称的文件——您不能在整个文件系统中搜索 "sysmon.py",但您可以检查攻击者已知会存放它的那三个位置。
#### C2 连接检查
检查活动的 TCP 连接是否与已知的攻击者基础设施进行通信。
1. 为操作系统选择合适的网络工具:Linux 上的 `ss -tnp`,macOS 上的 `lsof -i -P -n`,Windows 上的 `netstat -ano`。
2. 运行命令并 **将输出解析为结构化记录**(包含 `peer_ip`、`peer_port`、`pid` 和 `process_name` 的 `ConnectionRecord` 对象)。解析器理解 `ss` 和 `lsof` 的输出格式。
3. 将每个连接与威胁特征中的 C2 IP 地址进行匹配。如果威胁特征指定了端口(例如,Axios 使用端口 8000),则仅标记到这些端口的连接。如果未指定端口(例如,LiteLLM),则标记到 C2 IP 的任何连接。
4. 在 Linux 上,通过读取 `/proc/{pid}/exe` 使用可执行文件路径补充匹配到的连接信息。
如果启用了 `--resolve-c2`,扫描器还会对 C2 域名执行实时 DNS 查找,并将所有解析出的 IP 添加到匹配集中。此功能默认禁用,因为 DNS 查询对攻击者的基础设施是可见的。
**为什么使用结构化解析而不是子字符串匹配:** 最初的做法是检查 `ss` 输出中是否作为子字符串出现了某个 C2 IP。这会导致在查找 `1.2.3.4` 时错误地匹配到 `21.2.3.45`。结构化解析提取出实际的 IP:端口 对并进行精确匹配。输出中包含了 PID 和进程名,以便操作员可以立即识别出是哪个进程在与攻击者通信。
#### Kubernetes Pod 检查
如果 `kubectl` 可用且威胁特征定义了可疑的 Pod 模式,则查询指定命名空间中名称以这些模式开头的 Pod。LiteLLM 攻击在 `kube-system` 中部署了特权 `node-setup-*` Pod 以进行横向移动。
**原因:** 此检查仅在存在 `kubectl` 且威胁特征请求了该检查时才会运行。大多数用户不会配置 `kubectl`,该检查会被静默跳过。
#### 幽灵依赖检查
检查依赖树中本不应存在的包。这些是由受感染包注入的恶意依赖——例如,Axios v1.14.1 将 `plain-crypto-js` 添加为了一个依赖。
- **npm:** 结构化解析(JSON)`package-lock.json`,并对 `yarn.lock` 和 `pnpm-lock.yaml` 使用行锚定正则表达式。在可能的情况下提取已解析的版本(例如,`phantom:plain-crypto-js@4.2.1`)。
- **PyPI:** 遍历 `dist-info` 目录,查找引用了该幽灵包名的元数据。
**原因:** 幽灵依赖是妥协的强有力证据——它们之所以存在,仅仅是因为恶意版本拉取了它们。即使在您升级了父包之后,幽灵依赖可能仍然处于安装状态。锁文件检查能够捕获那些即使包本身已被删除,但幽灵依赖仍被记录在版本控制中的情况。
#### Windows 专项检查
在 Windows 上,会查询注册表 Run 键(`HKCU\...\Run`,`HKLM\...\Run`)和计划任务,以查找威胁特征中定义的持久性关键字。例如,LiteLLM 特征会在注册表条目和计划任务名称中搜索 "sysmon"、"litellm" 和 "system telemetry"。
**原因:** 这些是标准的 Windows 持久性机制。该检查是基于关键字的,因为攻击者在不同变种中可能会使用略微不同的路径或任务名称。
#### 持久性扫描
检查任何供应链攻击都可能滥用的通用持久性位置,并根据目标包名进行过滤:
- **Crontab:** 运行 `crontab -l` 并搜索提及该包名的行(忽略注释)。
- **Shell rc 文件:** 读取 `.bashrc`、`.zshrc`、`.profile`、`.bash_profile`,在非注释行中查找该包名。报告具体的行号。
- **`/tmp` 脚本:** 列出 `/tmp` 中的 `.py`、`.sh` 和 `.bash` 文件。对于 Python 文件,使用 AST 扫描器检查文件是否实际导入了目标包(而不是仅在字符串中提到它)。对于 shell 脚本,检查非注释行。
- **systemd 用户服务** (Linux):读取 `~/.config/systemd/user/*.service` 文件以查找包名。
- **XDG autostart** (Linux):读取 `~/.config/autostart/*.desktop` 文件以查找包名。
- **LaunchAgents** (macOS):读取 `~/Library/LaunchAgents/*.plist` 文件以查找包名。
**原因:** 供应链攻击通常会安装持久性机制以在包升级后幸存下来。crontab 检查能捕获 LiteLLM 后门 50 分钟的轮询计时器。`/tmp` 检查能捕获被释放的脚本。每一项检查都会根据包名进行过滤,以避免操作员被无关的 crontab 条目或 shell 配置所产生的噪音所淹没。
#### 缓存扫描
检查包管理器缓存中是否存在受感染包的痕迹。该扫描是按生态系统划分的——PyPI 威胁只检查 pip 缓存,npm 威胁检查 npm 和 pnpm 缓存。
- **pip 缓存:** 遍历 `~/.cache/pip` (Linux)、`~/Library/Caches/pip` (macOS) 或 `%LOCALAPPDATA%\pip\Cache` (Windows),查找名称中带有该包名的文件或目录。
- **npm 缓存:** 遍历 `~/.npm/_cacache` 查找带有该包名的文件。
- **pnpm store:** 遍历 `~/.local/share/pnpm/store` 查找带有该包名的目录。
每个缓存中找到一个就足够了——扫描器在第一次匹配后即停止。
**原因:** 即使在卸载受感染的包之后,包管理器的缓存中仍然会留有痕迹。在 pip 缓存中找到 `litellm-1.82.7.whl` 可确认在此机器上曾经下载过受感染版本。这本身属于低置信度证据,但有助于拼凑整体情况。
#### 历史记录扫描
在 `.bash_history` 和 `.zsh_history` 中搜索提及目标包的安装命令(`pip install`、`npm install`、`yarn add`、`pnpm add` 等)。命令模式是按生态系统划分的。
**原因:** Shell 历史记录是另一种在包被移除后依然存在的痕迹。在您的历史记录中找到 `pip install litellm==1.82.7` 可确认您安装了受感染版本,即使它后来已被卸载。
### 阶段 4:源代码与配置扫描
**目标:** 找到每一个使用了该包的源文件以及每一个引用了它的依赖配置。标记固定了受感染版本的配置。
扫描器会遍历所有搜索根目录,跳过 `site-packages` 和 `node_modules`(第三方代码并不重要——您想知道的是 *您自己的代码*)。对于每个文件:
1. **分类:** 文件根据生态系统插件的模式被分类为源文件(`.py`、`.js`、`.ts` 等)或配置文件(`requirements.txt、`pyproject.toml`、`package.json` 等)。
2. **快速路径过滤:** 读取整个文件,如果文件中的任何地方都没有出现包名,则立即跳过该文件。这消除了绝大多数无需逐行扫描的文件。
3. **源文件扫描:**
- 对于 **Python 文件**,扫描器使用 `ast.parse()` 构建抽象语法树,并遍历该树以查找 `import litellm`、`from litellm import X`、`from litellm.utils import Y` 以及 `litellm.completion()` 属性访问。这能产生零误报,排除了字符串字面量(注释或文档字符串中的 `"litellm"`)和仅仅提及该名称的正则表达式模式(`re.compile(r"litellm\.")`)。如果文件存在语法错误且无法解析,扫描器将退回到正则表达式匹配。
- 对于 **JavaScript/TypeScript 文件**,扫描器使用正则表达式模式来匹配 `require('package')` 和 `import ... from 'package'`。
4. **配置文件扫描:** 使用特定于生态系统的正则表达式检查依赖声明。如果发现固定版本(例如 `requirements.txt` 中的 `litellm==1.82.7`),则提取版本并在其受感染时进行标记。
扫描器排除了自身的源目录,以避免报告自身。
**为什么使用基于 AST 的检测:** 此扫描器旨在扫描 *合法* 使用目标包的系统。一台在生产环境中运行 LiteLLM 的机器会有许多 `.py` 文件在字符串、注释和变量名中提及 "litellm"。如果不进行 AST 解析,这些都会成为误报。AST 方法意味着扫描器只报告实际 `import` 或调用了该包的文件。
### 阶段 5:结论与修复
**目标:** 生成摘要和可操作的指导。
扫描器根据所有收集到的事实计算出一个 **置信度等级**:
1. 提取存在的发现类别集合(版本匹配、IOC 文件、C2 连接、持久性、缓存痕迹等)。
2. 应用优先级规则:
- 受感染版本 + 活跃 C2 连接 = **严重 (CRITICAL)**
- 受感染版本 + IOC 文件或幽灵依赖 = **高危 (HIGH)**
- 仅受感染版本,或持久性产物 = **中危 (MEDIUM)**
- 其他任何情况(源码引用、缓存痕迹、历史记录) = **低危 (LOW)**
- 完全没有发现 = 不显示置信度等级。
3. 打印统计信息:扫描的环境、发现的安装、受感染的版本、IOC 产物、源文件、配置文件以及置信度等级。
如果检测到妥协(`is_clean` 为假——意味着发现了受感染的安装、IOC 产物或受感染的配置固定项),扫描器会从 TOML 特征中打印 **针对特定威胁的修复步骤**:
1. 凭证轮换警告(如果特征中配置了 `rotate_secrets = true`)。
2. 产物移除说明(特定于平台的路径)。
3. 安全版本安装命令。
4. 配置文件更新指南(受感染版本被固定的特定文件和行号)。
5. 持久性检查命令(特定于平台)。
6. 指向安全通告的链接。
如果没有检测到妥协,但发现了源码/配置引用,则会打印一条警告,建议用户验证其版本是否安全。
**为什么采用分级结论而不是二元结论:** 一台安装了 `litellm==1.82.7` **并且** 正在主动连接到 C2 服务器的机器,与某人曾经运行过 `pip install litellm` 并留在 shell 历史记录中的机器,在根本上是处于不同情况的。这些等级有助于操作员优先安排他们的响应工作。
### 设计理念:广泛的文件系统遍历
扫描器有意遍历文件系统的大部分区域。这在拥有大量磁盘和众多文件的机器上速度会很慢。但对于需要找到 *每一次* 安装(而不仅仅是当前虚拟环境中的那个)的事件响应工具来说,这也是唯一可靠的方法。包管理器会安装到 conda 环境、pipx 虚拟环境、nvm 前缀、系统 site-packages 以及项目本地的 `.venv` 目录中。被遗忘的虚拟环境中的受感染包仍然是受感染的包。
快速路径过滤器(在逐行扫描之前检查文件内容中是否出现了包名)和目录修剪(跳过 `.git`、`__pycache__` 等)将成本保持在可管理的范围内。在典型的开发人员机器上,完整扫描只需几秒钟。
### 设计理念:数据驱动的威胁特征
扫描器不硬编码任何包名、版本、文件路径或 C2 地址。所有特定于攻击的内容都存在于 TOML 威胁特征中。这意味着:
- **无需更改代码即可添加新威胁。** 放入一个 TOML 文件,扫描器即可识别它。
- **用户可以定义自己的威胁。** 如果您的组织发现了内部妥协,编写一个 TOML 文件来描述它,扫描器就能立即工作。
- **扫描器代码纯粹是机械化的。** 它进行遍历、读取、解析、匹配和报告。智能蕴含在特征文件中。
## 免责声明
本工具按“原样”提供,不提供任何形式的保证。它搜寻已知的供应链妥协指标,但 **不能保证** 完全检测出来。一次干净的扫描并不意味着您的系统未受影响。如果您的环境中可能安装过受感染的包(哪怕是很短的时间),请务必执行凭证轮换。
标签:AMSI绕过, Axios, Blast Radius, GNU通用公共许可证, IOC检测, LiteLLM, Node.js, npm, PyPI, Python, Python安全, TeamPCP, TOML, 供应链妥协, 依赖项安全, 包管理器安全, 威胁建模, 威胁检测, 影响范围分析, 恶意包检测, 无后门, 用户可扩展, 自动化payload嵌入, 软件供应链攻击, 逆向工具