Apache shipped httpd 2.4.71 on November 11, fixing three CVEs including CVE-2026-119618 โ a critical race condition in the mod_http2 module that leads to heap corruption and unauthenticated remote code execution on any server running HTTP/2 with default configuration. The bug was found independently by Anthropic’s OSS Scanner (ANT-2026-MAPKT6MZ) and Anthony Weems together with Simon Scannell of Google Cloud Vulnerability Research. Here is how the race works, how I reproduced it in a Docker lab using the stock official images, and how to fix it.
๐ Apache HTTP Server 2.4 Security Advisory ยท Fix commit 824beab9b9 ยท NVD page ยท Researcher PoC repo
The Basics
CVE-2026-119618 is a concurrent execution using shared resource without synchronisation (CWE-362 / CWE-366) in Apache httpd’s mod_http2 module. The race hits assign_headers() in h2_request.c when a worker thread writes a synthesised Content-Length header into the mod_http2 shared stream pool โ a data structure that the connection thread may be reading from simultaneously.
| Attribute | Value |
|---|---|
| CVE | CVE-2026-119618 |
| CVSS | 9.8 (Critical) |
| CWE | CWE-362 / CWE-366 (Race condition) |
| Attack vector | Network โ unauthenticated, low complexity, no user interaction |
| Affected component | mod_http2 (modules/http2/h2_request.c โ assign_headers()) |
| Affected versions | httpd 2.4.55 through 2.4.70 ยท mod_h2 2.0.8 through 2.0.42 |
| Fixed in | httpd 2.4.71 (released 2026-11-11) ยท mod_h2 2.0.43 |
| Discoverers | Anthropic OSS Scanner (ANT-2026-MAPKT6MZ), Anthony Weems & Simon Scannell (Google Cloud VR) |
Technical Deep Dive
The vulnerable function assign_headers() in modules/http2/h2_request.c copies HTTP/2 HEADERS frame data into the request record that the worker thread processes. When the request body has ended โ eos seen before headers arrive โ the function needs to signal a zero-length body. It does this by writing Content-Length: 0 into what it thinks is the request’s header table.
The bug: it wrote this value into req->headers โ a pointer to the mod_http2 shared stream pool โ instead of into r->headers_in, the Apache request object’s own header table. Because req->headers points to connection-level memory, a worker thread’s write here races against the connection thread’s concurrent reads and writes on the apr_table_t allocator. Two threads hitting the pool’s free-list simultaneously produces either a use-after-free (heap read into freed memory) or a double-free (APR assert failure). Both can escalate to RCE.
The fix is a one-line change in commit 824beab9b9:
# --- h2_request.c (assign_headers) ---
# Vulnerable (2.4.55-2.4.70):
apr_table_setn(req->headers, "Content-Length", "0");
# Fixed (2.4.71, commit 824beab9b9):
apr_table_setn(r->headers_in, "Content-Length", "0");The fixed version writes into r->headers_in, which is the per-request table that only one worker thread ever touches. The shared stream pool is no longer mutated and the race disappears.
Lab Reproduction
I set up a differential lab using the official Docker images โ httpd:2.4.70 (vulnerable) and httpd:2.4.71 (patched). No custom build, no ASan instrumentation โ just stock binaries with h2c (cleartext HTTP/2) enabled. (The same Docker host I use for reclaiming disk space was fine for this.)
# Start vulnerable side (httpd 2.4.70)
docker run -d --name lab-vuln -p 127.0.0.1:18080:80 \
-v $PWD/vconf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro \
-v $PWD/vlogs:/logs httpd:2.4.70
# Start patched side (httpd 2.4.71)
docker run -d --name lab-fixed -p 127.0.0.1:18081:80 \
-v $PWD/fconf/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro \
-v $PWD/flogs:/logs httpd:2.4.71The key config line that makes the bug visible is the LogFormat directive: %{{Content-Length}}i records the request’s incoming Content-Length header exactly as the request handler sees it. The researcher’s deterministic functional probe sends a single HTTP/2 request and then greps the access log โ no race needed.

Next I ran the race-inducing exploit mode against both sides โ 24 simultaneous HTTP/2 connections, each driving 32 concurrent streams with 78 filler headers to maximise contention on the stream pool allocator. The vulnerable side suffered 2 worker crashes with SIGSEGV during the 60-second run. The patched side handled identical traffic with zero crashes.

I also verified the published workaround โ removing h2/h2c from the Protocols directive. Against a 2.4.70 container with Protocols http/1.1 the PoC correctly reported “RESULT: MITIGATED โ HTTP/2 is not offered, the CVE is unreachable”.
Detection
On a non-instrumented server the race leaves traces in the error log. With the LogFormat adjusted, the access log also reveals the bug without needing to trigger it:
| Layer | Detection Signal |
|---|---|
| error.log | child pid N exit signal Segmentation fault (11) during HTTP/2 load |
| access.log | h2clen=”-” in the access log indicates Content-Length was written to the shared pool, not to r->headers_in |
| Worker crashes | Sudden spike in child process restarts under moderate HTTP/2 traffic |
| WAF | High rate of HEADERS frames >100 per connection; many PUSH_PROMISE resets |
# Find worker crashes linked to http2 traffic
grep -E "Segmentation fault|child pid.*exit signal" /var/log/apache2/error.log
# Probe without race โ access log Content-Length signature
# Requires: LogFormat '... h2clen="%{Content-Length}i"' common
grep -c 'h2clen="-"' /var/log/apache2/access.logMitigation
The definitive fix is upgrading to Apache httpd 2.4.71 (released 11 November 2026) or applying the one-line patch from commit 824beab9b9. If you use mod_h2 as a standalone module from GitHub, update to version 2.0.43.
| Platform | Fixed Version |
|---|---|
| Apache httpd source | 2.4.71 (cherry-pick 824beab9b9 on 2.4.55-2.4.70) |
| mod_h2 (GitHub) | v2.0.43 |
| Red Hat Enterprise Linux | mod_http2 package update in RHEL 9 / 10 |
| Ubuntu / Debian | apache2 update in 24.04 / 26.04 repos |
| Docker official | httpd:2.4.71 or later |
Workaround: remove h2 / h2c from the Protocols directive. This disables all HTTP/2 benefits, so treat it as a temporary control until the upgrade is deployed.
The assign_headers() race is one of those vulnerabilities that looks trivial in hindsight โ a single pointer dereference separating a safe per-request table from a shared pool turns a harmless header write into a concurrent-use-after-free. If you run Apache servers with HTTP/2 enabled the upgrade to 2.4.71 is the safest option. If you run Apache behind DirectAdmin, the Custom Build tool should pick up 2.4.71 automatically. For readers who prefer to see PoCs running in a sandbox, the PHP2Shell article from last month follows a similar containerised reproduction pattern.