标题:OpenClaw Dashboard 通过 lastMessage 会话字段引发存储型 XSS
CVE ID: CVE-2026-66421
https://github.com/tugcantopaloglu/openclaw-dashboard
仪表盘着陆页上的会话表格显示每个代理会话的最后一条消息。该文本取自代理的对话记录,并通过 innerHTML 写入页面,且未进行任何转义。OpenClaw 是一个多渠道代理网关,因此消息文本可能来自任何能够与代理对话的人,例如聊天群组或 webhook。包含 HTML 载荷的消息会在管理员打开仪表盘时立即作为脚本在其浏览器中运行。
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N自首个公开版本起即存在。脆弱的渲染逻辑存在于最早的标签(v1.1.0)以及截至当前 main(d6198d0)在内的每个后续提交中。撰写本文时尚未修复。
攻击者是可以向代理会话记录中发送消息的一方。他们不需要仪表盘账户。在典型的 OpenClaw 部署中,这包括已连接聊天频道的成员,以及任何通过 webhook 将消息送达代理的来源,因此攻击者通常处于操作者的信任边界之外。
载荷存储在记录中,并在管理员查看会话页面时被渲染。该页面是默认着陆视图,并且会定时刷新,因此所需的唯一交互就是管理员加载他们平时使用的仪表盘。AT:P 反映了攻击者所依赖的一个条件:部署将他们的消息路由到仪表盘所显示的会话中。
第 1 步:从记录中提取消息文本。 服务器读取最近的一条消息并返回其文本,截断为 80 个字符,且不做任何净化处理:
// server.js:474-481
if (typeof msg.content === 'string') {
text = msg.content;
} else if (Array.isArray(msg.content)) {
for (const b of msg.content) {
if (b.type === 'text' && b.text) { text = b.text; break; }
}
}
if (text) return text.replace(/\n/g, ' ').substring(0, 80);
第 2 步:通过 sessions API 暴露。 getSessionsJson 将文本放入每个会话的 lastMessage 字段:
// server.js:538
lastMessage: getLastMessage(s.sessionId || key),
GET /api/sessions 将此数组返回给浏览器。
第 3 步:未经转义写入 DOM。 会话表格通过字符串拼接构建每一行,并使用 innerHTML 进行赋值:
// index.html:3758
const lastMsg = s.lastMessage ? s.lastMessage.substring(0, 60) + (s.lastMessage.length > 60 ? '…' : '') : '';
// index.html:3773
<div class="table-cell" style="..." onclick="toggleSessionExpand('${escapedKey}', event)">${lastMsg}</div>
lastMsg 被直接放入 HTML 字符串。此路径上没有任何编码步骤。
同一表格还以相同方式将 s.label(index.html:3770)和 s.key(index.html:3767)插值到行标记中。这些字段没有 60 字符上限,因此如果攻击者能够影响它们,将获得更大的载荷空间。
server.js:298 处的 Content-Security-Policy 允许内联事件处理程序(script-src 'self' 'unsafe-inline'),因此 onerror 处理程序会运行。
作为可以向代理发送消息的一方,发送一条文本为短载荷的消息, 使其适合 60 字符的限制,例如:
该载荷共 56 个字符,并像任何普通消息一样进入代理的会话记录。
确认服务器未经过转义地返回载荷:
curl -H "Authorization: Bearer ADMIN_TOKEN" http://TARGET:7000/api/sessions
受影响会话的 lastMessage 字段包含原始的 `` 字符串。
管理员打开仪表盘。会话页面是默认视图,因此无需额外导航。行被渲染,浏览器解析 `` 标签,onerror 处理程序运行,管理员的会话令牌被载荷窃取。
将 console.log 的主体替换为对同源已认证端点的 fetch,可使载荷以已登录用户的权限执行操作。
以已登录用户的身份在仪表盘源中执行脚本,而攻击者只需向代理发送一条消息即可触达。载荷可以读取会话令牌并调用已认证的端点,包括编辑代理自身指令文件和 OpenClaw 配置的端点。由于该漏洞在默认页面上触发,管理员无需执行任何异常操作。
lastMessage、label 和 key 插入行标记之前进行 HTML 编码,或者使用 textContent 而不是 HTML 字符串来构造单元格。innerHTML 渲染的其他会话字段应用相同的编码。script-src 中移除 'unsafe-inline',这样即使某个编码步骤被遗漏,注入的标记也无法执行。