
Un seul appel de fonction manquant lors de l'enregistrement de la route a suffi pour transformer l'interface MCP en passerelle RCE non authentifiée.
Outil de détection non destructif pour le contournement d'authentification MCP de nginx-ui connu publiquement sous le nom de MCPwn. Permet une prise de contrôle non authentifiée en deux requêtes HTTP de toute instance nginx-ui exécutant une version vulnérable.
nginx-ui expose une interface Model Context Protocol (MCP) qui permet à un opérateur authentifié de piloter des outils destructeurs tels que la modification de la configuration nginx, les commandes de redémarrage et les opérations de sauvegarde via JSON-RPC.
Le point de terminaison /mcp est protégé par le middleware AuthRequired(),
tandis que son point de terminaison associé /mcp_message, qui reçoit
réellement les invocations d'outils, a été déployé sans le middleware.
Tout client capable d'atteindre l'interface sur le réseau peut :
/mcp et recevoir un
sessionID frais sans présenter aucune information d'identification.sessionID pour invoquer n'importe quel outil MCP enregistré
en envoyant une requête POST vers /mcp_message, toujours sans
authentification.L'attaquant obtient un contrôle total du processus nginx : modifier les blocs serveur pour rediriger le trafic, extraire les clés privées TLS via des lectures de chemins, recharger ou arrêter nginx, et implanter des portes dérobées persistantes.
// vulnérable (pré-2.3.4)
r.GET("/mcp", AuthRequired(), mcpHandler)
r.POST("/mcp_message", mcpMessageHandler) // middleware manquant
// corrigé dans 2.3.4
r.GET("/mcp", AuthRequired(), mcpHandler)
r.POST("/mcp_message", AuthRequired(), mcpMessageHandler)
Un seul appel de fonction manquant dans l'enregistrement des routes a suffi pour transformer l'interface MCP en passerelle RCE non authentifiée.
Attaquant nginx-ui (<2.3.4)
| 1) GET /mcp (sans auth) |
| --------------------------------------> |
| 2) event: endpoint data: /mcp_message?sessionID=XYZ
| <-------------------------------------- |
| 3) POST /mcp_message?sessionID=XYZ |
| corps : JSON-RPC call_tool "nginx_reload" ou
| "edit_config {...}" |
| --------------------------------------> |
| 4) 200 OK ; outil exécuté avec tous les privilèges
| <-------------------------------------- |
Ce dépôt évite intentionnellement toute action destructrice. Le
détecteur n'utilise que des méthodes MCP en lecture seule (tools/list). La logique :
> Étape 1 Empreinte de nginx-ui via
> GET / (titre HTML, bundles JS)
> GET /api/settings (JSON référençant les clés nginx-ui)
> Extraction de la version via l'en-tête / regex du corps
> Étape 2 Ouvrir le flux SSE vers /mcp
> Analyser le premier sessionID de l'événement "endpoint"
> Étape 3 POST /mcp_message?sessionID=<id>
> Corps : {"jsonrpc":"2.0","method":"tools/list","params":{}}
> Aucun en-tête Authorization
> Étape 4 Vulnérable si statut 200 et le corps contient un manifeste d'outils
> Corrigé si statut 401 / 403 / 404
> Non concluant sinon
La méthode JSON-RPC tools/list est en lecture seule. Elle énumère les
outils connus du serveur MCP mais n'en invoque aucun. Le script
refuse d'envoyer toute autre méthode et ne construit jamais de charges utiles
pour les outils destructeurs.
git clone https://github.com/your-org/CVE-2026-33032-detector.git
cd CVE-2026-33032-detector
python3 --version # 3.9 ou plus récent
# Aucun paquet tiers requis.
Pour le composant Nmap NSE, copiez nginx-ui-mcpwn.nse dans votre répertoire
de scripts local et actualisez la base de données de scripts :
cp nginx-ui-mcpwn.nse /usr/share/nmap/scripts/
sudo nmap --script-updatedb
# Cible unique
python3 detect_nginx_ui_mcpwn.py --target https://nginx-ui.internal
# Analyse en masse depuis un fichier
python3 detect_nginx_ui_mcpwn.py --targets targets.txt --workers 20
# Sortie JSON (NDJSON) adaptée au piping vers jq
python3 detect_nginx_ui_mcpwn.py --target 10.0.0.5:9000 --json | jq .
Référence des arguments en ligne de commande :
nmap -p 80,443,9000 --script nginx-ui-mcpwn 10.0.0.0/24
nmap -p 443 --script nginx-ui-mcpwn --script-args "nginx-ui-mcpwn.timeout=8" host.example.com
_ _ _ _ _ ___ __ __ ____ ____
| \ | | __ _(_)_ __ __ __ | | | ||_ _| | \/ |/ ___| _ \__ ___ __
| \| |/ _` | | '_ \\ \/ /_____| | | | | | | |\/| | | | |_) \ \ /\ / / '_ \
| |\ | (_| | | | | |> <|_____| |__| | | | | | | | |___| __/ \ V V /| | | |
|_| \_|\__, |_|_| |_/_/\_\ |_____|_||___| |_| |_|\____|_| \_/\_/ |_| |_|
|___/
Détecteur CVE-2026-33032 nginx-ui MCPwn > non destructif
[!] VULNÉRABLE https://nginx-ui.internal version=2.3.2 preuve=statut=200; aperçu_corps={"jsonrpc":"2.0","id":"...","result":{"tools":[{"name":"edit_config",...
[+] corrigé https://nginx-ui-patched:9000 version=2.3.4 preuve=statut=401; aperçu_corps={"error":"unauthorized"}
[.] pas nginx-ui https://example.com preuve=empreinte négative
Résumé : 1/3 cibles signalées comme vulnérables
{"target":"https://nginx-ui.internal","reachable":true,"is_nginx_ui":true,"mcp_endpoint_present":true,"session_id_obtained":true,"mcp_message_unauthenticated":true,"vulnerable":true,"nginx_ui_version":"2.3.2","evidence":"status=200; body_preview=...","error":null}
PORT STATE SERVICE
9000/tcp open http
| nginx-ui-mcpwn:
| status: VULNERABLE
| version: 2.3.2
| session_id: abc123
|_ evidence: tools/list accepted without credentials
CVE-2026-27944 (le téléchargement non authentifié de /api/backup qui
divulgue la clé AES dans l'en-tête X-Backup-Security) se chaîne proprement
avec MCPwn : l'archive de sauvegarde expose le node_secret, qui peut être
utilisé pour s'authentifier auprès de /mcp même après l'arrivée du correctif
pour 33032, jusqu'à ce que les opérateurs fassent tourner le secret.
Le détecteur associé pour 27944 se trouve dans un répertoire frère de ce projet de recherche.
node_secret, les hachages de mots de passe administrateur
et toute clé privée TLS accessible au processus nginx-ui./mcp_message soit accessible.Cet outil est publié pour aider les défenseurs à identifier les instances non corrigées dans les environnements qu'ils sont autorisés à tester. Ne l'utilisez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas l'autorisation explicite d'évaluer. Les auteurs déclinent toute responsabilité en cas de mauvaise utilisation.
Publié sous la licence MIT. Consultez les en-têtes de chaque script pour les mentions par fichier.
| Champ | Valeur |
|---|
| ID CVE | CVE-2026-33032 |
| Alias | MCPwn |
| CVSS 3.1 | 9.8 / Critique (AV:N / AC:L / PR:N / UI:N / S:U / C:H/I:H/A:H) |
| CWE | CWE-306 Absence d'authentification pour une fonction critique |
| Produit concerné | nginx-ui (github.com/0xjacky/nginx-ui) |
| Versions concernées | Toutes les versions antérieures à 2.3.4 |
| Version corrigée | 2.3.4 |
| Statut d'exploitation | Active dans la nature (Picus Security, Recorded Future) |
| Exposition Shodan | ~2 689 instances accessibles sur Internet |
| Date de publication | 2026-04-15 |
| Outil MCP | Effet sur le serveur nginx |
|---|
edit_config | Réécrire des blocs serveur arbitraires, insérer un upstream attaquant |
reload_nginx | Appliquer la configuration de l'attaquant sans action manuelle |
stop_nginx | Déni de service |
read_file | Exfiltrer les clés privées TLS, les informations d'identification depuis les chemins montés |
create_cert | Réémettre des certificats TLS sous le contrôle de l'attaquant |
list_backups / download | Récupérer l'intégralité de la sauvegarde nginx-ui, y compris node_secret et les hachages |
| Option | Objectif |
|---|
--target | URL unique ou hôte[:port] |
--targets | Fichier de cibles séparées par des sauts de ligne |
--timeout | Délai d'expiration HTTP, 10 secondes par défaut |
--workers | Travailleurs concurrents pour les analyses en masse, 10 par défaut |
--verify-tls | Imposer la vérification du certificat TLS (désactivée par défaut) |
--json | Émettre du NDJSON, un enregistrement par cible |
--no-banner | Supprimer la bannière ASCII |