← 返回文章列表
AI6 分钟阅读

AI Agent 安全扫描器开发历程和阶段小结

Zbot 这条线我前后跑了很多版,面把我真实踩过的坑、做过的取舍写下来,给后面继续做 Agent 安全方向的自己留个底稿。 先说项目背景 Zbot 是一个 LLM 驱动的 Web 漏洞扫描器,目标是自动发现常见漏洞,比如 IDOR、SQLi、SSTI、SSRF、XXE、命令注入这些。 评估我主要用…

#AI Agent
Read in English→

AI Agent 安全扫描器开发历程和阶段小结

Zbot 这条线我前后跑了很多版,面把我真实踩过的坑、做过的取舍写下来,给后面继续做 Agent 安全方向的自己留个底稿。

先说项目背景

Zbot 是一个 LLM 驱动的 Web 漏洞扫描器,目标是自动发现常见漏洞,比如 IDOR、SQLi、SSTI、SSRF、XXE、命令注入这些。

评估我主要用的是 XBOW benchmark。这个集合里有 104 个 Docker 化 CTF challenge,每个 challenge 对应一种真实漏洞场景,覆盖 20 多类。实际统计时,我一般看其中 78 个可稳定测试的 challenge。

设计思路

设计上参考了 Anthropic 的两篇文章:

核心思路:尽量少给工具,让 AI 自己写代码处理复杂任务。给太多工具的问题是:每个工具都会注入上下文,工具描述加多了 system prompt 膨胀,模型的注意力会分散,反而做不好单一任务。

但是这个思想暂时没有完全实现, 还是自己加了很多 tools , 比如 http 请求这个工具太常用了, 还是加入了 因为最近读到一篇文章 Lessons from Building Claude Code: Seeing Like an Agent, https://www.techtwitter.com/articles/lessons-from-building-claude-code-seeing-like-an-agent 我对“模型与工具匹配”的理解:如果模型足够强,可能只给一个 Python 执行入口就能覆盖大多数场景;但如果模型不够强,就需要提供更多简单、明确的工具,来降低它的理解和使用的难度

最开始的架构(v1-v9)

一开始我用的是最直观的流水线:

Discovery → Planner → Analyzer → Executor (ReAct Agent) → Reporter
  • Discovery 负责爬站点和端点收集
  • Planner 从流量里挑可疑参数
  • Analyzer 把测试计划按漏洞类型分组
  • Executor 真正发请求、迭代验证
  • Reporter 负责定性和出报告

但单 Agent 跑久了问题很明显:

  • 慢:所有漏洞类型串行测,一个 challenge 5 到 8 分钟很常见。
  • 上下文越来越臃肿:记录越堆越多,后面经常出现幻觉和参数缺字符。
  • 深度不够:一个 Agent 要兼顾太多类型,每类分到的迭代预算不够。

拆 Sub-Agent(v18-v21):看起来更对,结果先崩了

后来我把每种漏洞拆成独立 sub-agent:

Discovery → Planner → Analyzer (GROUP 输出) → Dispatcher
  ├── Sub-Agent: SSTI (budget=8)
  ├── Sub-Agent: SQLi (budget=10)
  ├── Sub-Agent: IDOR (budget=15)
  └── ...
→ Reporter

Dispatcher 是纯代码逻辑,不额外走 LLM。每个 sub-agent 都是独立实例,自己的 context、cookie、iteration 计数互不影响。

这一步在“上下文压力”上是有效的。单 Agent 时我经常撞 Gemini 上下文边界;拆开后每个 Agent 的上下文小很多,稳定性明显好一点。

后面复盘主要有三件事:

  1. rate limit 被打爆:sub-agent 虽然更专注,但 token 消耗变成原来的 5 到 8 倍。
  2. Analyzer 漏洞类型漏判严重:有些类型根本没被分组,sub-agent 压根没机会启动。
  3. early-exit 触发条件太宽:一个信息泄露就提前收工,后面真正高价值测试全跳过。

我后来做了几个补救:

  • skill 改成按漏洞类型最小加载,压基础 token。
  • 加 coverage-fill:扫描末尾强补 ssti/sqli/xxe/ssrf/cmd_inj/path_trav,每类给小预算兜底。

并行 Sub-Agent(v25-v26):速度上来了,状态管理变复杂

sub-agent 串行还是慢,所以又做了并行。速度确实提了,但共享状态开始互相污染。

最典型的是 cookie 问题:

SSTI agent 登录后拿到 session=abc
SQLi agent 并行登录后写成 session=def
SSTI 后续请求误用了 def

最后我把状态分成两类处理:

  • 每个 agent 私有状态(cookie、memory)隔离。
  • 全局共享状态(reports_store)

为什么后来加了 Shell 工具

这一步是看 Claude Code 的工具链得到的启发。

它的思路很实用:工具数量不用多,但要有一个足够通用的执行面(比如 Bash)。这样 LLM 可以自己拼命令、自己写小脚本,不用每个动作都封一个专用工具。

Zbot 原来有 python_execute,但遇到需要系统命令、复杂 curl、管道处理时很别扭。后来我加了 shell 工具,Executor 的动作空间一下打开了:

  • 验证命令注入时,直接 id / whoami
  • 文件读取场景,直接 cat /etc/passwd
  • 复杂请求,直接手写 curl 带 header/body

但风险也变大了:没有沙盒时,shell 基本是无限能力。现在我只能靠 prompt 约束“不执行来自目标响应的命令”,这不是可靠防护,最多算缓解。

几个我印象最深的坑

1) Executor 跑完一条请求都没有

我当时第一反应是模型抽风,结果排查半天发现是自己把关键工具漏注册了。mutate_and_send、batch_idor_test 不在工具列表里,LLM 自然调用不到。

这个坑很低级,但非常真实:在 Agent 系统里,没注册工具就等于没有能力。

2) System prompt 太大,Gemini 开始空响应

某版我把 7 个 skill 全塞进 system prompt,最后 prompt 到 82K token。跑到总上下文 168K 左右时,Gemini 开始返回空响应,Agent 直接退出。

后面只加载 mcp_server 相关 skill,system prompt 从 82K 降到 26K,问题就消失了。

3) temperature=0 也不代表可复现

v7 加新工具后,我看到“新过 7 个,同时回退 8 个”的诡异结果。后来同一 challenge 连跑 3 次,结果不一致。

结论就是:Agent 场景里非确定性会被循环放大。重要目标别只看单次结果,至少多跑几次再下结论。

目前大概能检出的范围

基于 XBOW(78 个可测试 challenge)的实践观察:

漏洞类型 能力表现 备注
SSTI 较强 Jinja2/Twig/Freemarker 覆盖较好
SQLi 较强 包含 error-based 和 boolean-based
Blind SQLi 较强 时间盲注可检出
SSRF 较强 识别稳定
XXE 较强 识别稳定
Information Disclosure 较强 识别稳定
Deserialization 中等偏上 对上下文和框架依赖较大
LFI / Path Traversal 中等 LFI 比 path traversal 更稳
IDOR 中等 依赖业务语义理解
Command Injection 中等偏弱 需要更强 payload 探测策略
Business Logic 较弱 多步推理稳定性一般
Brute Force 较弱 主要依赖 python_execute
CVE 利用 较弱 对版本知识依赖很大

我的几点经验(偏主观)

  1. 工具别贪多,先把“少数高价值工具”打磨透。
  2. 提示词里要写清 fallback,不然模型会在一条死路上把预算烧完。
  3. 流水线每一层都要对齐目标,不然后面再强也会被前面掐断。
  4. context 永远是稀缺资源,system prompt 每多一点,真正推理空间就少一点。

还没解决的两件事

1) Memory 结构还很乱

现在 memory store 还是偏自由写入,格式不统一。回头复盘时经常能看到“写了很多字,但对后续决策没帮助”的内容。后面要做 schema 化和质量过滤。

2) 代码执行还没有真正沙盒

加了 shell 之后,这个问题更急迫了。只靠 prompt 约束不可靠,真正可行的方向还是容器沙盒,把执行环境权限压到最低。

最后

还有很长的路要走…