Status: Patched
This vulnerability has been verified as resolved and deployed.
Stream complex-value capture desynchronization causes heap overflow (CVE-2026-42533)
Summary
NGINX stream complex values allocated from stale capture state, then copied attacker-controlled regex captures
ngx_stream_complex_value() calculated an output length and copied the output in separate passes over mutable session-wide regex capture state. In a stream expression such as return "$1$m", the first pass initially counted $1 as zero bytes. Evaluating the later $m variable ran a regex-backed map over attacker-controlled TLS SNI and populated the session's capture array. The copy pass then revisited $1, found the new capture, and copied the SNI hostname into the buffer allocated from the stale one-byte length.
A live ASan build at NGINX commit e8053c867f9ab14f323e3019ccab585d857abb66 confirmed a 12,000-byte write in ngx_stream_script_copy_capture_code() past the one-byte complex-value allocation. The public F5 advisory, GitHub advisory, and CVE record classify the issue as CWE-122. They assign CVSS 3.1 score 8.1 (High) and CVSS 4.0 score 9.2 (Critical), describe both regex-map capture ordering and non-cacheable-variable variants, and identify NGINX Open Source 1.30.4 and 1.31.3 as the fixed releases. The NGINX security index lists 0.9.6-1.31.2 as vulnerable and 1.31.3+ as not vulnerable. F5 credits Mufeed VH of Winfunc Research among the researchers who independently reported the issue.
NGINX shipped the mainline fix in PR #1561. The patch series first simplified capture-copy accounting in commit 2821920, added bounds checks to HTTP and stream script copies in commit b767540, and extended the same protection to direct HTTP script users in commit 25f920e. Stable NGINX 1.30 received the corresponding commits ea47fab, 7ac6789, and 326b17b through PR #1563.
CVSS Score
Vulnerability Location
Source-to-Sink Analysis
A client connected to the reachable stream listener and supplied unauthenticated preread bytes. With ssl_preread on, those bytes were parsed as a TLS ClientHello before any upstream connection or application authentication.
The SSL preread parser allocated storage for the SNI hostname using the length encoded by the client. The generic parser copy state then copied the attacker-controlled hostname bytes into this session-owned buffer.
ngx_stream_complex_value() ran all length opcodes, allocated exactly that predicted length, and only then ran the copy opcodes. Both passes read mutable capture fields from the same ngx_stream_session_t.
The $1 length opcode ran before the regex-backed map variable. With no current capture, it contributed zero bytes to the allocation and did not reserve space for a later capture.
The following $m length opcode evaluated map $ssl_preread_server_name $m. Its regex matched the attacker-controlled SNI and published new offsets and captures_data into the session-wide capture state after $1 had already been sized.
With the map value X, the stale predicted output length was one byte. NGINX allocated that one-byte result even though $1 now referred to the much longer SNI capture.
During the copy pass, the $1 opcode observed the newly populated capture state and copied the full SNI hostname without checking the remaining result-buffer capacity. The ASan proof reached this sink with a 12,000-byte write.
The accepted fix records the allocated buffer end and routes literal, variable, and capture copies through a shared length guard. A mismatch logs an alert, stops the script, sets an internal-error status, and makes ngx_stream_complex_value() fail closed.
Impact Analysis
Critical Impact
The demonstrated primitive is an unauthenticated remote heap/pool out-of-bounds write in an NGINX worker, with write contents and length controlled through TLS SNI. The proof copied 12,000 attacker-controlled bytes into an output allocation sized for one byte. ASan and hardened allocators terminate the worker, producing denial of service; without them, adjacent pool or heap state can be corrupted. The public CVE states that code execution is possible when ASLR is disabled or bypassed, but this proof does not claim a reliable production code-execution chain.
Attack Surface
NGINX Open Source and NGINX Plus data-plane listeners that evaluate affected complex values. The confirmed stream path requires ngx_stream_module, PCRE/PCRE2 support, ngx_stream_ssl_preread_module, a reachable stream listener, and a regex-backed stream variable such as map $ssl_preread_server_name $m. The public advisory also covers equivalent HTTP map/regex and non-cacheable-variable length/copy desynchronization patterns.
Preconditions
For the demonstrated stream trigger, ssl_preread on must expose attacker-controlled SNI to a regex-backed map, and the same complex value must reference a positional capture before the map output, such as return "$1$m". No earlier regex may have populated an equivalent capture before the length pass. The attacker needs no authentication, local access, or user interaction, but exploitation depends on this non-default configuration.
Proof of Concept
Environment Setup
Check out the confirmed vulnerable NGINX revision and build it with the stream preread module and AddressSanitizer:
Target Configuration
Save this as conf/capture-desync.conf and start NGINX in the foreground with ASAN_OPTIONS='detect_leaks=0:abort_on_error=1:symbolize=1' objs/nginx -p "$PWD" -c conf/capture-desync.conf:
Exploit Delivery
Send a TLS ClientHello whose SNI hostname contains 12,000 a bytes:
Outcome
The request proves that regex evaluation between the length and copy passes can turn a previously absent positional capture into an attacker-sized copy. On NGINX 1.30.4, 1.31.3, or a build containing the applicable bounds-check commits, the same request must not produce an ASan report or crash the worker; the script should stop safely when the predicted and actual copy lengths diverge.
Expected Response: A vulnerable ASan build aborts with the capture copy in the stack:
Run this level of analysis on your repo.
Winfunc traces source-to-sink paths, validates exploitability, and gives your team patch-ready remediation.
