Executive Summary
CVE-2026-47291 is a critical remote code execution vulnerability in HTTP.sys, the kernel-mode HTTP protocol driver that underpins Internet Information Services (IIS) and all other Windows applications that register URL prefixes. The root cause is a 16-bit integer overflow in the buffer reference array growth logic triggered during HTTP/1.x header parsing over TLS connections. After 13,107 array-growth events, the capacity counter wraps to zero, causing a subsequent memmove to copy approximately 524 KB from an existing pool allocation into a freshly allocated 40-byte buffer — producing a massive kernel pool heap overflow exceeding 500 KB. The vulnerability was patched by Microsoft in the June 2026 Patch Tuesday release cycle.
Successful exploitation requires a remote, unauthenticated attacker to send approximately 65,536 crafted HTTP/1.x header lines, each encapsulated in its own TLS 1.3 application data record, over a sustained HTTPS connection. At 10 ms per record the attack takes roughly 11 minutes to trigger. The default MaxRequestBytes registry value of 16,384 bytes prevents exploitation on unmodified systems; however, any server that has raised this limit to 262,144 bytes or above is exposed. The minimum impact is an unexpected kernel-level system crash (BSoD), while arbitrary kernel-mode code execution is achievable under suitable heap layout conditions.
The Vulnerability
HTTP.sys is the kernel-mode HTTP protocol driver in Microsoft Windows. It handles HTTP request parsing, response caching, and SSL/TLS termination for IIS and any other application that registers a URL prefix. The driver listens on TCP port 80 (HTTP) and 443 (HTTPS) by default and processes inbound HTTP/1.x and HTTP/2 requests entirely at the kernel level.
When operating over HTTPS, HTTP.sys delegates TLS processing to the Windows Secure Channel (SChannel) provider. Inbound TCP data is decrypted on a per-record basis: each TLS record is an independent unit of encryption, decrypted separately by SChannel and delivered to HTTP.sys as a distinct plaintext buffer. A single TLS 1.3 application data record has the following structure:
Offset Size Field
------------------------------------------
0x00 1 ContentType (0x17 = application_data)
0x01 2 ProtocolVersion (0x0303)
0x03 2 Length (of encrypted payload)
0x05 variable Encrypted payload
Source: original article.
The decrypted payload of each TLS record is delivered independently to the HTTP parser via UlHttpBufferReceiveEvent(), regardless of how many TLS records the underlying TCP stack coalesces into a single TCP segment. This stands in contrast to plaintext HTTP, where the TCP stack and UlpMergeBuffers() collapse multiple receive indications before the data reaches HTTP.sys.
The HTTP parser maintains a per-request state object containing a dynamically grown buffer reference array. Three fields govern this structure:
capacity— total number of allocated slots (stored as a 16-bit unsigned integer at offset0x640)count— number of slots currently in use (16-bit unsigned integer at offset0x642)ref_array_ptr— pointer to the heap-allocated array of 8-byte buffer reference entries (at offset0x648)
The integer overflow resides in an inline buffer-reference routine called by UlpParseNextRequest() each time a new receive buffer is consumed during HTTP/1.x header parsing. When count reaches capacity, the routine grows the array by allocating a new buffer with five additional slots. The new allocation size is computed as 0x28 + capacity * 8; the old array contents are copied via memmove using count * 8 bytes; and capacity is then incremented by 5 as a 16-bit unsigned integer — with no overflow check performed on this addition.
After 13,107 growth events, capacity reaches 0xFFFB. The next addition of 5 produces 0x10000, which truncates to 0x0000 in the 16-bit field. On the very next buffer reference addition, count (now 65,536 or greater) exceeds the zero capacity, triggering yet another growth. The allocation size computation 0x28 + 0 * 8 produces a 40-byte allocation, but memmove copies count * 8 bytes — approximately 524,256 bytes — from the old buffer into the 40-byte allocation. This is a kernel pool heap buffer overflow of over 500 KB.
The critical enabler is the 1:1 correspondence between TLS records and buffer references over HTTPS. For plaintext HTTP the TCP stack and UlpMergeBuffers() collapse multiple receive indications, so each buffer reference may cover many header lines. Over TLS, each record is decrypted independently by SChannel and delivered through UlHttpBufferReceiveEvent() into UlpCopyIndicatedData(). If each TLS record carries exactly one complete header line (terminated by CRLF), the HTTP parser fully consumes the buffer without setting the partial-parse flag, causing UlpAdjustBuffers() to advance via its non-merge path — one buffer reference accumulated per TLS record sent.
To trigger the overflow, an attacker crafts an HTTP request in which every header line is encapsulated in a separate TLS application data record. Given a minimum header line size of approximately 4 bytes and a required count of 65,536 buffer references, the total request size is roughly 262,144 bytes. The MaxRequestBytes registry value (at HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters) must be configured to at least 262,144 bytes for the server to accept a request of this size. The default value of 16,384 bytes limits the request to approximately 4,000 header lines, which is insufficient to reach the overflow. Keeping MaxRequestBytes at or below 65,535 bytes is the most conservative mitigation available on unpatched systems.
A remote unauthenticated attacker can exploit this vulnerability by sending a specially crafted HTTP/1.x request over a TLS connection to any affected server. At minimum, successful exploitation results in unexpected system termination due to a memory access exception in the context of the kernel. Under specific memory layout conditions it can result in arbitrary code execution in the context of the kernel.
Notes:
- The vulnerability is only reachable through HTTP/1.x header parsing over TLS connections. HTTP/2 and HTTP/3 use different parser paths that do not interact with the buffer reference array.
- Body data parsing (Content-Length or chunked transfer encoding) does not add entries to the buffer reference array. Only header parsing triggers buffer reference growth.
- At a sending rate of 10 milliseconds per TLS record, the overflow requires approximately 11 minutes to trigger.
Source Code Walkthrough
The following code snippet was taken from HTTP.sys version 10.0.26100.7705. Comments added by TrendAI Research are included inline. The decompiled excerpt shows the buffer reference array growth logic inside UlpParseNextRequest(), illustrating the vulnerable 16-bit addition at offset 0x640 and the subsequent oversized memmove that overwrites memory beyond the 40-byte allocation.
In UlpParseNextRequest():
__int64 __fastcall UlpParseNextRequest(
__int64 request_object,
__int64 buffer,
unsigned int buffer_length)
{
[...truncated for readability...]
capacity = *(unsigned __int16 *)(request_object + 0x640);
count = *(unsigned __int16 *)(request_object + 0x642);
// Check whether the buffer reference array needs to grow
if (count >= capacity)
{
// Allocation size: 0x28 header bytes plus capacity * 8 bytes per entry
// When capacity has wrapped to 0, this computes a 40-byte allocation
alloc_size = 0x28 + (unsigned __int64)capacity * 8;
new_buffer = ExAllocatePool3(0x42, alloc_size, 'lSmU');
if (!new_buffer)
return STATUS_INSUFFICIENT_RESOURCES;
old_buffer = *(__int64 *)(request_object + 0x648);
//Copies count * 8 bytes from the old array into the new allocation
// After wrap: copies approximately 524 KB into the 40-byte allocation
memmove(new_buffer, old_buffer, (unsigned __int64)count * 8);
//16-bit unsigned addition with no overflow check
// After 13,107 growths: 0xFFFB + 5 = 0x10000, truncated to 0x0000
*(unsigned __int16 *)(request_object + 0x640) = capacity + 5;
ExFreePoolWithTag(old_buffer, 'lSmU');
*(__int64 *)(request_object + 0x648) = new_buffer;
}
// Appends the new buffer reference to the array
ref_array = *(__int64 *)(request_object + 0x648);
*(__int64 *)(ref_array + (unsigned __int64)count * 8) = buffer;
*(unsigned __int16 *)(request_object + 0x642) = count + 1;
[...truncated for readability...]
}
Source: original article. HTTP.sys version 10.0.26100.7705, decompiled by TrendAI Research.
Detection Guidance
To detect an attack exploiting this vulnerability, the detection device must monitor and parse traffic on TCP port 443. Because all traffic on this port is TLS-encrypted, the device must be able to decrypt the session before applying the primary detection method.
An HTTP/1.x request consists of a request line followed by zero or more header field lines, each terminated by CRLF. The following ABNF grammar defines the relevant structure:
HTTP-request = request-line *( header-field CRLF ) CRLF
request-line = method SP request-target SP HTTP-version CRLF
header-field = field-name ":" OWS field-value OWS
Source: original article.
Decrypted Traffic Inspection
After decrypting the TLS session, the detection device must parse the HTTP/1.x request headers and count the number of distinct header field lines in a single HTTP request. If the number of header field lines in a single request exceeds 1,000, the traffic should be considered suspicious; an attack exploiting this vulnerability is likely underway.
Encrypted Traffic Heuristics
Where decryption is not available, the detection device should inspect the pattern of TLS application data records within the encrypted session. If each TLS application data record contains a single short payload and the total number of such records on a single connection exceeds 1,000, the traffic should be considered suspicious.
Notes:
- The preferred detection method (header line count) requires the ability to decrypt TLS traffic — for example through TLS inspection, a decrypting proxy, or possession of the server private key. This method directly observes the attack indicator and produces low false-positive and false-negative rates.
- The TLS record heuristic operates on encrypted traffic without requiring decryption. It is more prone to false positives (legitimate applications such as interactive streaming sessions may send many small TLS records) and to false negatives (the threshold is based on observable record sizes rather than the actual header count that determines exploitability). Where possible, decrypted traffic inspection should be preferred.
- The attack requires approximately 11 minutes of sustained connection to accumulate sufficient header lines. Connection duration monitoring may serve as a supplementary detection heuristic.
Key Takeaways
- Kernel-mode 16-bit integer overflow: The capacity field in the HTTP.sys buffer reference array uses a 16-bit unsigned type. After 13,107 growth events, adding 5 wraps the field from
0xFFFBto0x0000with no check. - Massive kernel heap overflow: The resulting
memmovecopies ~524 KB into a 40-byte pool allocation, making this one of the largest kernel pool overflows disclosed in a Windows network driver. - TLS is the essential enabler: SChannel delivers each TLS record as a separate plaintext buffer, creating a 1:1 mapping between records sent and buffer references accumulated. Plaintext HTTP connections are not exploitable because the TCP stack coalesces receive indications.
- Zero authentication required: The attack path is fully unauthenticated and remote. No user interaction is needed.
- Default configuration is safe: The default
MaxRequestBytesvalue of 16,384 bytes limits requests to ~4,000 header lines, far below the 65,536 required. Only servers with elevated limits are vulnerable. - Impact spans DoS to RCE: The primary outcome is a kernel crash (BSoD); arbitrary kernel-mode code execution is achievable under specific heap layout conditions.
- Patched in June 2026: Microsoft addressed this in the June 2026 Patch Tuesday release. Apply the patch; registry mitigations are a secondary fallback only.
Defensive Recommendations
- Apply the June 2026 Microsoft patch immediately. This is the only complete remediation. All other measures reduce exposure but do not eliminate the underlying vulnerability.
- Audit
MaxRequestByteson all IIS and HTTP.sys hosts. CheckHKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters. If the value is 65,536 or higher, the unpatched system is exploitable. Set it to 65,535 or below as an interim measure. - Deploy TLS inspection at the network perimeter. The most reliable detection method requires decrypted HTTP/1.x traffic. A TLS inspection device or decrypting proxy enables the accurate header-count rule (>1,000 header lines per request = block/alert).
- Add IDS/IPS signatures for the header-count threshold. For environments with TLS decryption, alert and block any HTTP/1.x request exceeding 1,000 header lines. For encrypted-only inspection, alert on connections with more than 1,000 short TLS application data records in combination with a connection duration exceeding 60 seconds.
- Place IIS instances behind a reverse proxy or WAF that enforces request size limits and header count limits before traffic reaches the kernel driver. This adds a layer of defence in depth even after patching.
- Disable HTTP/1.x if not required. HTTP/2 and HTTP/3 code paths in HTTP.sys are not affected. Where all clients support HTTP/2 or HTTP/3, disabling HTTP/1.x eliminates the attack surface entirely.
- Monitor for anomalous long-lived HTTPS connections with repetitive short TLS records as a behavioral indicator. Eleven minutes of sustained high-frequency small-record traffic is abnormal for virtually all legitimate HTTPS workloads.
- Track remediation in your vulnerability management platform across all Windows servers that expose HTTPS through HTTP.sys, including non-IIS applications using URL prefix registration.
Conclusion
CVE-2026-47291 is a stark reminder that low-level integer type choices in kernel-mode network code carry serious security consequences. A single missing overflow check on a 16-bit capacity field in Windows HTTP.sys enables a remote, unauthenticated attacker to corrupt over 500 KB of kernel pool memory via a sustained but straightforward sequence of crafted TLS records. While the default registry configuration limits exposure, any server operator who has raised MaxRequestBytes — a common tuning for high-throughput IIS deployments — should treat unpatched systems as critically exposed. Apply Microsoft’s June 2026 patch, harden registry settings as an interim measure, and add detection rules tuned to the 1,000-header-line threshold identified by the researchers.
Original text: “CVE-2026-47291: Remote Code Execution in the Windows HTTP.sys” by Yazhi Wang and Jonathan Lein at TrendAI Zero Day Initiative.

