前言
我是一个极其普通的人。
虽然我是计算机科学专业,但毕业前的安全经验为零。去年这个时候(2025年4月),我的全部成果不过是侥幸向 KISA 提交了一个 Stored XSS。
然而,我利用 AI 在周下载量 5000 万次的 npm 包(TanStack Query)和 Apache Airflow 等大型开源项目中发现了漏洞,并成功获得了 CVE。
| CVE ID | 目标 | 漏洞类型 | 严重程度 |
|---|---|---|---|
| CVE-2026-26903 | TanStack Query | 无限递归导致的 DoS | Medium |
| CVE-2026-25604 | Apache Airflow AWS Auth Manager | Host Header 注入 | Medium |
这篇文章是写给那些在想我也能做到吗? 的朋友们——因为答案是是的,你也可以。
开始:在互联网上发现绿洲
始于Seokchan Yoon的一篇博客文章。
这篇文章我反复精读了无数遍。遇到不理解的地方就喂给 Claude 让它解释,如此反复。
借此机会,向Seokchan Yoon先生表达诚挚的感谢。
我做了什么,怎么做的?
工具:Claude Pro + Claude API
没有什么花哨的东西。一个 Claude Pro 订阅和 Claude API——仅此而已。
Step 1:构建扫描器
基于尹锡灿的文章,我指示 Claude 构建一个漏洞扫描器。从简单的结构(v1)开始,通过不断减少误报(False Positive),一路迭代到 v3。
让我惊讶的是,Claude 会自己升级自己。当我持续把 v1 的结果反馈给 LLM 时,它会说:”如果把检测逻辑改成这样,可以同时优化成本和误报率”——v2 和 v3 就是这么诞生的。
以下是 Claude 自行构建的 v1 到 v3 的内容。
v1:”找 bug 就行” —— 天真的第一版
“你是一名安全研究员。找出代码中的安全漏洞。”
这几乎就是整个系统提示词。架构也很简单:
git clone一个仓库- 将源代码文件切成 ~25K token 的块
- 每个块加上一个提示词发送给 Claude
- 解析 XML 结果
技术上来说,它能工作。问题是?它什么都报。一个接受 command 参数的库函数?”命令注入!”一个接受文件路径的开发者 API?”路径遍历!”一个设置数据库 URL 的配置选项?”SSRF!”
我拿几个仓库试了试,结果涌出了几百个发现。几乎全是垃圾。误报率大概超过了 95%。
这时Claude意识到:扫描器根本没有谁在调用这个函数?这个概念。它把每个参数都当成攻击者直接输入的。
v2:多阶段流水线 —— 加入一个怀疑论者
v2 的核心洞察是:不要只找 bug,还要尝试反驳它。
我把扫描器重构为多阶段流水线:
- Phase 1 — 侦察(Reconnaissance):不直接跳入漏洞分析,而是先绘制攻击面。HTTP 处理程序在哪里?
eval()和exec()等危险 sink 在哪里?哪些文件值得深入分析? - Phase 2 — 深度分析(Deep Analysis):只分析侦察阶段标记的文件。提示词要求具体的攻击向量——如果你不能一步步说明如何利用它,就不要报告。
- Phase 3 — 对抗性验证(Adversarial Validation):这才是真正的 game changer。我用不同的人设把每个发现再次发给 Claude:“你是一个持怀疑态度的安全审查员。你的任务是反驳这个发现。”它会检查:输入真的是用户可控的吗?有没有做过滤?这条代码路径在生产环境中真的可达吗?
我还加入了文件优先级评分系统。包含 HTTP 路由处理程序(app.get()、@PostMapping)和用户输入模式(req.body、request.form)的文件优先级提升,测试文件、配置文件和仅开发环境的代码则被自动过滤。
误报率显著下降了。但 v2 有一个无法解决的根本问题。
v3:信任边界分析(Trust Boundary Analysis)—— 突破口
在运行 v2 的过程中,我不断看到同样模式的误报:
“这个库有一个
execute(query)函数,不做参数化就执行 SQL——SQL 注入!”
但这个函数是一个库的 API。导入这个库的开发者自己选择传入什么。开发者是可信的主体。这不是漏洞,这是功能。
在扫描 npm 包和 Java 库时,这是最大的误报来源。于是我构建了 Phase 0:目标分类(Target Classification)。
在开始扫描之前,v3 首先回答这个问题:“这个代码库是什么?”
它将目标分为四种信任模型:
| 类型 | 谁是可信的? | 什么才算真正的漏洞? |
|---|---|---|
| APPLICATION | 用户不可信 | HTTP 输入 → 危险 sink |
| LIBRARY | 开发者可信 | 仅当外部数据在没有开发者介入的情况下到达 sink |
| FRAMEWORK | 插件开发者可信 | 仅当框架核心错误处理用户输入 |
| CLI_TOOL | 混合 | 处理的文件、网络响应 |
扫描器通过分析 package.json 字段(main、module、exports → 库)、代码模式(app.get()、req.body → 应用程序)和结构性指标来自动检测类型。
之后,所有后续阶段都使用这个信任上下文。侦察提示词对库和应用程序提出不同的问题。深度分析提示词包含针对每种信任模型的专用规则。关键的是——在库目标中,被标记为 requires_malicious_developer: true 的发现会被自动过滤。
找到两个 CVE 的正是这个版本。信任边界过滤器消除了噪音,不可信的终端用户输入真正到达危险 sink 的真实漏洞清晰地浮现出来。
实际上是怎么做的
大部分代码不是我自己写的。
我的工作流程是这样的:
- 反复阅读并理解尹锡灿的原始方法
- 向 Claude 详细描述我想要的东西——架构、阶段、提示词
- 审查输出——这是我的安全知识发挥作用的地方
- 在真实仓库上测试并分析失败原因
- 把失败案例反馈给 Claude:”这是误报。为什么会这样?怎么防止?”
- 迭代——v1 → v2 → v3 每一版都源于理解上一版为什么失败
我认为这里的关键在于:当 Claude 提出版本升级方案时,你要能判断它是否合理。如果我只是因为 Claude 说了就无脑照做,扫描器的性能反而可能会变差。
Step 2:扫描-> 审查-> 提交
在将扫描器迭代到 v3 之后,我根据扫描结果撰写了报告并提交。
我把热门开源项目——Spring、npm 包等——按下载量排序,然后逐个进行:git clone → 扫描 → 重复。总共扫描了 100 多个仓库,整个 2026 年 1 月都投入在这上面。
扫描器的原始输出(XML)不能直接当报告用。我把每个结果连同实际源代码一起发给 Claude 做最终审查——这是误报还是有效漏洞?我没有盲目信任自动化结果,而是把 LLM 当作二次验证员来使用。
对于通过最终审查的发现,我撰写报告并提交。报告撰写同样借助了 AI——我让 Claude 学习其他人的 CVE 报告并生成草稿。除了证据(PoC 截图、代码)之外的部分几乎都是 Claude 写的,我只负责审查。
结果
- 扫描的仓库:100+
- 提交的报告:4 份
- 获得的 CVE:2 个
- 耗时:约 1 个月(2026 年 1 月)
- API 费用:约 ₩70,000(约 ¥350),Claude Pro 订阅费另计
100 多个仓库扫描下来,提交了 4 份报告,2 份被认定为 CVE。命中率确实很低。
感悟:成为善用 AI 的人
我是一个在 AI 出现之前从未尝试过寻找 CVE 的人。那些前辈们到底用什么样的思维过程来发现漏洞,我完全摸不着头脑。
但在 AI 时代,我亲自证明了即使是像我这样的普通人也能获得 CVE。
说实话,既高兴又害怕——作为一个刚入行的新人,担心会被 AI 取代。
但经过一个月的实践,有一点我非常确定:
AI 并非万能。它只能发挥使用者本身能力的水平。
问题问得蠢,答案就蠢;问题问得聪明,答案就聪明。用过 AI 的人应该都能感同身受。
归根结底,AI 是工具,重要的是成为善于使用这个工具的人。
结语
我是一个阴差阳错比别人晚起步进入黑客/安全领域的人。毕业后才真正开始。
去年这个时候(2025年4月),除了凭运气向 KISA 提交了一个 Stored XSS 之外,几乎没有什么成果。此后,我主动拜访了各路高手,一起学习研究,在漏洞赏金方面也逐渐取得了一些成果。
今年年初(2026年1月)的目标是“无论如何一定要拿到 CVE!”——跌跌撞撞之中,竟然真的实现了。
希望这篇文章能对像我一样的普通人有哪怕一点帮助。
感谢您阅读到这里。