The Exploit
An unauthenticated remote attacker with the ability to send HTTP/2 requests to an NGINX server configured with proxy_http_version 2 or grpc_pass, ignore_invalid_headers off, and large_client_header_buffers > 2MB can trigger a heap buffer overflow by sending an oversized header name or value.
## Send a crafted HTTP/2 request with an oversized header value (>16384 bytes)
## Replace TARGET_HOST with the actual NGINX server
curl -k --http2 -H "X-CustomHeader: $(python3 -c 'print("A"*20000)')" \
"https://TARGET_HOST/some-path"
When the request is processed, the NGINX worker process will crash and restart, observable in the error log as a SIGSEGV or heap corruption message. In environments without ASLR, or if ASLR is bypassed, this crash can be escalated to remote code execution.
What the Patch Did
Before (vulnerable code in ngx_http_grpc_module.c):
// No check for NGX_HTTP_V2_MAX_FIELD on header name or value
len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len
+ NGX_HTTP_V2_INT_OCTETS + val_len;
After (fixed code):
if (key_len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"too long http2 header name");
return NGX_ERROR;
}
if (val_len > NGX_HTTP_V2_MAX_FIELD) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"too long http2 header value");
return NGX_ERROR;
}
len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len
+ NGX_HTTP_V2_INT_OCTETS + val_len;
The patch adds explicit size validation checks for both header names and values against the NGX_HTTP_V2_MAX_FIELD constant (16384 bytes). This is a classic input validation fix — the ngx_http_v2_module API already defined the field size limit, but the ngx_http_grpc_module was not enforcing it. The patch also adds corresponding checks for method, URI, and host fields.
Root Cause
CWE-122: Heap-based Buffer Overflow. The vulnerability exists because when an HTTP/2 request is proxied upstream via the gRPC module, the code constructs the upstream HTTP/2 HEADERS frame by directly calculating buffer lengths from the input header fields. The upstream buffer is pre-allocated based on initial estimates, but attacker-supplied header names and values can exceed the NGX_HTTP_V2_MAX_FIELD limit (16384 bytes) and the internal buffer size. Specifically, the len variable accumulates field sizes without validation, then a ngx_pnalloc() call allocates a buffer large enough for the attacker-controlled length — but the actual padding and framing overhead can cause the downstream WRITE operation to overflow the heap allocation when large_client_header_buffers is set to a size larger than 2MB. The dataflow: HTTP/2 HEADERS frame → ngx_http_grpc_create_request() → unchecked key_len / val_len → heap buffer write without bounds check.
Why It Works
The single load-bearing line is the val_len check: if (val_len > NGX_HTTP_V2_MAX_FIELD). If you removed only this check, an attacker could still send headers with values longer than 16384 bytes and overflow the buffer. The other checks for method, URI, host, and header name length are defense-in-depth — they prevent similar overflows from other field types but are not strictly necessary to block the most common attack vector (oversized header values). The engineer added them because any of these fields could theoretically be the overflow vector if the attacker controls enough of the request structure, and because consistency with the HTTP/2 specification's field size limits reduces the overall attack surface.
Hardening Checklist
- Implement maximum field length checks using
NGX_HTTP_V2_MAX_FIELD(or equivalent constant) for all user-controllable fields that are used in buffer size calculations, not just the ones that appear in the first exploit attempt. - Add a
ngx_conf_check_value()handler in the configuration parsing forlarge_client_header_buffersto reject values above 2MB when proxy HTTP/2 is enabled, preventing the preconditions for the overflow. - Use
ngx_pnalloc()with explicit bounds checking viangx_slab_alloc()or add a post-allocation size verification that compares the actual allocated size against the computed framing overhead. - Enforce HTTP/2 max frame size limits at the protocol level using
ngx_http_v2_module'sSETTINGS_MAX_FRAME_SIZEbefore any upstream encoding, rather than relying on later validation in the module-specific code. - Add unit tests that feed oversize headers (with lengths just above and well above NGX_HTTP_V2_MAX_FIELD) to the request creation path and assert failure, preventing regression.
References
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-42055