
Una sola chiamata di funzione mancante nella registrazione delle route è bastata a trasformare l'interfaccia MCP in un gateway RCE non autenticato.
Strumentazione di rilevamento non distruttiva per il bypass di autenticazione MCP di nginx-ui noto pubblicamente come MCPwn. Consente l'acquisizione non autenticata di qualsiasi istanza nginx-ui che esegua una versione vulnerabile, tramite due sole richieste HTTP.
| Campo | Valore |
|---|---|
| ID CVE | CVE-2026-33032 |
| Alias | MCPwn |
| CVSS 3.1 | 9.8 / Critico (AV:N / AC:L / PR:N / UI:N / S:U / C:H/I:H/A:H) |
| CWE | CWE-306 Mancanza di autenticazione per funzioni critiche |
| Prodotto interessato | nginx-ui (github.com/0xjacky/nginx-ui) |
| Versioni interessate | Tutte le versioni precedenti alla 2.3.4 |
| Versione corretta | 2.3.4 |
| Stato dello sfruttamento | Attivo in natura (Picus Security, Recorded Future) |
| Esposizione su Shodan | ~2.689 istanze esposte su Internet |
| Data di pubblicazione | 2026-04-15 |
nginx-ui espone un'interfaccia Model Context Protocol (MCP) che consente a un operatore autenticato di pilotare strumenti distruttivi come la modifica della configurazione di nginx, comandi di riavvio e operazioni di backup tramite JSON-RPC.
L'endpoint /mcp è protetto dal middleware AuthRequired(), mentre il
suo endpoint gemello /mcp_message, che riceve effettivamente le
invocazioni degli strumenti, è stato distribuito senza il middleware.
Qualsiasi client in grado di raggiungere l'interfaccia tramite rete può:
/mcp e ricevere un
nuovo sessionID senza presentare alcuna credenziale.sessionID per invocare qualsiasi strumento MCP
registrato inviando una POST a /mcp_message, sempre senza
autenticazione.L'attaccante ottiene il controllo completo del processo nginx: può modificare i server block per reindirizzare il traffico, estrarre le chiavi private TLS tramite letture di percorsi, ricaricare o arrestare nginx e impiantare backdoor persistenti.
// vulnerabile (pre-2.3.4)
r.GET("/mcp", AuthRequired(), mcpHandler)
r.POST("/mcp_message", mcpMessageHandler) // middleware mancante
// corretto in 2.3.4
r.GET("/mcp", AuthRequired(), mcpHandler)
r.POST("/mcp_message", AuthRequired(), mcpMessageHandler)
Una singola chiamata di funzione mancante nella registrazione delle route è stata sufficiente a trasformare l'interfaccia MCP in un gateway RCE non autenticato.
Attaccante nginx-ui (<2.3.4)
| 1) GET /mcp (senza auth) |
| --------------------------------------> |
| 2) event: endpoint data: /mcp_message?sessionID=XYZ
| <-------------------------------------- |
| 3) POST /mcp_message?sessionID=XYZ |
| body: JSON-RPC call_tool "nginx_reload" o
| "edit_config {...}" |
| --------------------------------------> |
| 4) 200 OK; strumento eseguito con pieni privilegi
| <-------------------------------------- |
| Strumento MCP | Effetto sul server nginx |
|---|---|
edit_config | Riscrive server block arbitrari, inserisce upstream dell'attaccante |
reload_nginx | Applica la configurazione dell'attaccante senza azione manuale |
stop_nginx | Denial of service |
read_file | Esfiltra chiavi private TLS, credenziali da percorsi montati |
create_cert | Rilascia certificati TLS sotto il controllo dell'attaccante |
list_backups / download | Scarica l'intero backup di nginx-ui inclusi node_secret e hash |
Questo repository evita intenzionalmente qualsiasi azione distruttiva.
Il rilevatore utilizza solo metodi MCP di sola lettura (tools/list).
La logica:
> Passo 1 Identificare nginx-ui tramite
> GET / (titolo HTML, bundle JS)
> GET /api/settings (JSON che fa riferimento a chiavi nginx-ui)
> Estrarre la versione tramite header / regex sul body
> Passo 2 Aprire lo stream SSE su /mcp
> Estrarre il primo sessionID dall'evento "endpoint"
> Passo 3 POST /mcp_message?sessionID=<id>
> Body: {"jsonrpc":"2.0","method":"tools/list","params":{}}
> Nessun header Authorization
> Passo 4 Vulnerabile se status 200 e il body contiene un manifest degli strumenti
> Corretto se status 401 / 403 / 404
> Inconcludente altrimenti
Il metodo JSON-RPC tools/list è di sola lettura. Enumera quali
strumenti conosce il server MCP ma non ne invoca nessuno. Lo script
rifiuta di inviare POST con qualsiasi altro metodo e non costruisce mai
payload per strumenti distruttivi.
git clone https://github.com/your-org/CVE-2026-33032-detector.git
cd CVE-2026-33032-detector
python3 --version # 3.9 o successiva
# Non sono richiesti pacchetti di terze parti.
Per il componente Nmap NSE, copia nginx-ui-mcpwn.nse nella tua
directory locale degli script e aggiorna il database degli script:
cp nginx-ui-mcpwn.nse /usr/share/nmap/scripts/
sudo nmap --script-updatedb
# Singolo target
python3 detect_nginx_ui_mcpwn.py --target https://nginx-ui.internal
# Scansione di massa da file
python3 detect_nginx_ui_mcpwn.py --targets targets.txt --workers 20
# Output JSON (NDJSON) adatto per il piping a jq
python3 detect_nginx_ui_mcpwn.py --target 10.0.0.5:9000 --json | jq .
Riferimento della riga di comando: