CyberKareem/kameHunt
GitHub: CyberKareem/kameHunt
一款利用 LLM 辅助验证和排序的 CVE 变体分析引擎,用于在开源代码中搜寻已知漏洞模式并大幅降低误报率。
Stars: 0 | Forks: 0
# kameHunt
**一款用于 CVE 研究的 LLM 辅助变体分析引擎。** kameHunt 接收一个
已知的漏洞模式,在公开的开源代码中搜索其特征,然后——这是核心重点——**验证并对原始命中结果进行排序**,从而让你获得的是少量高价值、可人工验证的线索,而不是模式匹配器产生的噪音。
由 **Abdullah Kareem** ([@CyberKareem](https://github.com/CyberKareem)) 创建。是 [kameRules](https://github.com/CyberKareem/kameRules)(强化模式源)和 [kameReport](https://github.com/CyberKareem/kameReport)(协调披露输出)的配套项目。
## 足以证明该工具价值的关键数据
对于 IDOR 这类漏洞,原始的模式匹配大多是误报;kameHunt 只有在其验证阶段成功将噪音转化为有效信号时,才体现出它的价值。因此,验证质量**就是**产品本身,且它是被实际测量出来的,而非凭空断言。在内置的 `authz_idor` benchmark(`python bench/run.py`)上:
| 条件 | 误报率 | 召回率 | 精确率 |
|-----------|--------------------:|-------:|----------:|
| **(a) 原始 Semgrep 模式** | 100% | 100% | 51% |
| **(b) + 启发式算法** | 42% | 95% | 70% |
| **(c) + 启发式算法 + LLM** | **11%** | **100%** | **91%** |
客观地解读,这说明了两件事:
1. **启发式算法以极低的成本完成了大部分清理工作**——在不调用模型的情况下,误报率从 100% 下降到了 42%——但它的处理比较生硬,牺牲了一点召回率(95%):有几个真正的 bug 被漏掉了,因为一个具有误导性的 token 看起来像是一个防护措施。
2. **LLM 阶段物有所值。** 我们*特意*将其单独展示。它进一步降低了误报率(42% → 11%),**并且**恢复了启发式算法牺牲掉的召回率(95% → 100%),因为它能读懂正则表达式无法识别的防护措施——并且它能分辨出一个看起来像“防护措施”的 token 实际上只是一行日志。在这次包含 39 个样本的运行中,该成本为 34 次 LLM 调用。
3. **存在一个客观的上限。** 即使经过 LLM 处理,仍有两个安全样本被误报,因为它们的安全性存在于工具展示给模型的*代码窗口之外*(在别处定义的 helper,或在上游挂载的 middleware)。工具在这里做了正确的处理——它没有假装自己很确定;而是将它们作为*未验证的线索*输出供人工检查,这正是其约定所在。
### 如何客观看待这些数据(请务必如此)
- 该语料库是**合成的且具有对抗性**:所有 19 个“安全”样本都是专门为了**触发原始模式**而设计的近似错误,因此 100% 的基准误报率是人为构建的——它实际上是*原始命中结果的精确率*,而不是对现实世界基准率的声明。这正是关键所在:它对*验证器*进行了压力测试。
- 它**规模很小**(20 个漏洞样本 + 19 个安全样本)且只针对**一类 bug**。它证明了前提,但不是一个排行榜。
- **提交的 LLM 结论是由 Claude**(该工具的默认模型)生成的,它完全根据工具发送的代码片段+上下文对每个模糊线索进行了分类,并且是确定性重放的,因此运行 benchmark 不需要 API key。可以使用 `--llm live` 针对线上模型重新生成它们(见下文)。其来源记录在 [`bench/_author_transcript.py`](bench/_author_transcript.py) 中。
如果你将自己真实的、已公开的案例添加到 `bench/corpus/authz_idor/` 中,数据会发生变化——我们鼓励这样做(`bench/README.md`)。
## 针对真实已公开 CVE 的现场测试(最真实的部分)
上面的 benchmark 测量的是针对用 kameHunt 自己的“方言”编写的语料库的*验证器*。为了验证它是否能在接触真实代码时保持效果,我们将 kameHunt 在处于预补丁提交状态的 **7 个真实、已公开的 npm 访问控制 CVE** 上进行了运行。
**首次运行:捕获 0 / 7。** 每个 bug 都是真实且经过确认的——而且每一个都是*结构性*的遗漏。最初的模式仅针对 Express + Mongoose/Prisma,而真实的 sink 是 Next.js 路由处理器、Hono 路由、`createAuthEndpoint` 和原始 SQL。检测根本没有触发,因此备受赞誉的验证器也没有运行。**对真实代码的召回率为零**——这正是 benchmark 整洁的数据所掩盖的局限性。
随后,检测层被**扩展到了现代惯用法**——自定义的 `*ById` repository、框架的 `ctx.body`/`ctx.query`、Hono 的 `c.req.param`、Next.js/Remix app-router 处理器、没有租户列的原始 SQL `WHERE id = ?`,以及 fail-open 的所有权反模式 `if (X && X !== Y) deny`:
| CVE | 项目 | 真实 sink 形态 | 之前 | 之后 | 线索 / 噪音 |
|-----|---------|-----------------|:------:|:-----:|:-------------:|
| GHSA-vjc7 | 9router | Next.js handler → `getProviderConnectionById(id)` | 遗漏 | **捕获** (0.75) | 7(几乎全是真实的未授权) |
| GHSA-fmh4 | better-auth | `adapter.findInvitationById(ctx.body…)` | 遗漏 | **捕获** (1.0) | 1 个真实对应 10 个 |
| GHSA-j8v8 | better-auth | fail-open check `provider.userId && provider.userId !== userId` | 遗漏 | **捕获** (0.6) | **1(干净)** |
| GHSA-382c | gittensory | 缺少*具名*防护的 Hono 路由 | 遗漏 | **捕获** (0.75) | 26(有噪音) |
| GHSA-j6r7 | n8n-mcp | 原始 SQL `WHERE id = ?`,自定义 repo | 遗漏 | **捕获** (0.56) | 41(噪音很大) |
| GHSA-g6g7 | 9router | 缺少 authz(+ 命令注入) | 遗漏 | 部分 | authz 路由被标记;cmd-injection 不在此类别中 |
| GHSA-2cf7 | n8n-mcp | 在已经有 scope 列的查询中出现 fail-open 的租户*值* | 遗漏 | **遗漏** | — |
**真实 CVE 的召回率:0/7 → 2/7 → 6/7。** 但请阅读下面剩下的两点诚实告诫,因为它们比单纯的数字更重要:
1. **最后一个(GHSA-2cf7)确实无法通过模式匹配捕获,我没有作弊。** 它的查询*已经包含*了 `instance_id` 列;bug 的原因在于当租户上下文不完整时,scope 推导的 helper 返回空值,导致传入的值为 `''`(fail-open)。这里不存在可以匹配的错误 *sink*——只有一个错误的*值*,这需要数据流/语义分析。要匹配它,就必须硬编码这个仓库的函数名,也就是在测试集上作弊,而避免这种行为正是本项目的初衷。
2. **召回率是用精确率换来的,这种权衡在表格中清晰可见。** 干净的胜利是 `j8v8`(1 条线索)和 `vjc7`(7 条线索,针对的是一个*完全*未经身份验证的 API——请记住,这是召回,不是噪音)。但是 `gittensory`(26 条)和 `j6r7`(41 条)几乎在每一个路由/每一个 by-id SQL 查询上都会触发,因为模式无法区分多租户表和单租户表,也无法区分需要 authz 的路由和公开路由。这种区分正是 **LLM 分类阶段**的工作——由于没有 API key,它无法在这里运行,所以这些噪音数据是**纯启发式的**,而这正是 `--llm on` 旨在减少的情况。
**结论:** 诚实且广泛的扩展将真实世界的召回率从 0 提高到了 6/7,明确指出了模式匹配在结构上无法触及的那一个 bug,并表明了在干净的案例之外,召回率的提升完全依赖于 LLM 阶段才具有可用性。请将此试验视为验收测试——每当模式发生变化时,请重新运行它(克隆 `bench/` 或使用你自己的代码)。
## 架构:获取 -> 检测 -> 验证 -> 报告
```
patterns/ (broad, high-recall Semgrep nets)
│
acquire ─────────────▼────────── detect ─────────── validate ─────────── report
local paths / repo run Semgrep, heuristics.py ranked UNVERIFIED
list (default) wrap each hit (reachability, leads: JSON + Markdown
OR GitHub code with tree- untrusted-input, + kameReport-compatible
search (subset) sitter/window guard-on-path) YAML in findings/unverified/
context scorer.py routes
ambiguous hits to
llm.py (optional)
```
- **`validate/` 是核心。** `heuristics.py` 根据可达性、不受信任的输入以及是否缺少租户范围/所有权/授权防护来对每个命中结果进行评分。`scorer.py` 免费丢弃确认为安全的命中结果,保留确认为有害的结果,并将处于模糊中间地带的结果发送给 `llm.py`。`llm.py` 会向模型请求一个保守的“是/否/可能”的结论,并建议一个人工测试方案。
- **模式被刻意设计得很宽泛。** 它们是 benchmark 测量的*原始基准*;确保精确率是验证阶段的工作,而不是模式的责任。(这些签名的强化且内置防护的版本存放在 `kameRules` 中。)
## 安装与使用
```
pip install -r requirements.txt # semgrep, pyyaml, requests
```
### 本地模式(默认模式,也是值得信赖的模式)
```
# 搜寻克隆/已授权的代码库,仅使用启发式方法
kamehunt hunt --pattern authz_idor --repos /path/to/repo --out leads.md
# 添加 LLM 分类(需要 ANTHROPIC_API_KEY),导出准备披露的线索
export ANTHROPIC_API_KEY=... # optional; without it, falls back to heuristics
kamehunt hunt --pattern all --repos repos.txt --llm on \
--export-findings ./out --out leads.md
```
### GitHub 模式(针对候选子集,而非详尽扫描)
```
export GITHUB_TOKEN=... # required; requests are authenticated + rate-limited
kamehunt hunt --pattern authz_idor --source github --max-repos 20 --out leads.md
```
### 其他命令
```
kamehunt list-patterns # the seed pattern library and its CVE references
kamehunt benchmark # run bench/run.py
kamehunt benchmark -- --json # machine-readable summary
```
针对线上模型复现 benchmark 的 LLM 条件:
```
kamehunt hunt --pattern authz_idor --repos bench/corpus/authz_idor --llm on --out /tmp/x.md
```
## 种子模式(两类,映射到已披露的 CVE)
| Bug 类别 | 模式 | 特征 | CWE | CVE |
|-----------|---------|-----------|-----|-----|
| `authz_idor` | `idor-db-lookup-by-request-id` | `findById(req.params.id)`, `findOne({_id: req.params.id})`, Prisma `findUnique` | CWE-639 | CVE-2026-59979 |
| `authz_idor` | `route-auth-without-authz` | 带有仅身份验证 middleware 的状态更改 Express 路由 | CWE-862 | CVE-2026-59979 |
| `authz_idor` | `idor-generic-by-id-lookup` | 自定义 `*ById(ctx.body.id)` repos, Hono `c.req.param` (非 ORM) | CWE-639 | CVE-2026-59979 |
| `authz_idor` | `nextjs-route-handler-idor` | 导出的执行 by-id 查找的 `GET/POST/…` app-router 处理器 | CWE-862 | CVE-2026-59979 |
| `authz_idor` | `idor-raw-sql-by-id` | 没有租户/所有者列的原始 SQL `WHERE id = ?`(手写数据层) | CWE-639 | CVE-2026-59979 |
| `authz_idor` | `web-route-request-param-no-authz` | 通过路径参数读取资源的 Hono/Koa 路由处理器 | CWE-862 | CVE-2026-59979 |
| `authz_idor` | `failopen-ownership-check` | `if (X && X !== Y) deny` — 当所有者字段为 null 时 fail-open | CWE-863 | CVE-2026-59979 |
| `deserialization` | `python-unsafe-deserialization` | `pickle.loads(...)`, 不带 `SafeLoader` 的 `yaml.load` | CWE-502 | CVE-2026-59889 |
| `deserialization` | `node-serialize-unserialize` | `node-serialize` `unserialize(...)` | CWE-502 | CVE-2026-59889 |
在下面的[现场测试](#field-test-against-real-published-cves-the-honest-part)表明仅针对 Express+Mongoose 的模式遗漏了所有真实的 CVE 之后,除了最初的两个模式外,又添加了五个宽泛的 `authz_idor` 模式。最后三个模式是高召回率/高噪音的,它们在精确率上依赖于 LLM 阶段(参见现场测试中的告诫)。
范围说明:kameHunt 有意发布了**两类**模式,并在**一类**(`authz_idor`)上证明了 benchmark。验证质量很重要;模式的广度并不重要。只有在核心循环和 benchmark 保持稳健之后,才应添加更多模式。
## 扩展
- **新模式:** 将一个 Semgrep YAML 放入 `kamehunt/patterns//` 中,并包含 `metadata.bug_class` `references`。`registry.py` 会自动加载它。
- **需要定制验证的新 Bug 类别:** 在 `kamehunt.validate.heuristics.evaluate` 中添加一个分支。这就是全部的配置过程。
- **真实的 benchmark 案例:** 在 `bench/corpus/authz_idor/{vulnerable,safe}/` 下添加 `.js` 文件。
## 测试
```
pytest # all offline: no GitHub token, no API key (LLM mocked/replayed)
```
CI ([`.github/workflows/ci.yml`](.github/workflows/ci.yml)) 会在每次 push 时安装 Semgrep 并运行 benchmark + 单元测试。
## License
[MIT](LICENSE) © 2026 Abdullah Kareem ([@CyberKareem](https://github.com/CyberKareem))。
请仅在你拥有或被授权审查的代码上使用它。
标签:DLL 劫持, 云安全监控, 变体分析, 大语言模型, 恶意代码分类, 逆向工具, 静态分析