2026-09-16 · 29 min read

拆 6.3 万 star 的 Strix:AI 渗透测试最难的不是找到漏洞,是承认自己没找到

这是我拆的第五个仓库,也是第一个「会动手打东西」的——AI 自主渗透测试工具,6.3 万 star。但真正让我停下来的是一个不到 300 行的技能文件:反证与结案纪律。它规定每个候选漏洞只能以三种状态之一结束,没有第四种,「我往下看了」不算其中一种。而要判一个候选「安全」,你必须能说完一句话:因为某个具体位置的某个具体控制,在攻击者可达的每一条路径上、在危险动作之前生效。说不出这句话,你就不能标 ruled_out——那是把「我没证明出来」伪装成「我证明了没有」,而真实漏洞就是这样被漏掉的。

A teardown of usestrix/strix, an autonomous AI pentesting tool with 63k stars. The most interesting part is not the hacking, it is a sub-300-line skill file on counterevidence and closure discipline: every candidate must end in exactly one of three states, there is no fourth, and 'I moved on' is not one of them. To call a candidate safe you must be able to finish the sentence naming the specific control, at a specific location, that runs before the sink on every attacker-reachable path. If you cannot finish that sentence, you are not allowed to mark it ruled_out — that is how a proof gap gets mislabelled as safety, and how real vulnerabilities get missed.

拆 6.3 万 star 的 Strix:AI 渗透测试最难的不是找到漏洞,是承认自己没找到

这是第五个仓库。前四个分别是封面 Skill、diagram-design、hypit 视频编译器、agent-skills。

这一个跟前面四个不太一样:它是唯一一个会真的动手攻击东西的。

usestrix/strix——62,913 star,Python,Apache-2.0,2025 年 8 月建仓,13 个月做到这个数。定位一句话:自主 AI 渗透测试 agent,像真黑客一样动态运行你的代码、找到漏洞、并用真实的 PoC 验证它们。

我原以为要写的是"AI 会打洞了"这类东西。读完发现真正值得写的是另一件事:它花了大力气防止 Agent 骗自己。

主要作用

Strix 是给开发者和安全团队用的自主渗透测试 Agent:它在一个 Docker 沙箱里拿到完整的攻击工具链,自己侦察、自己构造利用、自己验证,最后输出带 PoC 的漏洞报告——并且能生成补丁。

四个主要用例:应用安全测试、快速渗透测试(小时级而非周级,带合规报告)、漏洞赏金自动化、CI/CD 集成(在 PR 上拦截不安全代码)。

规模数据:

项数字
Star / Fork62,913 / 6,879
建仓2025-08-05(13 个月)
文件数597
贡献者69 人(主力 0xallam 559 次提交)
发布29 个版本,v0.3.1 → v1.6.2(2026-09-05)
内部知识库76 个技能文件(约 690 KB)
对外技能9 个(给 Claude Code / Cursor / Codex 用)
沙箱Docker(containers/Dockerfile,11.7 KB)
LicenseApache-2.0

工具链是真的专业级:Caido 做 HTTP 拦截代理、Playwright 驱动浏览器测 XSS/CSRF/点击劫持、交互式 terminal、Python 沙箱写 PoC、Nuclei / semgrep / ffuf / httpx / katana / naabu / nmap / sqlmap / subfinder 负责侦察与扫射。输出里带 findings.sarif(SARIF 2.1.0)和 vulnerabilities.json,无头模式退出码分三档:0 干净、1 致命错误、2 发现漏洞。

用起来就三行:

bash
curl -sSL https://strix.ai/install | bash
export STRIX_LLM="openrouter/z-ai/glm-5.3"
strix --target ./app-directory

这些是外壳。 我要写的是壳下面的东西。

它的核心不是自动化,是「结案纪律」

它的内部技能库里,analysis/ 目录下有一个文件叫 counterevidence.md,不到 300 行。开头两句就把整件事说清楚了:

证明一个 bug 是真的,只是工作的一半。另一半是证明一个候选不是真的——而误报和漏报都出在这一半上。

这个技能管的是你怎么结案。适用于你打开的每一个候选——不管它来自扫描器、代码阅读、爬虫,还是一个直觉。

三个状态,没有第四个

你打开的每个候选,最终只能落在这三个状态之一。没有第四个状态,而且"我往下看了"不算其中一个。

① confirmed(已确认)——你有可工作的 PoC;白盒情况下,你有一条完整的 source → control → sink → impact 链路,加上这条路径可达的证据。

② ruled_out(已排除)——注意这里的准入条件:

你能指名让这段代码安全的那个具体控制、在具体位置,并且你检查过这个控制确实在攻击者路径上执行。"指名"的意思是你能用具体细节补完这个句子:

"这是安全的,因为 <控制> 在 <文件:行号 或 观察到的行为> <做了什么>,在 <sink> 之前,在攻击者可达的每一条路径上。"

如果你补不完这个句子,你就不在 ruled_out。

③ open_proof_gap(未闭合的证明缺口)——候选是成立的,你没能确认它,而你也指不出一个能排除它的控制。这是一个正当的、预期之中的结果。

然后它写下了这个设计存在的唯一理由:

它要防的失败模式是:Agent 读了代码,感到不确定,然后悄悄把候选关掉了。 那就是一个 open_proof_gap 被错标成了 ruled_out——而真实漏洞就是这样被漏掉的。

这段我需要读两遍。 因为它识别出的不是一个技术缺陷,是一个心理动作:不确定 → 沉默地结案。而在一个报告只陈列"发现了什么"的系统里,这个被悄悄关掉的候选不留任何痕迹——它不会出现在报告里,它变成了"这里没扫出东西"。

什么不能排除一个候选

然后它列了一张清单,每一条都是"听起来很像理由、但都不足以成立"的说法:

① 对库或工具函数的笼统信任。

"它用了知名的消毒器 / 框架会转义这个 / ORM 会处理"——这不是反证。 你必须确认那个调用、在那些参数下、在那个上下文中。转义助手是上下文相关的:HTML 转义器在 JS 或属性上下文里什么也不做,SQL 标识符引号器不是值引号器,路径拼接器不是包含性检查。

② 跑在另一条路径上的控制。 中间件、装饰器、守卫保护了常规路由,不等于保护了兄弟路由、内部调用方、批处理/异步任务,或是一个别名 admin 入口。

③ 在错误时间点运行的控制。 重定向之前的校验、路径已经实体化之后的规范化、抽取之后的包含性检查、对象已经取出并返回之后的属主检查——这些是顺序 bug,不是控制。

④ 可能 fail-open 的控制。 写在吞掉异常的 try/except 里的加固开关、调用方可以覆盖的解析器特性、由调用方提供的工厂或配置对象、默认为空的允许列表。

⑤ 安全的兄弟实例。 一个调用点守卫正确,不能说明同一个 helper 的其他调用点。绝不能让一个安全实例关掉一个有漏洞的实例。

⑥ 信息缺失。 "我没找到调用方"、"我判断不了它有没有部署"、"我起不来这个服务"——每一个都是 open_proof_gap,不是安全的证明。

紧跟着那句话我想抄进自己的原则里:

缺失的证据就是缺失的证据;它不是"不存在的证据"。

⑦ 难度。 "构建失败了"、"我没有那个凭证"、"服务网格不可用"——是记录证明缺口、然后去做下一个候选的理由,不是把它标成干净的理由。

⑧ 运维可配置性。 "运维可以配一个过滤器"、"这是文档里的特性"、"默认是关的"——不是控制。实际发出去的东西和实际可达的东西才算数。

⑨ 它是内部的。 这条的处理最见功力:

仅限内部、仅限管理员、仅限已认证,降低严重性——但不让这个发现变得不真实。降级它;不要删掉它。

我读到这里的时候意识到:这整张表是一张"反合理化表",只是对象从程序员换成了安全结论。(这正好接上上一篇 agent-skills 里那张「agent 的借口 vs 现实」——同一个机制的第二次出现,而这次它是要害。)

那什么能排除?

它也给得很具体,而且要求可观察:

  • 你执行了攻击,它可证明地失败了,而且你理解它为什么失败(不是"响应是个 403")
  • 你能指出控制、指出位置,并证明它在每一条攻击者可达的路径上、在危险动作之前运行、且没有 fail-open 分支
  • sink 在这个上下文里确实不危险,而你能说出是什么让它失去活性
  • 输入不是攻击者可控的,而且你追踪到了可信来源,而不是假设

还有一条我很喜欢的技术细节——负向对照:

负向对照能让 ruled_out 强得多:发出如果 bug 为真就应该生效的那个 payload,证明它被拦住了,同时一个无害变体能通过。这才区分开了"控制确实生效"和"这个端点因为无关原因坏了/不可达"。

覆盖面账本:只记发现的扫描,读者无法知道什么被审过

这是我在这篇文章里最想推荐的第二个设计。

它说:

一次只记录发现的扫描,无法告诉读者哪些地方被审过并排除了——这让每一个"干净区域"和"没去过的区域"变得无法区分。

所以每个被评估的界面都要有一条 record_coverage 记录,映射是明确的:

结案状态记成
confirmedreported
ruled_outruled_out,evidence 里必须放那个被指名的控制(说不出名字就不是 ruled_out)
open_proof_gapneeds_follow_up,evidence 里放具体的缺口
彻底测过但一无所获no_issue_found
这个风险根本不适用于此界面not_applicable,附理由

而这个账本是跨所有 Agent 共享的,所以还有一条"结案不是终局"的规则,我觉得写得非常漂亮:

别人留在 needs_follow_up 的一条记录,是一个邀请:如果你有他们缺的凭证、起得来的服务、或可达性证明,用 update_coverage 去移动他们那一条,而不是并排记一条新的。 这个方向也是双向的——一条 ruled_out 如果它指名的控制并不覆盖你刚找到的那条路径,就要退回 reported 或 needs_follow_up。

上一次的状态会作为历史保留,所以修正记录不花任何代价,而留着一个错误记录要花掉一个发现。

"修正记录不花代价,留着一个错误记录要花掉一个发现。" 这句话值得单独拎出来。团队协作里最难推动的动作其实就是"去改别人已经写下的结论",它用一句成本对比把这件事变成了显然划算的选择。

(我顺手去核了实现:strix/report/coverage.py 里确实有 OUTCOME_LABELS 枚举,strix/tools/coverage/tools.py 里 update_coverage 和 list_coverage 都是真工具,不是文档里的说法。还有个很细的工程细节注释——技能文件名和渗透测试员写漏洞类别的方式不一样,比如带着 path_traversal_lfi_rfi 这个技能的 agent 会写成 "Path Traversal",所以它维护了一张措辞映射表;注释里写着"新增技能最多是匹配得严格一点,绝不会崩——但请补一条,因为一个错误的缺口会在报告里断言不真实的东西"。)

严重性校准:严重性是结论,不是开场立场

analysis/severity_calibration.md 是第二个被无条件注入每个 Agent 的技能。它的第一条规则就是时序:

在确立了可达性、并且跑完反证之后,才校准严重性——永远不要在此之前。严重性是一个结论,不是一个开场立场。

然后它给了一个我认为可以直接当口头禅用的检验:

在给任何东西打 high 或 critical 之前,先问:

这会在严肃的审计或漏洞赏金分级中,被一家拿声誉做抵押的公司接受为 high/critical 吗?

如果诚实的答案是"除非你接受一串假设",那就不是 high。给你证明了的那个弱点定级,不要给你能想象到的最坏情况定级。

它对每一档都给了具体判据(critical 保留给"现实攻击者获得决定性控制或大规模数据访问,且有证据";internal-only 这类约束把它降档而不是删掉)。最后那张验收清单里,有一条是:

  • 你会在客户复盘会上为这个定级辩护。

结尾那段是我见过的对"人 vs 工具"关系最清醒的处理:

严重性仍然来自 CVSS 向量——这个评分标准决定的是"哪个向量是诚实的"。 当你的直觉定级和算出来的 CVSS 严重性不一致时,重新检查指标:通常是 privileges_required、attack_complexity 或影响三元组里有一个被乐观地设了。去修那个指标,不要去覆盖那个结果。

"当人和工具不一致时,改你的输入,不要改输出。" 这句话可以用在任何有量化方法的地方。

未验证的修复比不修更糟

第三个我印象深的技能是 analysis/fix_verification.md(只有白盒 Agent 能加载,因为只有它们能给出一条可一键应用的修复)。

当你把一个 fix_before / fix_after 附到代码位置上时,你写的不再是建议。你写的是一段审查者可以一键应用、直接进入他们代码库的建议块。

未验证的修复比没有修复更糟:它把你的不确定性转换成了他们已合并的提交。

它的判定顺序也就成了优先级,而且有一条不可越界的规则:

  1. 当前状态被正确分类(有漏洞 / 已安全 / 未证明)
  2. 修复完整地关闭了破损的安全边界
  3. 合法行为和兼容性被保留
  4. 相关仓库检查通过
  5. 改动遵循仓库自己的约定
  6. 补丁只包含 1–5 要求的东西

永远不要用前者去换后者。 一个更小、更整齐、更地道的补丁,如果留着边界开着,它就是失败的。"最小"的意思是满足以上全部要求的、最小的仓库原生改动——不是行数最少。

最后那句我觉得是整篇文章里最锋利的一句技术判断:"最小"不是"行数最少"。 前者是约束满足问题,后者是一个很容易伪装成前者、但会悄悄优化错目标的代理指标。每一个"精简"过的人都知道这个区别。

工程:76 个技能、多 Agent 图,和一个诚实的基准

回到工程规模。

内部知识库 76 个文件、约 690 KB,按类别组织:

类别数量例子
vulnerabilities29agentic_system_security, argument_injection, authentication_jwt, browser_security, business_logic…
tooling13ffuf, httpx, hurl, hypothesis, katana, naabu, nmap, nuclei, python, semgrep, sqlmap, subfinder
technologies7supabase, firebase, grafana_prometheus, llm_applications
analysis4counterevidence, severity_calibration, fix_verification, source_aware_discovery
cloud4—
frameworks4Django / Express / FastAPI / Next.js
scan_modes4—
custom4—
coordination2root_agent, source_aware_whitebox
protocols2—
reconnaissance2—

最关键的机制在这里:每个 Agent 创建时最多加载 5 个针对性技能(create_agent(task=..., skills="authentication_jwt,business_logic")),但注入顺序里有一批是无条件的。我去读了 strix/agents/prompt.py 里 _resolve_skills() 的注释,它把顺序写成了带理由的清单:

  1. 调用者要求的,按顺序
  2. scan_modes/<mode>(总是),diff 作用域时再加 scan_modes/diff
  3. tooling/agent_browser(总是——每个 Agent 都有 shell 和 agent-browser)
  4. tooling/python(总是——Python 通过 exec_command 跑,沙箱脚本可以 import caido_api)
  5. analysis/counterevidence 和 analysis/severity_calibration(总是——结案纪律和严重性标准适用于每一个能打开/关闭候选、或提交报告的 Agent)
  6. coordination/root_agent(只有 root)
  7. 白盒专属技能

第 5 条是整个设计的支点。 反证和严重性校准不是"一个可以按需加载的安全技能"——它们被无条件注入每一个 Agent 的系统提示。理由写得也很清楚:任何一个能开/关候选的 Agent,都必须受这套纪律约束。

这跟我前几篇里看到的东西是同一个思路的不同实现:diagram-design 用 checker 强制规则、agent-skills 用反合理化表堵借口、Strix 干脆让纪律成为上下文的一部分,而不是一个可选项。

多 Agent 编排方面:它叫 "Graph of Agents"——root Agent 通过 create_agent 派生出侦察/利用/后渗透方向的专家 Agent,工具集里有 send_message_to_agent、wait_for_agents、view_agent_graph、stop_agent。重点是它们共享那个覆盖面账本——这也是为什么前面那条"去移动别人的记录,而不是并排记一条"的规则是必要的:多 Agent 并行工作时,同一个界面可能被两个 Agent 都碰过。

沙箱是 Docker 容器(containers/Dockerfile + entrypoint),strix/runtime/ 下有 docker_client.py、session_manager.py、还有 Caido 的 bootstrap/handle——它把"代理"这件事当成基础设施来管,不是当成一个工具调用。

基准测试:96%,以及一个我该诚实指出的地方

它用 XBEN(XBOW 的 104 道 web 安全挑战,CTF 格式,要拿到隐藏 flag)来做评测。Strix v0.4.0 在黑盒模式下解出 100/104 = 96%:

难度解出成功率
Level 1(易)45/45100%
Level 2(中)49/5196%
Level 3(难)6/875%

平均解题时间约 19 分钟,100 道总计成本约 $337(约 $3.37/道)。

这个数字是真的、也是可复现的(挑战是第三方的 XBEN)——但我要指出两件它没写在同一行的事:

① 结果是 v0.4.0 测的,而项目现在是 v1.6.2。 中间隔了 13 个 minor 版本——包括这次评测之后加进来的新能力(比如 now 的 Cloud、MCP 支持、v0.4.0 之后才有的东西)。所以那个 96% 不是一个当前版本的声明。

② 评测代码仓库自 1 月起没再动过。 指向的 usestrix/benchmarks 建仓和最后一次推送都是 2026-01-23(21 star)。挑战是 XBOW 的(第三方,这是加分项),但跑测的 harness 和公布的结果是厂商自己的,而且这份结果已经八个月没更新了。

我写这段不是要挑刺。"第三方挑战 + 自建 harness + 结果长期未更新"是这类型项目里非常常见的形态,而且它至少把挑战来源、版本号、平均耗时、总成本都写出来了——比只写一个百分数就完事要诚实得多。但读者该知道那个百分数属于哪个版本。

五个仓库,一条越来越清楚的线

拆完这个,我发现前四篇里那条线更清楚了:

仓库它要防的失败它给出的机制
夏林果封面用户不知道怎么描述一个不把人问烦的表单
diagram-design审美没判据40 行 Taste Gate + 几何验证
hypit时间轴不可维护把秒/帧从作者语言里删掉
agent-skills规则被绕过、失效反合理化表 + 三层评测 + 棘轮
StrixAgent 骗自己(两个方向)三态结案 + 完句测试 + 共享账本

而有一条我觉得是它独有的、前四个都没有的东西:它的评测有外部真值。

diagram-design 的 checker 是自己写的,agent-skills 的 eval 也是自己写的——它们都在自己给自己出题。安全是少数几个"世界会直接告诉你答案"的领域:你要么拿到了那个 flag,要么没拿到。所以那 96% 是有分量的,因为它不是自评。

但更有意思的是:也正是这个领域,假阴性最贵。 别的领域里"我没测出来"是个可以接受的结论;这里"我没测出来"可能就是一个正在被利用的 RCE。

所以它的答案不是"更用力地找",而是"更严格地结案"。 在假阳性会消耗信任、假阴性会漏掉漏洞的双向压力下,它选择把最大的一份力气花在**规定"什么才算证明"**上——包括证明"这里没问题"。

回到那句我最想抄走的话:

缺失的证据就是缺失的证据;它不是"不存在的证据"。

怎么用

装成给编程 Agent 用的技能(和上一篇 agent-skills 同一个协议):

bash
npx skills add usestrix/strix

装的是 9 个技能:跑渗透测试、修漏洞并复测、加 CI 扫描,覆盖代码、Web 应用、API 和 OWASP Top 10。

命令行直接用:

bash
curl -sSL https://strix.ai/install | bash
export STRIX_LLM="openrouter/z-ai/glm-5.3"
export LLM_API_KEY="<你的 key>"
 
strix --target ./app-directory                    # 扫本地代码
strix -n -t ./ --scan-mode quick --max-budget 10  # 无头模式(服务器/CI 一律用 -n)
strix view                                        # 本地看结果面板

要点:

  • 需要 Docker 在跑。 第一次运行会自动拉沙箱镜像;结果落 strix_runs/<run-name>/
  • 无头模式退出码:0 干净、1 致命错误、2 发现漏洞(可以直接用来卡 CI)
  • 按接口测更准:strix --target ./openapi.yaml --target https://api.your-app.com——指向 API 契约,它测每个声明的端点,不用靠爬虫去发现
  • 支持 OpenAPI/Swagger/Postman、MCP 服务器接入、以及用 ChatGPT 订阅跑(strix auth login chatgpt + STRIX_LLM="chatgpt/gpt-5.4")
  • 只提供能力,不提供授权:README 结尾有一段加粗警告——只能对你拥有或持有明确书面许可的系统运行,并留在约定范围内。未经授权的测试在大多数司法辖区是违法的。

仓库地址:github.com/usestrix/strix · 文档 docs.strix.ai


最后说一句我自己的感受。

拆这个仓库的时候,我原本准备写下"AI 现在会打洞了"这样的判断。但真正让我停下来的,是那个不到 300 行的文件在反复做同一件事:把"我感觉没问题"和"我能证明没问题"彻底分开。

它当然是为了安全场景写的。可"读了代码、感到不确定、然后悄悄把候选关掉"这个动作——在任何需要下判断的场合都存在。 我自己每天就在做这件事:一次检索没找到,就说"没有这方面的资料";一个方案想不出问题,就说"这个是可行的"。两次都是把 open_proof_gap 写成了 ruled_out。

区别只在于,它的错了会漏掉一个漏洞,我的错了会给你一个不准确的答案。

而两者的解法是同一个:说不出那个句子,就别打那个标签。