Executive Summary
CVE-2026-23918 is a double-free memory corruption vulnerability in Apache HTTP Server 2.4.66's mod_http2 module that can be triggered remotely by sending a crafted HTTP/2 HEADERS + RST_STREAM sequence, resulting in a worker process crash (DoS). On Debian-derived systems and official Apache Docker images — where the Apache Portable Runtime (APR) uses an mmap-based allocator — attackers can escalate this into full Remote Code Execution (RCE). Active exploitation for DoS has been confirmed in the wild as of May 2026; organizations running Apache 2.4.66 should upgrade to 2.4.67 immediately or disable HTTP/2 as an interim control.
1. What Is This Vulnerability?
CVE-2026-23918 is a double-free flaw in the stream cleanup path of Apache's mod_http2 module, specifically in modules/http2/h2_mplx.c. A double-free occurs when the same memory address is passed to the allocator's free() function (or equivalent) more than once, corrupting the heap's internal state. In Apache's case, the second free hits an apr_pool_destroy() call on memory that has already been returned to the allocator — a condition that can produce crashes, memory leaks, or — under the right conditions — arbitrary code execution.
Root Cause: The Early Reset Race
The bug is activated by an "early stream reset" sequence: the client opens an HTTP/2 stream with a HEADERS frame and immediately sends RST_STREAM with a non-zero error code before the server's multiplexer has finished registering the stream. This is a legitimate HTTP/2 flow supported by RFC 9113 §5.4, so the server cannot simply reject it.
When this sequence arrives, two nghttp2 library callbacks fire back-to-back on the connection thread:
on_frame_recv_cb(handling theRST_STREAMframe) → callsh2_mplx_c1_client_rst()→ callsm_stream_cleanup(), which pushes theh2_stream *pointer onto thespurge(stream-purge) cleanup array.on_stream_close_cb(handling the stream close) → callsh2_mplx_c1_client_rst()→ callsm_stream_cleanup()again for the same stream, pushing the same pointer ontospurgea second time.
Later, when c1_purge_streams() iterates the spurge array and calls h2_stream_destroy() → apr_pool_destroy() on each entry, the second iteration destroys a pool that was already freed on the first pass — triggering the double-free.
/* Vulnerable path in h2_mplx.c (simplified) */
static void m_stream_cleanup(h2_mplx *m, h2_stream *stream)
{
/* ... bookkeeping ... */
H2_MPLX_STREAM_LIST_REMOVE(m, stream);
/* BUG: stream can be pushed here even if already added */
h2_iq_append(m->spurge, stream); /* push to purge queue */
}
/* Later, in c1_purge_streams() */
while ((stream = h2_iq_shift(m->spurge)) != NULL) {
h2_stream_destroy(stream); /* second call = double-free */
}
Attack Vector
Exploitation is straightforward and requires no authentication:
# Minimal trigger (HEADERS + immediate RST_STREAM)
HEADERS frame (stream id = 1, END_HEADERS)
RST_STREAM (stream id = 1, error_code = CANCEL)
A public proof-of-concept (xeloxa/CVE-2026-23918-Apache-H2-PoC) implements both Rapid-RST (high-rate DoS) and Slow-Drip (stealthy low-rate DoS) modes, plus passive RCE/vulnerability detection for Apache 2.4.66 targets.
RCE Path (Debian / Docker)
On systems where APR uses the mmap allocator (the default on Debian, Ubuntu, and official Apache Docker images), the double-free opens a practical RCE path:
- The freed
h2_streamvirtual address becomes available formmap()reuse. - An attacker-controlled
mmap()call places a fakeh2_streamstruct at that address with its pool cleanup function pointer replaced withsystem(). - Apache's scoreboard — which resides at a fixed address throughout the server's lifetime even under ASLR — is used as a stable staging area for the fake structs and command strings.
- When the next cleanup fires on the poisoned pool,
system(attacker_command)executes in the Apache worker's security context.
Real-World Impact
As of May 2026, exploitation for Denial of Service is confirmed in the wild, with threat actors crashing Apache worker processes to cause repeated service disruptions. While RCE exploitation has not been publicly confirmed, the PoC-level technical primitives are documented and the mmap path is considered practical on affected Linux distributions.
2. Who Is Affected?
| Factor | Details |
|---|---|
| Affected version | Apache HTTP Server 2.4.66 only |
| Fixed version | Apache HTTP Server 2.4.67 (released May 4, 2026) |
| Module required | mod_http2 must be loaded and HTTP/2 enabled |
| MPM requirement | Multi-threaded MPM required: worker or event (default on most modern installs) |
| Not affected | MPM prefork (single-threaded), all versions ≤ 2.4.65, all versions ≥ 2.4.67 |
| RCE elevated risk | Debian, Ubuntu, and official Apache Docker images (APR mmap allocator) |
| No auth required | Attacker needs only network access to port 443/80 with HTTP/2 |
Given that Apache HTTP Server powers roughly 20–25% of all active websites globally, and 2.4.66 was the current stable release for several months before the patch, the potential blast radius is extremely large.
3. How to Detect It (Testing)
Step 1 — Identify Vulnerable Servers
# Check Apache version
httpd -v
# or
apache2 -v
# Expected vulnerable output:
# Server version: Apache/2.4.66 (Unix)
# Check if mod_http2 is loaded
httpd -M 2>&1 | grep http2
# Vulnerable output:
# http2_module (shared)
# Check active MPM
httpd -V | grep MPM
# Vulnerable if output is "worker" or "event"
Step 2 — Confirm HTTP/2 is Enabled on the Server
# Use curl to verify HTTP/2 support
curl -sI --http2 https://your-server.example.com | head -5
# Vulnerable output includes: HTTP/2 200
# Using nmap with http2 script
nmap -p 443 --script http2-enabled your-server.example.com
Step 3 — Test for the Double-Free Trigger (Safe DoS Probe)
Use the public PoC in a controlled, authorized environment:
# Clone PoC
git clone https://github.com/xeloxa/CVE-2026-23918-Apache-H2-PoC
cd CVE-2026-23918-Apache-H2-PoC
# Passive detection only (no crash) — checks version & HTTP/2 header
python3 poc.py --target https://your-server.example.com --mode detect
# Expected output if vulnerable:
# [!] Target Apache/2.4.66 detected
# [!] HTTP/2 enabled — mod_http2 present
# [VULNERABLE] CVE-2026-23918 conditions met
⚠️ Only run active crash tests against systems you own or have written authorization to test. The Rapid-RST mode will crash worker processes.
Step 4 — Check for Signs of Exploitation in Logs
# Look for repeated worker restarts (sign of DoS exploitation)
grep -i "caught SIGTERM" /var/log/apache2/error.log | tail -20
# Look for unexpected child exits
grep -i "child pid.*exit signal" /var/log/apache2/error.log | tail -20
# High-frequency RST_STREAM patterns in access log (requires HTTP/2 access logging)
grep "RST_STREAM" /var/log/apache2/access.log | wc -l
Automated Scanning
Nuclei template:
id: CVE-2026-23918-detect
info:
name: Apache HTTP/2 Double-Free (CVE-2026-23918)
severity: critical
http:
- method: GET
path:
- "{{BaseURL}}"
headers:
Upgrade: h2
matchers-condition: and
matchers:
- type: word
part: header
words:
- "Apache/2.4.66"
- type: word
part: header
words:
- "HTTP/2"
Shodan query to find exposed 2.4.66 instances:
"Apache/2.4.66" http.component:"mod_http2"
Code Review Checklist
- Confirm
ServerTokensis not set toFullorOS(limit version disclosure) - Verify
mod_http2is only loaded where HTTP/2 is genuinely required - Confirm the MPM is listed in
httpd -Voutput and matches expectations - Review Dockerfile/Ansible/Puppet configs — confirm base image does not pin to Apache 2.4.66
4. How to Fix It (Mitigation)
Step-by-Step Remediation
Option A — Upgrade to Apache 2.4.67 (Recommended)
-
Identify your package manager and current version:
httpd -v # or apache2 -v -
Upgrade via package manager:
# Debian / Ubuntu sudo apt update && sudo apt install --only-upgrade apache2 # RHEL / CentOS / AlmaLinux sudo dnf update httpd # Compile from source wget https://downloads.apache.org/httpd/httpd-2.4.67.tar.gz tar xzf httpd-2.4.67.tar.gz && cd httpd-2.4.67 ./configure --with-mpm=event --enable-http2 make && sudo make install -
Restart Apache:
sudo systemctl restart apache2 # Debian/Ubuntu sudo systemctl restart httpd # RHEL/CentOS -
Verify the new version:
apache2 -v # Server version: Apache/2.4.67 (Unix) ← patched
Option B — Disable mod_http2 (Interim)
If upgrading immediately is not possible, disable HTTP/2:
# In httpd.conf or your virtual host config:
# Comment out or remove mod_http2 loading
# LoadModule http2_module modules/mod_http2.so
# Also disable the protocol in VirtualHost blocks
<VirtualHost *:443>
# Remove or comment out this line:
# Protocols h2 http/1.1
Protocols http/1.1
</VirtualHost>
Reload Apache:
sudo systemctl reload apache2
Option C — Switch to MPM Prefork (Not Recommended for Production)
# In /etc/apache2/mods-available/mpm_event.conf (disable event)
# Enable prefork instead:
sudo a2dismod mpm_event
sudo a2enmod mpm_prefork
sudo systemctl restart apache2
⚠️ Prefork is single-threaded and significantly less performant. Use only as a last resort in low-traffic environments.
Configuration Hardening (Apply Regardless of Option Chosen)
# Limit HTTP/2 streams per connection to reduce attack surface
H2MaxSessionStreams 100
# Reduce the timeout window for stream resets
H2StreamTimeout 15
# Restrict H2 to TLS connections only (not plain text)
Protocols h2 http/1.1
# Remove 'h2c' if present — prevents cleartext HTTP/2
# Limit server version disclosure
ServerTokens Prod
ServerSignature Off
5. How to Test the Fix (Validation)
Regression Test Scenarios
- Scenario A: After patching to 2.4.67, run the PoC's
--mode detect— it should report NOT VULNERABLE. - Scenario B: Send a legitimate HTTP/2 request to confirm normal service continues:
curl --http2 -sI https://your-server.example.comshould returnHTTP/2 200. - Scenario C: Run your application's full regression suite to confirm no functionality is broken by the upgrade.
Security Test Cases
Test Case 1 — Vulnerability No Longer Present
| Field | Value |
|---|---|
| Precondition | Apache upgraded to 2.4.67, mod_http2 loaded, event MPM |
| Steps | Send HEADERS + RST_STREAM sequence using PoC --mode detect |
| Expected Result | No crash; PoC reports "NOT VULNERABLE" |
Test Case 2 — Attack Path Fails Safely
| Field | Value |
|---|---|
| Precondition | Apply patch or disable mod_http2 |
| Steps | Send Rapid-RST sequence (10 streams/sec) for 30 seconds |
| Expected Result | Workers remain stable; no SIGTERM in error.log; service responds normally |
Test Case 3 — HTTP/2 Still Functions Normally
| Field | Value |
|---|---|
| Precondition | Apache 2.4.67 with mod_http2 loaded |
| Steps | Load test with 100 concurrent HTTP/2 connections using h2load |
| Expected Result | All requests succeed; no worker crashes |
Automated Validation
# Confirm version with automated check
python3 - <<'EOF'
import subprocess, sys
result = subprocess.run(["apache2", "-v"], capture_output=True, text=True)
version_line = result.stdout.strip()
print(version_line)
if "2.4.67" in version_line or "2.4.6" not in version_line:
print("[PASS] Apache version is patched or unaffected")
sys.exit(0)
elif "2.4.66" in version_line:
print("[FAIL] Apache 2.4.66 detected — CVE-2026-23918 present")
sys.exit(1)
EOF
# Confirm HTTP/2 still works post-patch
curl -sv --http2 https://localhost/ 2>&1 | grep -E "< HTTP/|< server:"
6. Prevention & Hardening
Best Practices
Patch cadence: Apache HTTP Server is actively maintained and releases security patches through its minor version series. Subscribe to the Apache httpd-announce mailing list or monitor the Apache security page to receive timely notification of new releases.
Principle of least module: Only load Apache modules you actively use. HTTP/2 (mod_http2) should be disabled on servers where HTTP/2 is not a business requirement. Review httpd -M output periodically to audit loaded modules.
Container base images: If you use official Apache Docker images, pin to httpd:2.4.67 or later in your Dockerfile rather than httpd:latest. Establish an automated scan (e.g., Snyk Container, Trivy, Grype) in your CI/CD pipeline to flag outdated base images.
Defense in depth: Place Apache servers behind a WAF or reverse proxy (e.g., Nginx, HAProxy, AWS CloudFront) that can terminate HTTP/2 and rate-limit RST_STREAM floods before they reach Apache worker processes.
Regular vulnerability scanning: Use Trivy, Grype, or similar SCA tools in your pipelines:
# Trivy: scan running container
trivy image your-apache-image:tag
# Grype: scan file system
grype /
Monitoring & Detection
Operators should baseline normal worker restart rates and alert on deviations. Key signals to watch:
# Prometheus/Grafana: alert on mod_status worker state anomalies
# In Apache config, enable mod_status:
<Location "/server-status">
SetHandler server-status
Require ip 127.0.0.1
</Location>
# Key metrics to monitor:
# - apache_workers{state="closing"} rising rapidly → RST flood in progress
# - OS-level: repeated apache2 worker process respawns in systemd journal
sudo journalctl -u apache2 --since "1 hour ago" | grep -i "child\|crash\|signal"
# SIEM detection rule (pseudo-code):
# ALERT when: count(apache_error_log contains "caught SIGTERM") > 5 in 60s
# OR count(apache_error_log contains "double free") > 0
Network-level indicators: A flood of HTTP/2 RST_STREAM frames from a single IP (or small range) within a short window is a strong indicator of CVE-2026-23918 exploitation. If your load balancer or WAF supports HTTP/2 frame inspection, alert on:
- More than 50
RST_STREAMframes from a single source IP in under 10 seconds RST_STREAMframes with error code0x8(CANCEL) on streams with no corresponding response
References
- NVD: CVE-2026-23918 Detail
- Apache Security Advisory: Apache HTTP Server 2.4 vulnerabilities
- OSS-Security Disclosure: oss-security CVE-2026-23918 post
- Hacker News Coverage: Critical Apache HTTP/2 Flaw — The Hacker News
- Security Affairs Analysis: Apache fixes critical HTTP/2 double-free flaw
- Detailed Technical Writeup: Hadrian.io — Apache CVE-2026-23918 RCE Explained
- Rescana Analysis: Apache HTTP Server 2.4.66 Vulnerability Deep Dive
- Public PoC: xeloxa/CVE-2026-23918-Apache-H2-PoC (GitHub)
- Patch Download: Apache HTTP Server 2.4.67 Downloads
- SOCRadar Blog: CVE-2026-23918 Apache HTTP/2 Double Free With Possible RCE