epaneral/yara-detection-harness
GitHub: epaneral/yara-detection-harness
一个基于 YARA 的检测即代码框架,通过配对合成语料库和 CI 门禁对规则召回率与误报率进行回归测试。
Stars: 0 | Forks: 0
# yara-detection-harness
[](https://github.com/epaneral/yara-detection-harness/actions/workflows/ci.yml)
一个用于**生成的文本和代码**的小型、可测试的检测 pipeline:手写的
YARA 规则、带标签的合成语料库,以及一个以召回率和误报率作为构建把关标准的
回归测试套件。
这是“检测即代码”(detection-as-code)。规则存放在版本控制中,每次运行时都会针对
已知的恶意和良性样本对每条规则进行测试,并且一条规则不能仅仅因为编译通过就“过关”——它必须能抓取到它应该抓取的内容,同时对那些看起来很像但并非如此的情况保持沉默。
## 为什么会有这个项目
大规模的内容扫描在成为威胁情报问题之前,首先是一个数据和精度问题。难点不在于编写一条能针对反向 shell 触发的规则——而在于编写一条既能触发反向 shell,又*不会*误报三行之外碰巧使用了 `>&` 重定向的备份脚本的规则。这个仓库就是围绕这个问题构建的:每一个良性样本都是某个恶意样本的刻意**近似匹配(near-miss)**,因此该测试套件衡量的是精度,而不仅仅是覆盖率。
## 范围与威胁模型
- **在范围内:** 恶意的*文本和代码*制品——钓鱼工具包标记、凭证收集器、shell/PowerShell 下载器和 stager、数据渗出代码片段。这反映的是内容扫描(生成系统可能被要求生成的那类制品),而不是 PE/二进制恶意软件分析。
- **不在范围内:** 真实的恶意软件二进制文件。语料库完全是合成的并且存放在仓库中,因此磁盘上没有任何实际样本,并且良性/恶意的划分处于完全控制之下。
- **构造上即已去除威胁:** 所有网络指标都使用文档保留的 IP(`192.0.2.0/24`,RFC 5737)以及明显是伪造的 token。这里没有任何东西是可运行的。
## 布局
```
rules/ YARA rules, grouped by family (powershell / shell / phishing)
corpus/
malicious/ known-positive samples
benign/ known-benign near-misses (each shadows a malicious sample)
tests/
manifest.yml ground-truth labels + expected matches (single source of truth)
test_rules.py the harness
enrichment-mcp/
server.py VirusTotal enrichment MCP server (separate component, own deps)
test_server.py unit tests for its pure logic (validation, encoding, normalize)
ruff.toml lint + format configuration for the Python (harness + MCP server)
.github/workflows/ci.yml per-component CI (lint / harness / enrichment-mcp) on every push
```
## 语料库设计
语料库是成对的。对于每个恶意样本,都有一个良性样本与其共享表面特征但不共享意图:
| 恶意 | 良性近似匹配 | 区分因素 |
|---|---|---|
| IEX + `DownloadString` 执行载体 | 管理脚本:`DownloadFile` 到磁盘 | 执行原语(IEX),而非下载操作 |
| `-enc` + 隐藏窗口 | base64 *配置* 解码 | 编码命令 + 窗口抑制组合 |
| `bash -i >& /dev/tcp/...` | 带有 `>&` / `2>&1` 重定向的备份 | `/dev/tcp` socket 的使用 |
| `bash -i >& /dev/tcp/...` | `/dev/tcp` TCP 端口检查(无交互式 shell) | 交互式 shell(`bash -i`),而非单独的 `/dev/tcp` |
| `curl http:// \| bash` | rustup 风格的 `curl https://host \| sh` | 基于 http 的原始 IP 源 |
| `$_POST['password']` → `mail(attacker)` | 同源登录处理程序 | 向外的数据渗出,而非捕获行为 |
| 携带凭证的 Telegram API | 携带部署状态的 Telegram API | 凭证上下文 |
## 测试套件
`tests/test_rules.py` 完全由 `tests/manifest.yml` 驱动——在其中添加样本和标签,它就会被自动覆盖测试。三道关口:
1. **编译** —— 每个 `.yar` 文件都能编译;损坏的规则会导致构建失败。
2. **召回率** —— 每个恶意样本都能被其预期的规则捕获。
3. **误报** —— 良性样本不产生任何匹配,并且整个良性语料库的总误报率必须保持在 `FP_THRESHOLD`(此处设为 `0.0`)或以下。
该阈值是一个单一常量,因此随着语料库的增长且零误报标准不再现实时,该关口是明确且可调整的。
```
pip install -r requirements.txt
pytest -v
```
除了检测关口外,CI 还会在
Python(测试套件 + MCP server)上运行 [`ruff`](https://docs.astral.sh/ruff/),作为额外的两道关口:一个**格式检查**(`ruff format
--check`)和一个 **lint pass**(`ruff check`,涵盖 pyflakes、bugbear、blind-except、
pyupgrade、async 和 pytest 风格规则)。两者都固定了版本,因此结果只取决于代码,而不是碰巧运行在某个 runner 上的 `ruff` 版本。
```
ruff format --check . # formatting gate
ruff check . # lint gate
```
CI 将这些作为三个独立的任务运行 —— `lint`(全仓库 ruff)、`harness`
(规则 + 语料库套件)和 `enrichment-mcp`(MCP server 的单元测试)—— 每个
任务都只配置了它所需的依赖。MCP server 保留了它自己的
依赖集,因此它的测试会独立安装和运行:
```
pip install -r enrichment-mcp/requirements-dev.txt
pytest enrichment-mcp -v
```
## 规则设计说明
编写规则时考虑了 YARA 的匹配引擎,而不仅仅是正确性:
- **原子优于通配符。** 条件锚定在具体的字符串上(`/dev/tcp/`、
`api.telegram.org/bot`、IP 正则表达式之前的 `http://`),以便扫描器获得快速的
首次匹配,而不是被迫进行全面评估。没有以 `.*` 开头的
正则表达式,也没有主要由通配符组成的模式。
- **组合优于单一存在。** 几乎每条规则都要求*两个*原语同时出现
(捕获**且**渗出,编码**且**隐藏,获取**且**管道传输至 shell)。单一
特征的规则是误报的来源;近似匹配语料库的存在正是为了
捕获这种失败。
- **每条规则中声明了精度控制手段。** 每条规则的 `meta` 和行内注释都指明了
使其避开良性孪生样本的那一个特征。
## 路线图(尚未构建 —— 阶段 2)
为了确保可交付,故意将其排除在当前范围之外:
- `yaraQA`(Florian Roth)作为性能/质量**关口**接入,而不仅仅是一个运行器。
- 超出 yaraQA 的自定义基于 `plyara` 的检查。
- 双路径接入:一个抓取的静态源 + 一个结构化订阅源。
- 一个对存储的语料库运行新规则的追溯狩猎(retro-hunt)作业。
## 本项目不包含什么
- 这不是生产级的检测内容 —— 合成语料库,没有真实的误报基准率。
- 未曾基于活体恶意软件进行训练或测试。
- 规则覆盖范围仅用于演示(少数几个家族),旨在展示测试
规范,而非穷尽所有可能。
*作者:Elyse Paneral · 2026*
标签:URL发现, YARA, 云资产可视化, 安全检测, 安全规则引擎, 持续集成(CI), 文本分类, 测试框架, 逆向工具