# REPRO-2026-00171: nginx WebDAV: heap-buffer-overflow in COPY/MOVE with alias directive ## Summary Status: published Severity: high Type: security Confidence: Unknown ## Identifiers REPRO ID: REPRO-2026-00171 CVE: CVE-2026-27654 ## Package Name: nginx Ecosystem: source Affected: OSS 0.5.13–0.9.7, 1.0.0–1.28.2, 1.29.0–1.29.6; Plus R32–R36 Fixed: Unknown ## Root Cause # RCA Report: CVE-2026-27654 — nginx WebDAV COPY/MOVE heap-buffer-overflow with alias directive ## Summary CVE-2026-27654 is a heap-based buffer overflow in nginx's `ngx_http_dav_module` that triggers when processing WebDAV COPY or MOVE requests under a location that uses the `alias` directive. The vulnerability stems from an integer underflow in `ngx_http_map_uri_to_path()`: when the destination URI (`duri`) is shorter than the location prefix length (`clcf->alias`), the buffer size calculation `clcf->root.len + r->uri.len - alias + 1` underflows as an unsigned `size_t`, resulting in a near-zero or wrapped allocation. The subsequent `memcpy` of the alias root path and destination URI then overflows the tiny heap buffer. This is reachable unauthenticated by any client that can send a WebDAV COPY/MOVE request to an nginx instance with `dav_methods` enabled under an aliased location. ## Impact - **Package/component affected**: nginx Open Source and Plus, `ngx_http_dav_module` - **Affected versions**: `0.5.13–0.9.7`, `1.0.0–1.28.2`, `1.29.0–1.29.6` - **Fixed versions**: `1.28.3` (stable), `1.29.7` (mainline) - **Risk level**: High (CVSS 3.1: 8.2, CVSS 4.0: 8.8) - **Consequences**: Worker process crash (DoS), potential arbitrary file write outside the intended directory, and possible escalation to RCE depending on heap allocator state. ## Root Cause The bug is located in `ngx_http_dav_copy_move_handler()` (`src/http/modules/ngx_http_dav_module.c`). Before computing the destination filesystem path, the handler temporarily overwrites `r->uri` with the parsed `Destination` header URI (`duri`) and calls `ngx_http_map_uri_to_path()`. That function computes the required buffer length with: ```c path->len = clcf->root.len + reserved + r->uri.len - alias + 1; ``` All operands are unsigned `size_t`. When `alias` (which stores the location prefix length) is larger than `clcf->root.len + r->uri.len + 1`, the subtraction wraps around. For example, with `location /davvvv/` (alias = 7) and `alias /a/` (root.len = 3), a destination URI of `/xx` (len = 2) produces: `3 + 2 - 7 + 1` → `5 - 7` wraps to `SIZE_MAX - 1`, then `+ 1` wraps back to `0`. `ngx_pnalloc(pool, 0)` returns a tiny pointer from the nginx pool. The following `ngx_copy(path->data, clcf->root.data, 3)` writes 3 bytes into a zero-byte allocation, and the subsequent `ngx_copy(last, r->uri.data + alias, r->uri.len - alias)` passes a negative-size parameter (`-5`) to `memcpy`, causing a massive out-of-bounds read/write. **Fix commit**: `9739e75` in the nginx repository (`Dav: destination length validation for COPY and MOVE.`). The patch adds an explicit check before the path mapping: ```c if (clcf->alias && clcf->alias != NGX_MAX_SIZE_T_VALUE && duri.len < clcf->alias) { return NGX_HTTP_BAD_REQUEST; } ``` This rejects any COPY/MOVE request whose destination URI is shorter than the location prefix, preventing the underflow. ## Reproduction Steps The reproduction is fully automated in `repro/reproduction_steps.sh`. What the script does: 1. Clones the nginx repository (or reuses the existing clone). 2. Builds the vulnerable version (`release-1.29.6`) and the fixed version (`release-1.29.7`), both compiled with AddressSanitizer (`-fsanitize=address`). 3. Creates a minimal nginx configuration with a location `/davvvv/` that maps via `alias` to a short directory path, and enables `dav_methods COPY MOVE`. 4. Starts the **vulnerable** nginx binary in the foreground (`daemon off; master_process off;`), sends a `COPY` request with a deliberately short `Destination: http://127.0.0.1:8080/xx` header, and captures ASAN output via `ASAN_OPTIONS=log_path=...`. 5. Repeats the same request against the **fixed** nginx binary and captures the HTTP response. 6. Verifies that the vulnerable build crashes with an ASAN error while the fixed build returns HTTP `400 Bad Request` with no ASAN error. **Expected evidence**: - `logs/vulnerable_asan.txt` — ASAN report showing `negative-size-param: (size=-5)` inside `ngx_http_map_uri_to_path` called from `ngx_http_dav_copy_move_handler`. - `logs/fixed_curl.txt` — HTTP `400 Bad Request` response from nginx/1.29.7. ## Evidence ### Vulnerable build (release-1.29.6) ASAN log (`logs/vulnerable_asan.txt`): ``` ==16927==ERROR: AddressSanitizer: negative-size-param: (size=-5) #0 ... in memcpy #1 ... in ngx_http_map_uri_to_path src/http/ngx_http_core_module.c:1987 #2 ... in ngx_http_dav_copy_move_handler src/http/modules/ngx_http_dav_module.c:703 #3 ... in ngx_http_dav_handler src/http/modules/ngx_http_dav_module.c:194 #4 ... in ngx_http_core_content_phase src/http/ngx_http_core_module.c:1282 ... SUMMARY: AddressSanitizer: negative-size-param ... in memcpy ``` The stack trace confirms the crash occurs inside `ngx_http_map_uri_to_path` while handling the COPY request in the DAV module. ### Fixed build (release-1.29.7) HTTP response (`logs/fixed_curl.txt`): ``` 400 Bad Request

400 Bad Request


nginx/1.29.7
HTTP_CODE:400 ``` The fixed binary rejects the malicious request with a `400 Bad Request` and logs: ``` client sent invalid "Destination" header: "http://127.0.0.1:8080/xx" ``` ### Environment - OS: Linux 6.18.5 x86_64 - Compiler: GCC 13.3.0 - ASAN flags: `-fsanitize=address -g -O1 -fno-omit-frame-pointer` - nginx configure: `--with-http_dav_module` ## Recommendations / Next Steps 1. **Upgrade immediately** to nginx `1.28.3` (stable) or `1.29.7` (mainline) or later. 2. **Mitigation** (if upgrading is not immediately possible): disable WebDAV COPY and MOVE methods, or avoid using the `alias` directive in locations that expose DAV methods. 3. **Regression testing**: any future changes to `ngx_http_dav_copy_move_handler` or `ngx_http_map_uri_to_path` should include a test case with a destination URI shorter than the location prefix under an aliased location. 4. **Code review**: audit other callers of `ngx_http_map_uri_to_path` that temporarily modify `r->uri` to ensure similar length validations are in place. ## Additional Notes - **Idempotency**: `repro/reproduction_steps.sh` has been executed twice consecutively on a clean environment and produced the same confirmed verdict both times. - **Edge cases**: The trigger requires a specific relationship between the alias path length, the location prefix length, and the destination URI length. The script uses `location /davvvv/` (len 7) + `alias /a/` (len 3) + destination `/xx` (len 2) to force the underflow to wrap to exactly `0`, which maximizes the chance of a detectable heap overflow. Other combinations (e.g., longer destination URIs that still underflow to small non-zero values) are also exploitable. - **Limitations**: The reproduction requires building nginx from source with ASAN, which takes several minutes. The script caches the built binaries in `$ROOT/builds/` to avoid repeated compilation. ## Reproduction Details Reproduced: 2026-05-28T12:07:11.635Z Duration: 1301 seconds Tool calls: 173 Turns: 151 Handoffs: 3 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00171 pruva-verify CVE-2026-27654 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00171&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00171/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-27654 ## Artifacts - bundle/repro/rca_report.md (analysis, 7003 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 4887 bytes) - bundle/vuln_variant/rca_report.md (analysis, 8209 bytes) - bundle/vuln_variant/reproduction_steps.sh (reproduction_script, 7112 bytes) - bundle/context.json (other, 2544 bytes) - bundle/metadata.json (other, 655 bytes) - bundle/ticket.md (ticket, 5114 bytes) - bundle/repro/validation_verdict.json (other, 1180 bytes) - bundle/repro/nginx.conf (other, 476 bytes) - bundle/vuln_variant/root_cause_equivalence.json (other, 1378 bytes) - bundle/vuln_variant/patch_analysis.md (documentation, 4701 bytes) - bundle/vuln_variant/variant_manifest.json (other, 3214 bytes) - bundle/vuln_variant/runtime_manifest.json (other, 2583 bytes) - bundle/vuln_variant/validation_verdict.json (other, 1422 bytes) - bundle/logs/vuln_prefix_curl_err.txt (other, 0 bytes) - bundle/logs/vuln_exact_stdout.txt (other, 0 bytes) - bundle/logs/fixed_prefix_curl_err.txt (other, 0 bytes) - bundle/logs/fixed_stderr.txt (other, 0 bytes) - bundle/logs/vuln_urlenc_curl.txt (other, 0 bytes) - bundle/logs/vuln_nested_curl_err.txt (other, 0 bytes) - bundle/logs/test_stderr.txt (other, 0 bytes) - bundle/logs/fixed_prefix_stderr.txt (other, 0 bytes) - bundle/logs/fixed_nested_curl_err.txt (other, 0 bytes) - bundle/logs/vulnerable_asan.txt (other, 3262 bytes) - bundle/logs/fixed_script_curl.txt (other, 172 bytes) - bundle/logs/vuln_prefix.18268 (other, 3262 bytes) - bundle/logs/vuln_move_stderr.txt (other, 0 bytes) - bundle/logs/fixed_exact_curl.txt (other, 172 bytes) - bundle/logs/fixed_nested_stdout.txt (other, 0 bytes) - bundle/logs/asan_vulnerable.17016 (other, 3262 bytes) - bundle/logs/variant_final.log (log, 1116 bytes) - bundle/logs/vuln_exact_curl_err.txt (other, 0 bytes) - bundle/logs/fixed_curl_err.txt (other, 0 bytes) - bundle/logs/fixed_urlenc_stderr.txt (other, 0 bytes) - bundle/logs/fixed_script_curl_err.txt (other, 0 bytes) - bundle/logs/vuln_script_stdout.txt (other, 0 bytes) - bundle/logs/fixed_nested_stderr.txt (other, 0 bytes) - bundle/logs/fixed_move_stdout.txt (other, 0 bytes) - bundle/logs/fixed_script_stdout.txt (other, 0 bytes) - bundle/logs/vuln_move_stdout.txt (other, 0 bytes) - bundle/logs/fixed_move_curl_err.txt (other, 0 bytes) - bundle/logs/error.log (log, 8781 bytes) - bundle/logs/vuln_script_curl.txt (other, 0 bytes) - bundle/logs/fixed_script_stderr.txt (other, 0 bytes) - bundle/logs/vuln_prefix_stdout.txt (other, 0 bytes) - bundle/logs/fixed_move_stderr.txt (other, 0 bytes) - bundle/logs/vuln_move.18292 (other, 3262 bytes) - bundle/logs/fixed_nested_curl.txt (other, 172 bytes) - bundle/logs/vuln_script.18286 (other, 3262 bytes) - bundle/logs/vuln_script_curl_err.txt (other, 0 bytes) - bundle/logs/vuln_nested_stdout.txt (other, 0 bytes) - bundle/logs/vulnerable_curl_err.txt (other, 0 bytes) - bundle/logs/fixed_exact_stdout.txt (other, 0 bytes) - bundle/logs/vuln_nested.18280 (other, 3262 bytes) - bundle/logs/test_stdout.txt (other, 0 bytes) - bundle/logs/fixed_prefix_stdout.txt (other, 0 bytes) - bundle/logs/fixed_urlenc_curl_err.txt (other, 0 bytes) - bundle/logs/vuln_nested_stderr.txt (other, 0 bytes) - bundle/logs/access.log (log, 3380 bytes) - bundle/logs/vuln_script_stderr.txt (other, 0 bytes) - bundle/logs/vulnerable_stdout.txt (other, 0 bytes) - bundle/logs/vuln_urlenc_stdout.txt (other, 0 bytes) - bundle/logs/variant_tests.log (log, 76 bytes) - bundle/logs/vuln_prefix_curl.txt (other, 0 bytes) - bundle/logs/vuln_nested_curl.txt (other, 0 bytes) - bundle/logs/fixed_move_curl.txt (other, 172 bytes) - bundle/logs/vuln_move_curl_err.txt (other, 0 bytes) - bundle/logs/vulnerable_curl.txt (other, 0 bytes) - bundle/logs/test_curl_err.txt (other, 0 bytes) - bundle/logs/test_curl.txt (other, 0 bytes) - bundle/logs/vuln_urlenc_stderr.txt (other, 0 bytes) - bundle/logs/vuln_move_curl.txt (other, 0 bytes) - bundle/logs/fixed_exact_stderr.txt (other, 0 bytes) - bundle/logs/nginx.pid (other, 6 bytes) - bundle/logs/vuln_exact.18274 (other, 3262 bytes) - bundle/logs/vuln_urlenc_curl_err.txt (other, 0 bytes) - bundle/logs/fixed_urlenc_stdout.txt (other, 0 bytes) - bundle/logs/fixed_curl.txt (other, 172 bytes) - bundle/logs/vulnerable_stderr.txt (other, 0 bytes) - bundle/logs/fixed_stdout.txt (other, 0 bytes) - bundle/logs/vuln_exact_curl.txt (other, 0 bytes) - bundle/logs/vuln_prefix_stderr.txt (other, 0 bytes) - bundle/logs/vuln_exact_stderr.txt (other, 0 bytes) - bundle/logs/vuln_urlenc.18298 (other, 3305 bytes) - bundle/logs/fixed_exact_curl_err.txt (other, 0 bytes) - bundle/logs/fixed_urlenc_curl.txt (other, 172 bytes) - bundle/logs/fixed_prefix_curl.txt (other, 172 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00171 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00171/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/r/REPRO-2026-00171 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev