Status: Patched
This vulnerability has been verified as resolved and deployed.
rewrite overlapping captures heap overflow (CVE-2026-9256)
Summary
NGINX under-sized rewrite redirect buffers when distinct overlapping captures were escaped independently
A regex rewrite such as rewrite ^/((.*))$ http://127.0.0.1:18081/$1$2 redirect; can make $1 and $2 cover the same attacker-controlled URI bytes. In NGINX 1.31.0 (e8053c867f9ab14f323e3019ccab585d857abb66), the rewrite compiler could discard the capture-aware length bytecode for capture-only replacements because sc.variables == 0 and sc.dup_capture == 0 when the replacement references distinct capture numbers.
At runtime, ngx_http_script_regex_start_code() then used its code->lengths == NULL fast path. Before the fix, that fast path added the replacement's static size, one whole-URI escape adjustment, and the raw length of each capture. That was not equivalent to the copy phase for overlapping captures: ngx_http_script_copy_capture_code() escapes every referenced capture independently when the rewrite is in a redirect or arguments context and the request URI contains escapable bytes such as +.
The original ASan reproducer on the real HTTP listener showed ngx_http_script_copy_capture_code() writing past a 4096-byte request-pool allocation when a path of plus signs was rewritten through $1$2. F5 published this as CVE-2026-9256, CWE-122, with CVSS 3.1 score 8.1. NGINX fixed it through PR #1395, merged as commit ca4f92a27464ae6c2082245e4f67048c633aa032, and the NGINX 1.31.1 changelog credits Mufeed VH of Winfunc Research.
CVSS Score
Vulnerability Location
Source-to-Sink Analysis
The rewrite compiler removed the exact capture-aware length program when the replacement had no non-capture variables and no duplicate reference to the same capture number. A replacement like $1$2 with distinct captures satisfied that condition even though both captures could overlap.
With regex->lengths == NULL, the runtime fast path sized the output buffer by adding one whole-URI escape adjustment and the raw length of each capture. It did not calculate escape expansion per referenced capture.
The under-sized length was used for a request-pool allocation. In the ASan run, the allocation stayed in a 4096-byte pool block and the later escaped copy wrote at the first byte past that block.
A URI containing + or quoted bytes sets request parser flags that later make redirect and arguments capture copies use NGX_ESCAPE_ARGS escaping.
The copy phase escapes each referenced capture independently. For nested captures such as ^/((.*))$, $1 and $2 can cover the same bytes, so the same attacker-controlled plus signs are expanded twice even though the vulnerable fast path only reserved one URI-wide escape adjustment.
The accepted upstream fix keeps the fast path but makes its sizing mirror the copy phase: each capture contributes its raw length plus its own NGX_ESCAPE_ARGS expansion. Distinct overlapping captures are therefore counted separately.
Impact Analysis
Critical Impact
The demonstrated primitive is unauthenticated remote heap memory corruption in an NGINX worker process. The original ASan build aborted with a heap-buffer-overflow at the end of a 4096-byte request-pool allocation. The public CVE record states that exploitation can cause a worker restart and that code execution is possible when ASLR is disabled or bypassed. This entry does not claim a confirmed production RCE chain beyond the proven out-of-bounds write.
Attack Surface
NGINX Open Source and NGINX Plus configurations using ngx_http_rewrite_module with a regex rewrite whose replacement references distinct, overlapping PCRE captures in a redirect or arguments context. The NGINX security index lists Open Source versions 0.1.17-1.31.0 as vulnerable and 1.31.1+ / 1.30.2+ as fixed.
Preconditions
The target must have regex rewrite support enabled and a reachable rewrite rule shaped like ^/((.*))$ with a replacement such as $1$2. The replacement must take the capture-only fast path, and the attacker must send a matching URI containing bytes that require arguments escaping, such as +.
Proof of Concept
Environment Setup
Build the affected NGINX Open Source 1.31.0 release with ASan from the source root:
Target Configuration
Run a single-process listener with a nested capture redirect rewrite:
Exploit Delivery
Start nginx with ASan enabled and send a URI containing escapable plus signs:
Outcome
The crafted request causes redirect construction to overflow the under-sized request-pool buffer. After applying PR #1395 / commit ca4f92a27464ae6c2082245e4f67048c633aa032, the same request should not produce an ASan heap-buffer-overflow and the worker should continue serving requests.
Expected Response:
A vulnerable ASan build reports a heap-buffer-overflow in ngx_http_script_copy_capture_code, with allocation from ngx_http_script_regex_start_code and a write at the end of a 4096-byte region:
Run this level of analysis on your repo.
Winfunc traces source-to-sink paths, validates exploitability, and gives your team patch-ready remediation.
