The nginx-ui MCP (Model Context Protocol) integration exposes two HTTP endpoints: /mcp and /mcp_message. While /mcp requires both IP whitelisting and authentication (AuthRequired() middleware), the /mcp_message endpoint only applies IP whitelisting - and the default IP whitelist is empty, which the middleware treats as "allow all". This means any network attacker can invoke all MCP tools without authentication, including restarting nginx, creating/modifying/deleting nginx configuration files, and triggering automatic config reloads - achieving complete nginx service takeover.
mcp/router.go:9-17 - Auth asymmetry between endpoints
func InitRouter(r *gin.Engine) {
r.Any("/mcp", middleware.IPWhiteList(), middleware.AuthRequired(),
func(c *gin.Context) {
mcp.ServeHTTP(c)
})
r.Any("/mcp_message", middleware.IPWhiteList(),
func(c *gin.Context) {
mcp.ServeHTTP(c)
})
}
The /mcp endpoint has middleware.AuthRequired(), but /mcp_message does not. Both endpoints route to the same mcp.ServeHTTP() handler, which processes all MCP tool invocations.
internal/middleware/ip_whitelist.go:11-26 - Empty whitelist allows all
func IPWhiteList() gin.HandlerFunc {
return func(c *gin.Context) {
clientIP := c.ClientIP()
if len(settings.AuthSettings.IPWhiteList) == 0 || clientIP == "" || clientIP == "127.0.0.1" || clientIP == "::1" {
c.Next()
return
}
// ...
}
}
When IPWhiteList is empty (the default - settings/auth.go initializes Auth{} with no whitelist), the middleware allows all requests through. This is a fail-open design.
From mcp/nginx/:
restart_nginx - restart the nginx processreload_nginx - reload nginx configurationnginx_status - read nginx statusFrom mcp/config/:
nginx_config_add - create new nginx config filesnginx_config_modify - modify existing config files
-...Exploitability
AV:NAC:LPR:NUI:NScope
S:UImpact
C:HI:HA:H9.8/CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H