On May 4, 2026, the Apache Software Foundation released version 2.4.67 of its HTTP server, fixing five vulnerabilities. The most severe, CVE-2026-23918 (CVSS 8.8), is a double-free in the HTTP/2 implementation that allows a trivial denial of service and, in certain configurations, unauthenticated remote code execution. And the worst part: it affects the default configurations of many Debian distributions and official Docker images.
Since Apache httpd is one of the most widely deployed web servers in the world, and HTTP/2 is enabled by default, this flaw is worth a closer look — not just to patch it, but to understand why a memory-management bug that's several years old only became exploitable now.
What are we talking about? The double-free in a nutshell
A double-free (CWE-415) occurs when a program frees the same region of memory twice. The second call to free() operates on a pointer that no longer belongs to it: depending on the allocator's state, this triggers a crash (DoS) or, if an attacker controls what was reallocated in the meantime, a heap corruption that can be exploited to hijack the execution flow (RCE).
Here, the doubly freed region is an HTTP/2 stream object (h2_stream), destroyed twice during stream cleanup in mod_http2.
The root cause: a duplicated stream cleanup
The vulnerability lives in the stream cleanup path, specifically in h2_mplx.c. The trigger is a timing issue in the ordering of HTTP/2 frames:
The client sends aHEADERSframe immediately followed by aRST_STREAMwith a non-zero error code, on the same stream, before the multiplexer has registered that stream.
Two callbacks from the nghttp2 library then fire in sequence:
on_frame_recv_cb(for the RST_STREAM)on_stream_close_cb(for the stream close)
Both end up calling h2_mplx_c1_client_rst → m_stream_cleanup, which pushes the same h2_stream pointer twice onto the spurge cleanup array. Later, c1_purge_streams walks this array and calls h2_stream_destroy → apr_pool_destroy on each entry. The second pass hits memory that is already freed: double-free.
The core of the problem is unsynchronized ownership tracking between the HTTP/2 cleanup callbacks — exactly the kind of concurrency bug that multithreaded servers make hard to reason about.
Why only now? The effect of an allocator change
The most instructive detail: the latent bug probably existed earlier, but it only became an exploitable double-free with version 2.4.66, due to a memory allocator change in mod_http2 v2.0.33 shipped with that release. It's a classic reminder: a security vulnerability isn't just a faulty line of code, it's also the execution context that makes it (or not) triggerable.
Are you vulnerable? The three conditions
Three prerequisites must be met:
- Apache HTTP Server 2.4.66 (the version where the allocator makes the bug exploitable).
- HTTP/2 enabled — which is the default as soon as
mod_http2is loaded in 2.4.66. - A multithreaded MPM:
workerorevent. ThepreforkMPM (single-threaded) is not affected, since the bug doesn't manifest in that model.
Quick check:
# Apache version (watch out for distro backports)
httpd -v # RHEL/CentOS
apache2ctl -v # Debian/Ubuntu
# Is mod_http2 loaded?
apache2ctl -M 2>/dev/null | grep http2
# Current MPM
apache2ctl -V 2>/dev/null | grep -i mpm
Trivial DoS, conditional RCE: the real risk level
The two impacts must be distinguished:
- Denial of service — the path is trivial and requires no authentication: a few well-ordered frames are enough to crash the worker processes. One source reports DoS exploitation already observed in the wild.
- Remote code execution — far more sophisticated, but proven viable by the researchers. It is made easier on deployments using APR with mmap — precisely the configuration of Debian systems and official Docker images. A PoC exists; no mass public RCE exploitation observed to date.
In other words: if you're running Apache 2.4.66 in an official Docker container, you're in the most exposed scenario. The flaw was discovered by Bartlomiej Dmitruk and Stanislaw Strzalkowski during an internal code review, and reported on December 10, 2025.
Remediation: patch, or mitigate in the meantime
The only complete remediation is upgrading to Apache 2.4.67+, which removes the vulnerable cleanup path entirely.
If you can't patch immediately, two effective mitigations:
- Disable
mod_http2: the vulnerable code is then no longer executed.# Debian/Ubuntu sudo a2dismod http2 && sudo systemctl restart apache2 - Switch to the prefork MPM: the bug doesn't manifest in this single-threaded model (at the cost of lower performance).
Distributions have backported the fix — for example the USN-8239-1 advisory for Ubuntu. Check your package manager rather than relying on the upstream version number alone.
Detection: spotting an exploitation attempt
Watch for the following signals:
- Repeated crashes of Apache worker processes (the main DoS symptom).
- Log entries mentioning double-free or heap corruption.
- Abnormal HTTP/2 traffic: bursts of
HEADERSimmediately followed byRST_STREAM. - Core dumps related to memory management — to be analyzed.
For fine-grained kernel-level runtime detection (abnormal child processes, crashes), an eBPF-based tool like Falco is ideal.
The lesson: HTTP/2, a recurring attack surface
CVE-2026-23918 isn't an isolated case. The HTTP/2 implementation — with its multiplexing, concurrent streams, and complex state management — has become a recurring source of vulnerabilities across all servers (remember the "Rapid Reset" family and its relatives). Concurrency and manual memory management in C make for a cocktail ripe for use-after-free and double-free bugs.
Two reflexes to ingrain: treat every exposed server as a critical asset to patch as a priority, and reduce the surface — only enable HTTP/2 if you actually use it, segment, and place a defensive layer upstream. That's the logic of a Zero Trust architecture applied all the way down to the web layer. And if you're still torn between Apache and its competitor, our NGINX configuration guide details a robust alternative.
Comments