Pinperepette/snakebite
GitHub: Pinperepette/snakebite
基于启发式规则与 LLM 智能过滤的 PyPI 恶意包检测工具,专注于发现零日供应链攻击并有效降低误报。
Stars: 28 | Forks: 1



# snakebite
PyPI 供应链攻击检测器。扫描 Python 包中的恶意模式(凭据窃取、代码混淆、持久化机制),并使用 LLM 过滤误报。
```
___ _ _ _ _
/ __|_ _ ___| |_____| |__(_) |_ ___
\__ \ ' \/ _ \ / / -_) '_ \ | _/ -_)
|___/_||_\__,_|_\_\___|_.__/_|\__\___|
```

## 为什么需要它
2026 年 3 月 24 日,`litellm` 的 1.82.7 和 1.82.8 版本被发布到 PyPI,其中包含窃取凭据的 payload。一个恶意的 `.pth` 文件会在每次 Python 进程启动时自动执行——无需导入——并将 SSH 密钥、云凭据、加密钱包和 Kubernetes 密钥泄露到攻击者控制的域。该包的每月下载量高达 9700 万次。
纯粹基于启发式的扫描器会标记诸如 `os.environ` 或 `subprocess` 之类的模式,但会让你淹没在误报中——合法的包也一直都在使用这些。snakebite 通过两阶段方法解决了这个问题:
1. **14 条针对真实攻击模式(而非通用代码异味)调优的启发式规则**
2. **LLM 驱动的分析**,结合上下文阅读代码并过滤掉合法使用
LLM 知道 `os.environ.get("AIOHTTP_NO_EXTENSIONS")` 是一个构建开关,而不是凭据窃取。知道编辑器包中的 `subprocess.call([editor])` 是正常的。知道测试文件中的 `base64.b64decode` 是测试数据。你得到的是有效信号,而不是噪音。
## 安装
```
git clone https://github.com/pinperepette/snakebite.git
cd snakebite
```
零外部依赖。仅使用标准库。Python 3.8+。
## 两种模式
### `local` — 扫描本地机器上已安装的包
```
# 扫描所有内容
python3 snakebite.py local
# 扫描特定 packages
python3 snakebite.py local flask requests litellm
```
从 PyPI 下载每个包,解压 sdist + wheel,扫描每个 `.py`、`.pth`、`setup.py`、`__init__.py`。如果配置了 LLM 后端,可疑的发现会在报告前进行分析。
### `feed` — 实时监控 PyPI
```
# 对最新 packages 进行单次扫描
python3 snakebite.py feed
# 每 60 秒持续监控
python3 snakebite.py feed --loop 60
```
监控 PyPI 的 RSS 订阅以获取新增和更新的包,下载并扫描每一个。让它保持运行,以便在恶意包发布时立即捕获——这正是攻击者在包被下架前利用的窗口期。
## LLM 后端
当你运行 snakebite 而不带 `-m` 时,它会询问你想使用什么:
```
Select LLM backend for false positive filtering:
1) claude-code Claude Code CLI (subscription)
2) claude Anthropic API (ANTHROPIC_API_KEY)
3) chatgpt OpenAI API (OPENAI_API_KEY)
4) ollama Ollama local model
5) none Heuristics only, no LLM
```
或者直接指定:
```
python3 snakebite.py local -m claude-code # Claude Code CLI (subscription)
python3 snakebite.py local -m claude # Anthropic API
python3 snakebite.py local -m chatgpt # OpenAI gpt-4o
python3 snakebite.py local -m chatgpt:gpt-4o-mini # OpenAI specific model
python3 snakebite.py local -m ollama:qwen2.5:32b # Ollama local
python3 snakebite.py local --no-llm # No LLM, heuristics only
```
### API 密钥
```
# Anthropic (用于 -m claude)
export ANTHROPIC_API_KEY="sk-ant-..."
# OpenAI (用于 -m chatgpt)
export OPENAI_API_KEY="sk-..."
```
将 export 行添加到 `~/.zshrc` 或 `~/.bashrc` 以持久化它们。
**claude-code** 通过 `claude` CLI 使用你的 [Claude Code](https://claude.ai/code) 订阅。不需要 API 密钥。
**ollama** 完全在本地运行。安装 [Ollama](https://ollama.ai),拉取一个模型(`ollama pull qwen2.5:32b`),即可开始。
## 架构
```
┌─────────────┐ ┌──────────────┐ ┌────────────────┐ ┌─────────────┐
│ PyPI API │────>│ Download │────>│ Heuristic │────>│ LLM filter │
│ / RSS feed │ │ & extract │ │ engine │ │ (optional) │
└─────────────┘ │ sdist+wheel │ │ 14 rules │ │ │
└──────────────┘ └───────┬────────┘ └──────┬──────┘
│ │
no hits? ─> CLEAN verdict:
│ TRUE/FALSE POSITIVE
hits found │
└──────────────────>│
v
┌──────────┐
│ Output │
└──────────┘
```
1. **获取** — 从 PyPI 下载 sdist 和/或 wheel(或通过 RSS 获取新包)
2. **解压** — 安全解包(路径遍历保护,符号链接过滤)
3. **扫描** — 针对 `.py`、`.pth`、`setup.py`、shell 脚本运行 14 条基于正则表达式的启发式规则
4. **过滤** — 仅在发现匹配项时:将代码片段 + 上下文发送给 LLM 进行判断
5. **报告** — 附带解释输出 CLEAN / LOW / MEDIUM / HIGH / CRITICAL
## 检测内容
| 规则 | 严重性 | 模式 |
|------|----------|---------|
| `PTH_EXEC` | CRITICAL | 带有可执行代码的 `.pth` 文件(litellm 攻击媒介) |
| `BASE64_NESTED` | CRITICAL | 嵌套的 base64 解码(payload 混淆) |
| `EXEC_ENCODED` | CRITICAL | 带有编码 payload 的 `exec()`/`eval()` |
| `SETUP_NETWORK` | CRITICAL | `setup.py` / `__init__.py` / `.pth` 中的网络调用 |
| `CRED_HARVEST` | CRITICAL | 访问 SSH 密钥、AWS/GCP/Azure 凭据、kubeconfig |
| `CRYPTO_WALLET` | CRITICAL | 访问 Bitcoin/Ethereum/Solana 钱包文件 |
| `K8S_SECRETS` | CRITICAL | 读取 Kubernetes secrets 或服务账户 token |
| `PERSISTENCE` | CRITICAL | systemd/cron/launchd/shell rc 持久化 |
| `SETUP_SUBPROCESS` | HIGH | setup/init 文件中的 subprocess/os.system |
| `ENV_DUMP` | HIGH | 安装上下文中的批量环境变量收集 |
| `DNS_EXFIL` | HIGH | 云元数据 endpoint(AWS IMDS、GCP、Alibaba) |
| `OBFUSCATION` | HIGH | chr() 链、反向 exec、动态 base64 导入 |
| `ARCHIVE_EXFIL` | HIGH | 创建压缩包 + HTTP POST(数据渗出) |
| `OPENSSL_ENCRYPT` | HIGH | OpenSSL 加密(渗出准备阶段) |
## 真实世界检测:litellm 1.82.7
snakebite 能通过三个 CRITICAL 发现捕获 litellm 供应链攻击:
```
======================================================================
[CRITICAL] litellm 1.82.7
LLM: Malicious .pth file executing obfuscated credential stealer at Python startup
======================================================================
[CRITICAL] PTH_EXEC in litellm_init.pth:1
import os, subprocess, sys; subprocess.Popen([sys.executable, "-c", "import base64; exec(...)"])
[CRITICAL] CRED_HARVEST in litellm_init.pth:1
.ssh/id_rsa, .aws/credentials, .kube/config
[CRITICAL] BASE64_NESTED in litellm_init.pth:1
exec(base64.b64decode(base64.b64decode(...)))
```
不需要 LLM —— 仅凭启发式就能将其标记为 CRITICAL。LLM 会确认其为真阳性,并添加有关渗出机制的上下文。
## 威胁模型
snakebite 能检测:
- 发布到 PyPI 的 Python 包中的供应链攻击
- 在安装或导入时发生的凭据(SSH、云、数据库、加密货币)渗出
- 用于隐藏恶意 payload 的代码混淆
- 嵌入在包中的持久化机制(systemd、cron、launchd)
- 滥用 `.pth` 文件进行预导入代码执行
- 从受损包发起的 Kubernetes 横向移动
snakebite **不能**检测:
- 恶意编译扩展(`.so`、`.pyd`、`.dll`)—— 二进制分析超出范围
- 由特定输入或条件触发的仅限运行时的攻击
- 没有静态指标的逻辑炸弹
- 域名仿冒或依赖混淆(使用 [pip-audit](https://github.com/pypa/pip-audit) 来处理)
- Python/PyPI 生态系统之外的攻击
- 合法代码中的漏洞(使用 [bandit](https://github.com/PyCQA/bandit) 或 [safety](https://github.com/pyupio/safety))
## LLM 使用与隐私
当一个包触发了启发式规则时,snakebite **仅发送可疑的代码片段**(每次命中周围的几行上下文)到选定的 LLM 后端。它**不会**发送:
- 完整的包源代码
- 你的系统信息
- 你的凭据或环境变量
- 通过了启发式检查的包内容
如果你对此有顾虑:
- 使用 `ollama` —— 所有内容都保留在你的机器上
- 使用 `--no-llm` —— 完全不进行外部调用,纯启发式分析
- 查看发送的内容:使用 `--verbose` 运行以查看确切的代码摘录
## 性能
| 模式 | 速度 | 备注 |
|------|-------|-------|
| 仅启发式 (`--no-llm`) | 每包约 1-2 秒 | 下载 + 解压 + 正则扫描 |
| 使用 LLM | 每包约 5-15 秒 | 取决于后端和模型 |
| RSS 订阅 (`--loop 60`) | 每周期约 40 个包 | PyPI 每次拉取 RSS 发布约 40 个包 |
LLM 是瓶颈。`claude-code` 和 API 后端比本地模型更快。Ollama 的速度取决于你的硬件和模型大小。
仅启发式模式足够快,可用于完整的本地扫描(几分钟内扫描数百个包)。
## 对比
| 工具 | 方法 | LLM 过滤 | 侧重供应链 | 误报 |
|------|----------|---------------|-------------------|-----------------|
| **snakebite** | 启发式 + LLM | 是 | 是 | 低(由 LLM 过滤) |
| [bandit](https://github.com/PyCQA/bandit) | AST 分析 | 否 | 否(通用代码质量) | 高 |
| [pip-audit](https://github.com/pypa/pip-audit) | 漏洞数据库 | 否 | 部分(仅限已知 CVE) | 低 |
| [safety](https://github.com/pyupio/safety) | 漏洞数据库 | 否 | 部分(仅限已知 CVE) | 低 |
| [packj](https://github.com/ossillate-inc/packj) | 启发式 | 否 | 是 | 中高 |
pip-audit 和 safety 捕获**已知**漏洞。snakebite 捕获**未知**的恶意代码 —— 尚未被报告的零日供应链攻击。
## CI 集成
在部署前扫描依赖项:
```
# 在你的 CI pipeline 中
pip install -r requirements.txt
python3 snakebite.py local --no-llm
```
使用 LLM(在 CI secrets 中设置 API 密钥):
```
export ANTHROPIC_API_KEY="${{ secrets.ANTHROPIC_API_KEY }}"
python3 snakebite.py local -m claude
```
扫描 requirements 文件而无需安装:
```
cat requirements.txt | cut -d'=' -f1 | xargs python3 snakebite.py local
```
GitHub Actions 示例:
```
- name: Scan dependencies for supply chain attacks
run: |
pip install -r requirements.txt
python3 snakebite.py local --no-llm
```
## 选项
```
-m, --model LLM backend (claude-code, claude, chatgpt, ollama:)
--no-llm Heuristics only, skip LLM analysis
--log FILE Save suspicious findings to a JSON file
-v, --verbose Show clean packages and false positive details
--version Show version
```
Feed 模式:
```
--loop N Repeat scan every N seconds (default: single run)
```
## 警报日志
使用 `--log` 将所有可疑发现保存到 JSON 文件:
```
# 监控 feed 并记录 alerts
python3 snakebite.py feed --loop 60 --log alerts.json -m claude-code
# 扫描本地 packages 并记录 alerts
python3 snakebite.py local --log alerts.json -m claude-code
```
每个警报都会附带完整详细信息追加到文件中:
```
{
"timestamp": "2026-03-24T18:29:33.450991+00:00",
"package": "stats-helpers",
"version": "1.0.0",
"threat_level": "CRITICAL",
"summary": "Litecoin private key stealer",
"pypi_url": "https://pypi.org/project/stats-helpers/",
"hits": [
{
"rule": "SETUP_NETWORK",
"severity": "CRITICAL",
"file": "stats_helpers/__init__.py",
"line_no": 36,
"line": "response = requests.post("
}
],
"llm_findings": [...],
"reviewed": false
}
```
`"reviewed": false` 字段让你可以追踪哪些警报已经分析过。使用 `--loop` 让它保持运行,并定期检查文件以获取新发现。
## 输出示例
干净(误报被 LLM 过滤):
```
18:30:54 OK astroid 3.3.10 - CLEAN (LLM: Benign setuptools namespace package .pth files)
18:31:22 OK babel 2.16.0 - CLEAN (LLM: Legitimate CLDR data import in setup.py, not executed during install)
18:31:39 OK banks 2.2.0 - CLEAN (LLM: Benign test code verifying base64 encoding of image data URLs)
```
可疑:
```
======================================================================
[CRITICAL] evil-package 0.1.0
LLM: Credential stealer targeting SSH keys and cloud provider tokens
======================================================================
! CRED_HARVEST: Reads SSH private keys and AWS credentials, concatenates into single payload
! SETUP_NETWORK: POSTs collected credentials to external domain during pip install
! ENV_DUMP: Captures all environment variables including API tokens
```
## 许可证
MIT
标签:AI风险缓解, Chrome Headless, DLL 劫持, DNS 反向解析, GraphQL安全矩阵, Kubernetes安全, LLM, Petitpotam, PFX证书, PyPI, Python, Python包安全, SAST, Unmanaged PE, 代码混淆, 包管理器安全, 启发式分析, 大语言模型, 安全扫描器, 提示注入防御, 攻击检测, 无后门, 源代码安全, 盲注攻击, 网络安全, 误报过滤, 逆向工具, 错误基检测, 隐私保护, 零依赖, 静态代码分析