Patsakas/Nemesis
GitHub: Patsakas/Nemesis
该项目是一个面向 C/C++ 库的自动化漏洞挖掘引擎,结合大语言模型、符号执行与模糊测试来实现端到端的安全缺陷发现。
Stars: 0 | Forks: 0
# NEMESIS
[](https://github.com/Patsakas/Nemesis/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.python.org/downloads/)
[](https://github.com/AFLplusplus/AFLplusplus)
[](https://github.com/Z3Prover/z3)
**面向软件安全缺陷的神经符号漏洞挖掘引擎**
NEMESIS 是一个用于 C/C++ 库的自动化漏洞发现引擎。它将基于 LLM 的代码推理、符号验证 (Z3) 和覆盖率引导的模糊测试 (AFL++) 链接成一个单一的 pipeline,无需人工干预即可实现从*目标选择*到*崩溃分诊*的全流程。
你只需将其指向一个 C/C++ 项目。它会自动分析出哪些函数值得攻击,为每个函数编写 fuzzing harness,构建注入插桩的二进制文件,对其进行模糊测试,并仅反馈那些在未修改库上能够复现的崩溃。

该项目附带一个 Web 仪表板:你可以并排浏览库中的每个函数及其 **OSS-Fuzz 覆盖率和 NEMESIS 达到的覆盖率**,固定值得攻击的目标,启动运行任务,并实时监视 AFL++。

## 工作原理
NEMESIS 运行四个阶段,并通过反馈循环来改进失败的尝试。
```
+------------------+
| Target Library | any C/C++ project
+--------+---------+
|
Stage 1: RECON
- Scan the source tree (and optional OSS-Fuzz coverage data)
- Find low-coverage functions that touch untrusted input
- Rank by memory ops, pointer arithmetic, call depth, complexity
|
Stage 2: NEURAL (LLM)
- Analyze each target: likely bug class, CWE, reachability blockers
- Generate a format-specific AFL++ harness (3 temperature variants)
- Extract an AFL dictionary (magic bytes) from the source
|
Stage 3: SYMBOLIC (Z3 + Build)
- Verify reachability constraints with Z3
- Auto-fix harness compile errors (LLM repair, multi-pass retry)
- Build an instrumented binary (AFL + ASan/UBSan, optional MSan/TSan)
- Pre-fuzz profiling: confirm the harness actually reaches the target
|
Stage 4: FUZZING (AFL++)
- Parallel AFL++ instances with format-specific seeds + dictionary
- Crash triage: sanitizer classification, GDB backtrace, CWE mapping
- Reproduce every crash against the clean, unmodified library
|
+--------+---------+
| Reproduces on |
| clean library? |----- yes ----> Reported as a candidate
+------------------+
| no
Feedback loop (max N iterations)
Diagnose why (0% coverage -> broken harness,
coverage but no crash -> different approach) and
send back to Stage 2 for refinement.
```
### 为什么它能避免误报
每次崩溃都会在目标的**干净、从未修改过的检出**版本上重放。如果崩溃仅出现在已修补或注入插桩的构建版本中,但在干净的库上没有出现,则将其作为伪影丢弃。仅报告在真实库上能够复现的崩溃。
### 超越“段错误”的检查器
当目标配置开启时,NEMESIS 能够检测到普通的 ASan 崩溃永远无法发现的 bug 类型:
- **往返 / 差异检查器** — `decode(encode(x)) == x`,或将目标与参考实现进行比较,以捕获静默损坏和逻辑分歧。
- **输出不变量** — 每次操作后断言的逐格式安全契约。
- **MemorySanitizer** — 使用未初始化的值 (CWE-908)。
- **ThreadSanitizer** — 多线程 harness 中的数据竞争 (CWE-362)。
- **LeakSanitizer** — 内存泄漏,从运行后的 AFL 队列中抽样。
## 验证
NEMESIS 通过**对已发布的 CVE 进行回测**来评估:将其指向库的易受攻击版本,并衡量它是否能独立重新发现该 bug 以及花费的时间。这些都是可复现的——因为基本事实是公开的。
| 库 | CVE | CWE | 首次崩溃时间 | 人工输入 |
|---------|-----|-----|--------------------|-------------|
| **libpng** | [CVE-2018-13785](https://nvd.nist.gov/vuln/detail/CVE-2018-13785) | CWE-369 除以零 (经由 CWE-190 溢出) | **60 秒** (整个 pipeline 22.7 分钟) | 函数名 + 文件路径 |
| **cJSON** | [CVE-2023-53154](https://nvd.nist.gov/vuln/detail/CVE-2023-53154) | CWE-125 越界读取 | **81 秒** | 无 — 完全自动 |
| **libtiff** | [CVE-2022-3970](https://nvd.nist.gov/vuln/detail/CVE-2022-3970) | CWE-190 溢出 → 堆 OOB 写入 | 34 分钟 | 函数名 + 文件路径 |
**libpng** — 在 `pngrutil.c:3154` 处崩溃,这正是 NVD 条目中指明的确切位置。唯一的输入是固定的函数;NEMESIS 生成了 harness,发现并注入了绕过 libpng 自身防护所需的 `png_set_user_limits()` 调用,并自行合成了一个感知数据块的 PNG 变异器。
**cJSON** — 完全无需人工干预端到端完成。后来被 `--auto-sanitizer` 独立重新发现,该功能使用 LLM 对 sanitizer 配置进行排名,并将前两个作为单独的步骤运行(约 51 分钟,去重后合并为一项发现)。
**libtiff** — 这是反馈循环的一个很好的例证:第 0-2 次迭代生成的 harness 虽然调用了目标,但从未进入其主体(0% 行覆盖率,因为 `TIFFReadRGBATileExt` 在非分块输入上提前返回)。循环不断进行优化,第 3 次迭代中的第二次 LLM 修复生成了一个跳过非分块图像并预分配光栅缓冲区大小的 harness — 在模糊测试开始 60 秒内就出现了崩溃。
*坦诚的警告:* 这些崩溃是在 ASan 下手动复现并验证的;由于 libtiff 回调签名周围无关的 LLM 噪声干扰,自动发现条目失败,因此这不是像其他两个那样完全干净的端到端自动结果。
### 目前尚不适用的情况
只有同时报告失败案例,回测才是诚实的。
- **狭窄的结构触发条件无法通过字节级变异触及。** libwebp 的 [CVE-2023-4863](https://nvd.nist.gov/vuln/detail/CVE-2023-4863) (Huffman 码长损坏) 和 lz4 的 [CVE-2021-3520](https://nvd.nist.gov/vuln/detail/CVE-2021-3520) (字面长度 token 链) 在 15 分钟的预算内**未能**被重新发现,尽管 harness 到达了正确的函数。原生的 AFL 字节翻转无法合成这些位模式。这正是前文所述促使开发格式感知变异器合成的原因 —— 它现在能生成符合规范的适配器,但执行成本仍然是一个瓶颈。
- **CVE 归因可能具有误导性。** libtiff CVE-2022-22844 被描述为通过 `TIFFFetchNormalTag` 进行的越界读取,但上游修复补丁修补的是 `tools/tiffset.c` 中的 argv 处理 —— bug 出在 CLI 工具中,而不是库中,且无法从任何内存文件 harness 中触达。报告中指出的是崩溃位置,而不是 bug 位置。
## 快速开始
### 前置条件
- Python 3.11+
- [AFL++](https://github.com/AFLplusplus/AFLplusplus) (模糊测试阶段)
- [Z3](https://github.com/Z3Prover/z3) (符号验证)
- `clang` / `afl-clang-fast`,以及目标自身的构建依赖
- 至少一个 LLM 提供商的 API 密钥(见下文)
### 安装
```
git clone https://github.com/Patsakas/Nemesis.git
cd Nemesis
pip install -e ".[dev]"
```
`pip install -e` 会将 `nemesis` 命令放置在你的 PATH 中。如果找不到该命令(或者你安装到了与 PATH 上的解释器不同的解释器中 —— 这在 conda 环境中很常见),下文的所有命令也可以通过在仓库根目录下运行 `python -m nemesis.cli ` 来代替。
### 配置 LLM 提供商
NEMESIS 使用多提供商链,并在达到速率限制时自动回退。复制模板并填入你拥有的任何密钥 —— 单个提供商即可运行。
```
cp .env.example .env
# 然后编辑 .env:
# NVIDIA_API_KEY=... # NVIDIA NIM
# GROQ_API_KEY=... # Groq(在 console.groq.com 有免费层级)
# CEREBRAS_API_KEY=... # Cerebras
# GOOGLE_AI_KEY=... # Gemini
```
### 运行
```
# 接入新库:从其 source tree 自动生成 target config
nemesis onboard --source-root ~/libfoo --project-name libfoo
# Scan mode:每个 target 的预算较短,target 数量多 —— 适合首次扫描
nemesis run --target libfoo --scan
# 对单个 target 运行 Full pipeline
nemesis run --target libfoo
# Deep mode:scan -> score -> 对 top-N targets 进行更长时间的 deep-fuzz
nemesis run --target libfoo --deep
# 仅执行 Recon(只对 targets 排名,不进行 fuzzing)
nemesis recon --target libfoo
# 显示某个 target 完全解析后的 config
nemesis config --target libfoo --show
```
有用的参数:
| 参数 | 功能 |
|------|--------------|
| `--timeout-hours N` | 每个目标的模糊测试预算 (`0.5` = 30 分钟)。覆盖 `--scan` 使用的 15 分钟。 |
| `--max-targets N` | 在 N 个函数后停止。 |
| `--auto-sanitizer` | 让 LLM 对 sanitizer 配置进行排名,并将排名靠前的配置作为单独的步骤运行。 |
| `--stages 1,2` | 仅运行部分阶段。 |
| `--resume` | 跳过上次运行已处理的函数。 |
## 其他命令
除了核心的 `onboard` / `run` 循环外,CLI 还提供了一些辅助工具。使用 `--help` 运行它们以获取完整的参数列表。
### `nemesis scout` — 寻找值得模糊测试的目标
将未经过模糊测试的 C/C++ 解析器库作为新 bug 的候选目标进行排名。它会排除所有 OSS-Fuzz 已经持续模糊测试的项目,并根据可 fuzz 性和往返检查器潜力对其余项目进行评分,这也是最有可能发现新漏洞的地方。
```
nemesis scout # top 25 candidates
nemesis scout --round-trip-only # only where a differential oracle applies
nemesis scout -n 50 --out scout.md # write a markdown report
```
### `nemesis setup` — 准备目标的工作区
读取现有的目标配置,通过 rsync 同步工作副本,创建构建目录,并验证注入插桩和调试构建能够编译。请参阅[添加新目标](#adding-a-new-target)。
```
nemesis setup -t libfoo
nemesis setup -t libfoo --url https://github.com/some/libfoo # clone if source is missing
nemesis setup -t libfoo --skip-build # workspace only
```
### `nemesis verify-crashes` — 将真正的 bug 与补丁伪影区分开来
仅与 `patch` 策略相关。重新构建*未打补丁*的库并针对其重放每个崩溃:复现意味着真正的 bug,未能复现则意味着该崩溃是 LLM 的可达性补丁人为制造的。
```
nemesis verify-crashes -t libfoo
```
### `nemesis report` — 导出发现
```
nemesis report # all findings, to stdout
nemesis report --run-id --format markdown # one run, as markdown
nemesis report --format json -o findings.json
```
### `nemesis daemon` — 计划扫描
按计划在无人值守的情况下运行 pipeline,并可选择发送至 webhook。
```
nemesis daemon --once # one cycle, then exit
nemesis daemon -t all --interval 12 # every 12 hours
nemesis daemon --schedule '0 2 * * *' # 2am daily, cron syntax
```
## 添加新目标
NEMESIS 是**完全配置驱动**的 —— 添加库不需要修改代码,只需在 `config/targets/` 下放置一个 YAML 文件。
通常的流程:
```
git clone https://github.com/some/libfoo ~/libfoo_clean # onboard scans a real source tree
nemesis onboard --source-root ~/libfoo_clean --project-name libfoo
nemesis setup -t libfoo # prepare the work copy + verify the builds compile
nemesis run -t libfoo --scan # first pass
```
`nemesis onboard` 会扫描源码并生成一个初始配置(构建命令、头文件路径、C 与 C++ 检测、依赖提示);随后你可以对其进行完善。`nemesis setup` 读取该配置以通过 rsync 同步工作副本,创建构建目录,并在你花费时间进行模糊测试之前检查注入插桩 + 调试构建是否能够真正编译 —— 传入 `--url `,如果缺少 `source_root`,它也会将其克隆(在全新的机器上很有用),或者传入 `--skip-build` 仅准备工作区。
最小的配置如下所示:
```
target:
name: "mylib"
source_root: "$HOME/mylib_clean" # pristine checkout — NEVER modified
work_root: "$HOME/mylib_work" # working copy — patched per target (Strategy B only)
build_dir: "$HOME/mylib_work/build_fuzz"
debug_build_dir: "$HOME/mylib_clean/build_debug"
build:
configure: >
rm -f CMakeCache.txt &&
CC=afl-clang-fast cmake .. -DCMAKE_BUILD_TYPE=Debug
-DCMAKE_C_FLAGS="-g -fsanitize=address -Wno-error"
make: "make -j$(nproc)"
debug_configure: >
rm -f CMakeCache.txt &&
CC=clang cmake .. -DCMAKE_BUILD_TYPE=Debug
-DCMAKE_C_FLAGS="-g -O1 -fsanitize=address,undefined -Wno-error"
debug_make: "make -j$(nproc)"
library_name: "libmylib.a" # static library in the build tree
link_libs: "-lz -lm -lpthread" # linker flags for the harness
harness_includes: ["mylib.h"] # public headers the harness needs
api_func_fixes: # correct known LLM API hallucinations
wrong_api_name: "correct_api_name"
magic_bytes: # format magic bytes -> AFL dictionary
format_foo: ["FOO\x00"]
recon_scoring:
bonus_patterns:
"mylib_parse_": 5.0 # boost high-value filename prefixes
penalty_files: ["utils.c", "log.c"]
penalty_dirs: ["test", "examples"]
penalty_funcs: ["_free", "_init"]
fuzzing:
timeout_hours: 0.25
seeds:
formats:
foo: "$HOME/nemesis/seeds/mylib/foo"
```
有关完整、真实的示例,请参阅 `config/targets/libarchive.yaml`。
## 架构
[ARCHITECTURE.md](ARCHITECTURE.md) 进行了更深入的探讨:保持 pipeline 与具体库无关的不变量、harness 生成和崩溃分诊的工作原理,以及塑造了它们的故障模式。
```
nemesis/
├── config/
│ ├── default.yaml # default engine parameters
│ └── targets/ # per-target YAML overrides
├── nemesis/ # Python package
│ ├── cli.py # Click CLI entry point
│ ├── config.py # Pydantic settings + YAML layering
│ ├── models.py # all shared data models (Pydantic v2)
│ ├── pipeline.py # orchestrator + feedback loop
│ ├── onboard.py # `nemesis onboard` — auto-generate a target config
│ ├── reporter.py # findings database management
│ ├── recon/ # Stage 1: source scan + OSS-Fuzz introspector + scoring
│ ├── neural/ # Stage 2: LLM analysis, harness generation, RAG oracle
│ ├── symbolic/ # Stage 3: Z3 verification, build, instrumentation
│ ├── fuzzing/ # Stage 4: AFL++ orchestration, crash triage, coverage
│ ├── api/ # FastAPI backend for the dashboard (routes + models)
│ └── templates/ # harness scaffolding (FuzzedDataProvider, etc.)
├── frontend/ # React dashboard (Vite), served by the API in production
├── seeds/ # seed corpora per format
├── tests/ # pytest suite
└── pyproject.toml
```
### 配置分层
`config/default.yaml` → `config/targets/.yaml` → `NEMESIS_*` 环境变量。
目标配置覆盖默认配置;环境变量覆盖一切。
### 双仓库模型
每个目标都有两个根目录。**干净的**检出 (`source_root`) 从未被修改,用于崩溃复现。**工作**副本 (`work_root`) 仅由可选的补丁策略触碰。默认的 harness 策略直接从干净的源码构建,从不进行任何修补。
## Web 仪表板
可选的 React 仪表板 (`frontend/`) 与 FastAPI 后端通信,该后端读取与 CLI 生成的相同的发现数据库、运行结果和实时 AFL 统计信息:
- **目标** — 库中的每个函数,其 OSS-Fuzz 覆盖率与 NEMESIS 达到的覆盖率并排显示;勾选一个以固定它(写入 `config/targets/.yaml`,保留注释),并打开固定行以调整检查器和 harness 参数。从这里启动运行任务,并选择每个目标的模糊测试预算。
- **实时** — 通过 websocket 传输的 AFL++ 统计信息:execs/sec、语料库、崩溃、挂起。
- **操作** — 运行 `onboard`、`setup`、`recon`、`scout` 和 `verify-crashes` 并跟踪其输出。这些命令通过固定的服务端白名单底层调用相同的 CLI,并且每个字段都作为单独的 argv 条目传递,而不是作为 shell 字符串传递。
- **运行 / 报告** — 运行历史记录(包含逐函数的结果)和披露草稿。
一个运行任务会在最初的几分钟里进行探测、harness 生成和注入插桩的构建 —— 远在 AFL 产生任何统计信息之前。pipeline 会持续推进并写入心跳信息,因此横幅会显示当前处于哪个阶段、正在处理的函数以及目标列表的进度。它同样适用于从 CLI 启动的运行任务,并会将进程已死亡的运行报告为“陈旧”状态,而不是让其永远处于“运行中”。



仪表板仅以源码形式提供 —— `frontend/dist/` 是构建产物,不在仓库中,因此**在提供服务前构建一次**,否则 `/` 将返回 404(`/api` 路由仍然有效)。
**单端口(推荐):**
```
cd frontend && npm install && npm run build && cd .. # build the UI once
pip install -e ".[web]"
nemesis serve # UI + API together on http://localhost:8000
```
**双端口(开发模式,热重载):**
```
nemesis serve # terminal 1 — API on :8000
cd frontend && npm run dev # terminal 2 — UI on :5173, proxies /api → :8000
```
在仓库根目录下运行 `nemesis serve`它会解析相对于工作目录的 `config/targets/`、`workspace/` 和 `findings.yaml`。如果 `nemesis` 脚本不在你的 PATH 中,`python -m nemesis.cli serve` 是等效的。
仪表板从端到端都是数据驱动的 —— 如果还没有运行任务或发现,它将渲染空状态而不是示例数据。视图是可寻址的 (`#/targets/libpng`, `#/live`),因此你可以直接为它们添加书签或链接。
| | |
|---|---|
|  |  |
| **实时** — 通过 websocket 传输的 execs/sec、语料库增长、崩溃和挂起 | **运行** — 每次 pipeline 执行,包含逐函数结果 |
## 开发
```
pytest tests/ -v # run the test suite
ruff check nemesis/ tests/ # lint
ruff format nemesis/ tests/ # format
```
## 路线图
- [x] 配置驱动的 pipeline — 零代码修改添加目标
- [x] `nemesis onboard` — 从源码树自动生成目标配置
- [x] 集成 OSS-Fuzz 语料库作为种子
- [x] 深度模糊测试模式 (扫描 → 评分 → 对前 N 名进行深度模糊测试)
- [x] 多检查器支持(往返、差异、MSan、TSan、LSan、不变量)
- [x] 自动选择 sanitizer(由 LLM 排序,多步执行)
- [ ] 自动检测除 cmake 之外的构建系统(autotools、meson)
- [ ] 结构感知变异(protobuf、ASN.1、归档文件头)
- [ ] 污点引导的模糊测试:追踪哪些输入字节触达了目标
- [ ] Git 历史分析:最近更改的函数、过去的 bug 位置
- [ ] 自动起草漏洞报告 + PoC
## 负责任地使用
NEMESIS 是一款安全研究工具,用于在**你获得授权**进行测试的软件中寻找内存安全 bug。如果你使用它在第三方项目中发现漏洞,请遵循协同披露原则:私下向维护者报告,并给予他们在任何公开披露之前修复漏洞的时间。
## 作者
**Georgios Patsakas** — https://github.com/Patsakas
## 许可证
[MIT](LICENSE)
标签:C2, DLL 劫持, 大语言模型, 神经符号AI, 逆向工具, 防御框架