# 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`):

```
<html>
<head><title>400 Bad Request</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx/1.29.7</center>
</body>
</html>

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.
