Finding six NGINX vulnerabilities with open models

Author: Mufeed VH (Co-founder & CEO, Winfunc)
Published: July 23, 2026
Source: https://winfunc.com/research/endginx

Summary

Winfunc ran its autonomous vulnerability-research agent, driven by the open models GLM-5.1 and GLM-5.2 (from Z.AI), against the NGINX source tree. The scan produced six security findings carrying five CVE identifiers: two heap overflows in the HTTP/2 upstream request builders, one heap overflow in the rewrite engine, one heap overflow in the stream scripting engine, one HTTP/2 frame-injection bug, and one mTLS authorization bypass that accepted a revoked certificate. All were fixed upstream by F5/NGINX, and the advisories credit “Mufeed VH of Winfunc Research.” The report is notable both for the specific bugs — several rated CVSS 3.1 8.1 (High) in mature, heavily reviewed C code — and for the methodology: a read-only source-analysis agent that forms a hypothesis, then actively tries to disprove it, with AddressSanitizer/live-service reproduction handled by a separate build environment. Every finding traces back to one recurring pattern: two paired operations that were supposed to use the same value or state, but didn’t.

Technical Details

The six findings, with their affected components, root causes, and proof conditions as reported:

1. Stream TLS did not enforce the OCSP result — CVE-2026-28755 (Medium, CVSS 3.1 5.4, ngx_stream_ssl_module)

The HTTP TLS path calls ngx_ssl_ocsp_get_status() to consult the stored OCSP revocation result before allowing a request; the stream TLS path never made that call. ngx_stream_ssl_handler() checked only the standard chain-verification result (SSL_get_verify_result() and the client-certificate-required check). Because chain verification and OCSP validation are separate — OpenSSL can accept the chain while NGINX’s asynchronous OCSP check reports the leaf certificate revoked — a revoked certificate could complete stream mTLS. Conditions: NGINX terminates stream TLS, ssl_verify_client on and ssl_ocsp on are configured, and the attacker holds a revoked client certificate plus its private key; the connection then reaches the protected upstream until the certificate expires. Runtime validation confirmed application data flowing despite an OCSP “revoked” result. Affected: NGINX Open Source 1.27.2–1.29.6; fixed in 1.28.3 and 1.29.7.

2. HTTP/2 upstream proxy truncated an oversized body length — CVE-2026-42926 (Medium, CVSS 3.1 5.8 / CVSS 4.0 6.3, HTTP/2 upstream proxy)

An HTTP/2 DATA frame uses a 24-bit payload length (max 16,777,215). NGINX stored the generated proxy_set_body length in a size_t but copied only the low 24 bits into the frame header (header->length_0 = (u_char)(body_len >> 16), etc.), then appended the full body. A body exceeding the 24-bit limit is truncated in the header, so the upstream parser reads only the low-24-bit count as the DATA payload and reinterprets the remaining bytes as additional HTTP/2 frames — request-derived data can control those bytes, injecting attacker-chosen frames into the NGINX-to-upstream stream. This corrupts the upstream protocol stream; it does not overwrite the NGINX worker heap. Requires proxy_http_version 2 and a proxy_set_body value able to exceed 16,777,215 bytes, plus attacker-controlled data past the wrapped frame boundary. Affected: 1.29.4–1.30.0; fixed in 1.30.1 and 1.31.0 (commit c24fb259) by routing generated bodies through the normal bounded DATA-frame output path.

3. Overlapping rewrite captures caused an undersized allocation — CVE-2026-9256 (CVSS 3.1 8.1 / CVSS 4.0 9.2, HTTP rewrite engine)

NGINX compiles a rewrite directive into a length program (computes allocation size) and a copy program (writes output); both must compute the same size. A capture-only fast path could discard the exact capture-aware length bytecode. With overlapping capture groups — e.g. rewrite ^/((.*))$ http://127.0.0.1:18081/$1$2 redirect; where $1 and $2 have different capture numbers but cover the same URI bytes — the length path adds the URI’s escape expansion once while the copy path escapes $1 and $2 separately, escaping the overlapping bytes twice. When URI bytes require escaping, written > allocated. AddressSanitizer, using a URI with many + characters, confirmed a write past a 4,096-byte request-pool allocation in ngx_http_script_copy_capture_code(). Affected through 1.31.0; fixed in 1.30.2 and 1.31.1 (PR #1395, commit ca4f92a) by making the length calculation process each capture the same way the copy program does.

4. gRPC request builder undercounted HPACK length fields — CVE-2026-42055 (gRPC path) (CVSS 3.1 8.1 / CVSS 4.0 9.2, ngx_http_grpc_module)

HPACK uses variable-length integers. The gRPC upstream request builder reserved four bytes for an encoded forwarded-header length, but the HPACK encoder can require five bytes for a raw field above NGX_HTTP_V2_MAX_FIELD — one byte too small per oversized field, accumulating across multiple fields and across inserted CONTINUATION frames until the write runs past the temporary request buffer. Default NGINX rejects the oversized input; exploitation needs a non-default configuration that retains otherwise-invalid raw headers and raises buffer limits: ignore_invalid_headers off; large_client_header_buffers 8 5m; with grpc_pass. Retained headers reach ngx_http_grpc_create_request() with no outgoing max-field check before allocation. AddressSanitizer confirmed an out-of-bounds write during upstream gRPC request construction. The report characterizes this as unauthenticated worker memory corruption under a non-default configuration and does not claim reliable remote code execution; F5 notes code execution is possible only if address-space randomization is disabled or bypassed, which the validation did not demonstrate. The gRPC path dates back to 1.13.10.

5. HTTP/2 proxy request builder had the same HPACK size error — CVE-2026-42055 (proxy path) (CVSS 3.1 8.1, HTTP/2 upstream proxy)

A separate request builder, ngx_http_proxy_v2_create_request(), reserved the same four-byte allowance before calling the variable-length HPACK encoder. Selected by proxy_http_version 2, with the same large-header configuration and the five-byte raw (non-Huffman) encoding path reachable, the confirmed out-of-bounds write occurred in the ngx_memmove() loop that inserts CONTINUATION-frame headers into an already one-byte-short buffer. It is a distinct finding from the gRPC path because the two upstream modes are enabled independently and use different request builders reaching different write operations. This HTTP/2 upstream proxy path exists only in the newer 1.29.x line. Both CVE-2026-42055 paths were fixed by NGINX PR #1474 (commit 26d824e), which rejects forwarded field names/values above the supported HTTP/2 field limit before buffer allocation; Open Source fixes shipped in 1.30.3 and 1.31.2. F5 scores CVE-2026-42055 at 8.1 (CVSS 3.1) / 9.2 (CVSS 4.0).

6. Stream capture state changed between the length and copy passes — CVE-2026-42533 (CVSS 3.1 8.1 / CVSS 4.0 9.2, stream scripting engine)

Stream complex values also use a length program and a copy program, both reading regex captures from mutable stream-session fields (captures, ncaptures, captures_data) that must stay unchanged between passes. For an expression like return "$1$m"; — where $m is backed by a regex map over attacker-controlled TLS SNI — the length pass sees $1 as empty (0 bytes) but evaluating $m runs the map regex, mutating the shared capture state; the allocation is sized at one byte. The copy pass re-evaluates and now finds $1 referencing the attacker’s SNI capture. AddressSanitizer confirmed NGINX copying 12,000 bytes into a one-byte allocation in ngx_stream_script_copy_capture_code(). Affected: NGINX Open Source 0.9.6–1.31.2 (the widest range of the set); fixed in 1.30.4 and 1.31.3 via centralized bounds checks (PR #1561 mainline, PR #1563 stable backport) that stop the copy when a capture does not fit. F5’s CVSS 4.0 9.2 maps to Critical in its advisory, though Winfunc’s Hacktivity keeps the finding labeled High.

The shared root-cause pattern

Every finding involved two operations expected to use the same value or state but consuming different ones: a validation path versus an authorization path (OCSP); an in-memory size_t versus a 24-bit wire field (frame injection); URI-based sizing versus overlapping-capture copying (rewrite); a fixed byte allowance versus a variable-width HPACK integer (both HPACK bugs); and length-pass capture state versus copy-pass capture state (stream). Winfunc frames this as a reusable class of scan hypotheses — compare count vs. copy, validation vs. authorization, parser vs. forwarder, in-memory vs. wire-format size, and check whether mutable state, configuration, encoding, or integer width changes the value in between.

Impact

Outcomes span authentication bypass to heap memory corruption in NGINX worker processes. CVE-2026-28755 lets a holder of a revoked client certificate bypass stream mTLS revocation checks and reach protected upstreams. CVE-2026-42926 enables HTTP/2 frame injection into the NGINX-to-upstream stream (upstream-protocol corruption, not worker-heap overwrite). The four 8.1-rated bugs (CVE-2026-9256, both CVE-2026-42055 paths, and CVE-2026-42533) are heap out-of-bounds writes in NGINX worker processes, confirmed with AddressSanitizer; the reports describe unauthenticated worker memory corruption but do not demonstrate reliable code execution, and several require non-default configurations (large-header settings, proxy_http_version 2/grpc_pass, or stream scripting with attacker-controlled SNI). The stream capture bug (CVE-2026-42533) has by far the broadest version exposure, reaching back to NGINX 0.9.6. For context on the program’s scale, Winfunc says its public Hacktivity index holds 28 findings across 12 projects with 19 distinct CVEs (4 Critical, 11 High, 13 Medium), including recent React and Node.js CVEs and (redacted) Chromium findings.

Mitigation

All six issues are fixed upstream — update NGINX to a patched release:

  • CVE-2026-28755 → 1.28.3 / 1.29.7
  • CVE-2026-42926 → 1.30.1 / 1.31.0
  • CVE-2026-9256 → 1.30.2 / 1.31.1
  • CVE-2026-42055 (both gRPC and HTTP/2 proxy paths) → 1.30.3 / 1.31.2
  • CVE-2026-42533 → 1.30.4 / 1.31.3

Because several bugs require non-default settings, operators who cannot immediately patch can reduce exposure by reviewing those configurations: avoid ignore_invalid_headers off combined with very large large_client_header_buffers on grpc_pass/proxy_http_version 2 upstreams (the HPACK overflows), keep ssl_ocsp on enforcement in mind for stream mTLS (the OCSP bypass is a code defect fixed only by patching), and be cautious with stream complex-value expressions that mix regex-map variables and captures over attacker-controlled SNI. Consult the per-CVE F5/NGINX advisories for the authoritative supported-version and NGINX Plus matrices. NGINX Plus and mainline/stable ranges differ from the Open Source versions above.

References

Leave a Comment