Back to blog

CVE-2026-23918: Apache HTTP/2 Double-Free Flaw That Can Bring Down Your Server (and Worse)

· · 15 min read
CVE-2026-23918: Apache HTTP/2 Double-Free Flaw That Can Bring Down Your Server (and Worse)

On May 4, 2026, the Apache Software Foundation shipped version 2.4.67 of the Apache HTTP Server, patching eleven vulnerabilities in a single release. The most consequential of them is CVE-2026-23918, a double-free memory corruption bug in the mod_http2 module carrying a CVSS score of 8.8 (High). An unauthenticated attacker can crash any Apache worker process with a single TCP connection and two HTTP/2 frames. On Debian-derived systems and in the official Apache Docker image, the same primitive has a viable path to full remote code execution. The fix was ready one day after the bug was reported. The patch took five months to reach users.

This article follows the same two-part format we use for all our CVE breakdowns: a plain-language executive summary first, then a full technical dissection for readers who want to understand the bug from first principles. At the end, we discuss what this class of vulnerability means in practice for teams running Apache in cloud and containerized environments, and how the Pragma Core platform addresses the underlying detection and prevention challenges.


Part I. Executive breakdown

What happened

Researchers Bartlomiej Dmitruk of striga.ai and Stanislaw Strzalkowski of isec.pl reported a logic flaw in Apache's HTTP/2 implementation to the Apache security team on December 10, 2025. A source-level fix landed the following day. The patched release, 2.4.67, was published on May 4, 2026, five months later.

The flaw lives in h2_mplx.c, the multiplexer component responsible for managing concurrent HTTP/2 streams over a single connection. When a client sends a stream-opening HEADERS frame and immediately follows it with a RST_STREAM reset frame before the server has finished registering the stream, two separate cleanup callbacks both push the same stream object into the cleanup queue. When Apache later destroys those queue entries, it frees the same memory region twice. The result is heap corruption that, at minimum, crashes the worker process and, under specific but common allocator configurations, can be shaped into arbitrary code execution.

The vulnerability is specific to Apache HTTP Server 2.4.66. A memory allocator change shipped in that release is what turns a pre-existing race condition into an exploitable double-free. Older versions are not affected in the same way.

Who is affected

Component / Configuration Status
Apache HTTP Server 2.4.66 with mod_http2 loaded Vulnerable. Upgrade to 2.4.67 immediately.
Apache HTTP Server 2.4.66 with mpm_prefork Vulnerable to DoS only. RCE path is not present.
Apache HTTP Server 2.4.66 on Debian / Ubuntu (APR mmap allocator) Vulnerable. RCE path is viable.
Official Apache httpd Docker image (pre-2.4.67) Vulnerable. RCE path is viable. Pull updated image.
Apache HTTP Server 2.4.65 and earlier Not affected by this specific double-free.
Apache HTTP Server 2.4.67 Patched.

Any internet-facing Apache 2.4.66 instance with HTTP/2 enabled is at immediate risk of denial of service. Deployments using threaded MPMs (mpm_worker or mpm_event) with the APR mmap allocator face a viable remote code execution path as well.

Why this matters more in containerized environments

The official Apache httpd Docker image is one of the most pulled images on Docker Hub. It ships with both mod_http2 enabled and the APR mmap allocator configuration that makes the RCE path viable. Every team using this image as a base layer in their pipeline is affected without any misconfiguration on their part. The default is the attack surface.

In Kubernetes and similar orchestration platforms, this has a compounding effect. If the base image reference is not updated, every new pod that spins up, on every autoscaling event, on every deployment restart, pulls the vulnerable image. Ephemeral container lifecycles mean the vulnerability regenerates continuously until the base image is pinned to a patched version.

The denial-of-service path is also more disruptive in orchestrated environments than on a single server. A sufficiently fast sequence of reset frames can crash workers faster than the orchestrator restarts them, producing a sustained outage rather than a momentary blip.

Recommended actions

  1. Upgrade to Apache HTTP Server 2.4.67. This is the only complete remediation. The patch eliminates the vulnerable code path rather than constraining it. No configuration workaround provides equivalent protection.

  2. If an immediate upgrade is not possible, disable HTTP/2. Set Protocols http/1.1 in your Apache configuration. This blocks the vulnerable code path entirely at the cost of HTTP/2 performance benefits. It is a sound interim control for environments where change windows prevent immediate patching.

  3. Update your container base images. Pull the latest official httpd Docker image and rebuild any images that use it as a base layer. Update the image reference in your CI/CD pipelines and trigger redeployment across any affected Kubernetes workloads.

  4. Inventory your Apache deployments and confirm exposure. Check whether mod_http2 is loaded (httpd -M | grep http2) and confirm the MPM in use (httpd -V | grep MPM). Prioritize internet-facing reverse proxies, API gateways, and authentication portals.

  5. Monitor for anomalous HTTP/2 reset patterns until you patch. Unusual volumes of RST_STREAM frames or unexpected worker process crashes are the most actionable behavioral signals for this CVE. There are no vendor-published indicators of compromise specific to CVE-2026-23918.


Part II. Technical breakdown

Background: HTTP/2 streams and the multiplexer

HTTP/2 is a binary framing protocol that multiplexes multiple logical streams over a single TCP connection. Each stream is identified by an integer stream ID and has a well-defined lifecycle: it opens when the client sends a HEADERS frame, carries DATA frames, and closes either through normal termination or a RST_STREAM reset. Streams are managed in Apache by a component called the multiplexer, implemented in h2_mplx.c, which tracks active streams and coordinates their cleanup when they terminate.

The multiplexer maintains an internal cleanup queue. When a stream finishes, a callback pushes its object pointer into that queue. A separate cleanup pass later iterates the queue, frees each stream, and releases associated memory. The invariant this system depends on is simple: each stream object appears in the cleanup queue exactly once.

The early reset condition and where the invariant breaks

HTTP/2 clients are permitted to send a RST_STREAM frame at any point after opening a stream, including immediately after the HEADERS frame that opened it. This is legitimate protocol behavior used when a client decides it no longer needs the response it just requested.

The vulnerability is triggered by a specific timing window. When a client sends HEADERS and then immediately sends RST_STREAM with a non-zero error code before the multiplexer has finished registering the stream, two things happen in sequence:

  1. The early reset handler fires and pushes the stream object into the cleanup queue, treating the stream as terminated.
  2. The normal stream registration path also reaches its own cleanup callback, which pushes the same object into the cleanup queue a second time.

Neither callback is individually incorrect. The problem is that neither one checks whether the object is already in the queue. With the mod_http2 v2.0.33 memory allocator change that shipped in Apache 2.4.66, the same stream object now ends up in the cleanup array twice.

When the cleanup pass runs, it frees the stream on the first encounter. On the second encounter, it attempts to free memory that has already been released. This is a CWE-415 double-free.

Double-free mechanics: from crash to code execution

Freeing the same heap allocation twice corrupts the allocator's internal bookkeeping structures. Modern heap allocators maintain metadata about each allocation, typically stored in headers adjacent to or within the allocated region. A double-free overwrites that metadata in a way that causes subsequent allocations or frees to behave incorrectly.

In the simplest case, the corrupted metadata causes the allocator to crash the next time it attempts to use the corrupted region. This is the denial-of-service outcome: a worker process dies.

In more controlled conditions, an attacker can influence which allocations happen between the first and second free in order to shape the heap state before the corruption occurs. If a new allocation fills the freed region before the second free fires, the double-free corrupts live allocation metadata for that new object. Depending on what that object is and how the allocator handles the corrupted metadata, it may be possible to redirect subsequent writes to attacker-controlled addresses, achieving code execution.

The Apache Portable Runtime (APR) is the abstraction layer Apache uses for memory management. On Debian-derived systems and in the official httpd Docker image, APR uses an mmap-based allocator whose memory layout is consistent enough that heap shaping is realistic. On other systems and with mpm_prefork, where workers do not share heap state across threads, the practical path to RCE is significantly harder. The denial-of-service path is straightforward and reliable on all affected configurations.

The exploit primitive

The minimal trigger for CVE-2026-23918 is two HTTP/2 frames over a single TCP connection, requiring no authentication, no specific URL, and no prior knowledge of the target:

[Attacker] --> TCP connect to port 80/443
[Attacker] --> HTTP/2 connection preface + SETTINGS frame
[Attacker] --> HEADERS frame  (stream ID 1, any valid headers)
[Attacker] --> RST_STREAM frame  (stream ID 1, error_code != 0)

[Server]   --> Worker process heap corrupted
             -> Crash (DoS, all configurations)
             -> Potential RCE (mmap allocator + threaded MPM)

The two-frame sequence can be repeated across multiple streams or connections to maintain a sustained denial-of-service condition. The researchers confirmed that a single TCP connection is sufficient to crash a worker on default mpm_event and mpm_worker deployments.

The mod_http2 v2.0.33 allocator change

The double-free is specific to Apache 2.4.66 because of a memory allocator change introduced in mod_http2 v2.0.33, which shipped with that release. Before this change, the behavior on an early reset produced a different execution path that did not result in the same double-push to the cleanup array.

This is an important scoping detail for vulnerability management. If you are not running 2.4.66, you are not affected by this specific double-free. The race condition may exist in earlier versions, but without the v2.0.33 change the cleanup path does not behave in the way that produces exploitable heap corruption. Prioritize locating 2.4.66 specifically, rather than treating the entire 2.4.x branch as uniformly exposed.

Detection

The most direct detection signal for active exploitation attempts is network-level: a high volume of HTTP/2 streams that open and immediately reset, particularly from a single source IP or across a short time window. Most legitimate HTTP/2 clients do not send RST_STREAM frames immediately after HEADERS. That behavior is a strong indicator of either an exploitation attempt or a client bug.

On the host side, the most reliable signal is unexpected Apache worker process crashes, particularly in environments where workers are not normally crashing. On mpm_event and mpm_worker deployments, worker crashes without a corresponding application-level error in the Apache error log warrant immediate investigation.

Web application firewalls and load balancers that terminate HTTP/2 connections before passing traffic to Apache will absorb this attack vector and protect the backend. If your Apache instances are behind a WAF or reverse proxy that handles HTTP/2 termination, your exposure to CVE-2026-23918 is reduced.

Timeline

Date Event
April 2025 (approx.) Apache HTTP Server 2.4.66 released, shipping mod_http2 v2.0.33 with the allocator change that makes the double-free exploitable.
December 10, 2025 Bartlomiej Dmitruk and Stanislaw Strzalkowski report CVE-2026-23918 to the Apache security team.
December 11, 2025 Source-level fix committed.
May 4, 2026 Apache HTTP Server 2.4.67 released. CVE-2026-23918 patched.
May 5-6, 2026 SUSE, Red Hat, and Debian downstream advisories published.
May 8, 2026 No confirmed active exploitation reported. No public proof-of-concept code.

What we should take away from CVE-2026-23918

A few patterns from this vulnerability are worth holding onto, because they recur across the class of bugs that survive longest in widely deployed, well-maintained software:

A single version-specific change can create a vulnerability where none existed. The underlying race condition around early stream resets existed before 2.4.66. It only became an exploitable double-free because of a specific allocator change in that release. Vulnerability scanners that flag an entire version family as affected, rather than the specific release with the triggering change, both over-report and may cause teams to under-prioritize the actual affected version.

Default configurations are the attack surface. mod_http2 ships enabled by default. HTTP/2 is activated for performance reasons in the vast majority of production deployments. The APR mmap allocator is the default on Debian and in the official Docker image. No misconfiguration is required to land on the RCE-viable path. The question defenders need to ask is not "did we do something wrong?" but "are we running this version?"

Container images distribute vulnerabilities at scale, and ephemeral lifecycles perpetuate them. A patched binary and an unpatched base image can coexist in the same pipeline indefinitely. Teams that update server packages but do not rebuild and redeploy container images have not actually remediated the vulnerability. This process gap shows up repeatedly in post-incident reviews and is worth addressing structurally, not just reactively.

A five-month gap between patch and release is a structural exposure window. The fix was committed the day after the report. The patch reached users five months later. The disclosure policy is not the issue here. The gap between "fixed in source" and "available to users" represents months of one-sided exposure. Shortening that window for any software you maintain or depend on is a meaningful security improvement independent of any specific vulnerability.


How Pragma Core addresses this class of problem

CVE-2026-23918 illustrates two distinct problems that show up in different parts of the security program: the challenge of tracking specific vulnerable versions across a heterogeneous deployment estate, and the challenge of identifying HTTP/2 attack surface that is reachable from the internet without a complete asset inventory.

Continuous SCA and version tracking across your entire fleet

The scoping of CVE-2026-23918 is precise: Apache 2.4.66, mod_http2 loaded, threaded MPM. Knowing whether you are affected means knowing exactly what version of Apache is running in every environment, including containers, VMs, bare metal, and CI/CD build images. The Pragma Core SCA and dependency tracker maintains a continuous inventory of software versions across all connected repositories and deployment manifests, surfaces CVE matches the moment they are published, and identifies which specific workloads are affected. For a CVE this version-specific, the difference between "all Apache" and "exactly 2.4.66" is the difference between a false alarm and an actionable finding.

Dynamic testing of live HTTP/2 surfaces

The vulnerability requires HTTP/2 to be enabled and reachable. Coverage for HTTP/2 protocol behaviors is not standard in most scanner configurations. The Pragma Core DAST module tests live application surfaces, including HTTP/2-capable endpoints, and confirms whether specific protocol behaviors are reachable from the attacker's perspective. For teams that want to verify whether the early reset trigger is reachable on their specific deployment before committing to an emergency change window, dynamic confirmation is more reliable than a version check alone.

Automated base image tracking for containerized workloads

For teams building on the official Apache Docker image, the question is not just whether a CVE exists but whether the base image currently referenced in your pipeline is affected. Pragma Core tracks base image versions and flags CVE exposure in container build definitions, surfacing the finding at the CI/CD layer where it can be blocked before deployment rather than discovered after the fact.

Infrastructure penetration testing for web tier exposure

Internet-facing Apache deployments, API gateways, and authentication portals are exactly the targets where a DoS or RCE at the web server layer has immediate operational and business impact. The Pragma Core infrastructure penetration testing module runs agentic tests against live environments with coverage for web server vulnerabilities, confirming exploitability in your specific configuration rather than relying on a generic CVSS score.


Closing thoughts

CVE-2026-23918 is the kind of vulnerability that gets underestimated at first glance. "A DoS in the HTTP/2 handler" sounds operational rather than critical. The detail that changes the calculus is the word "default": default module, default allocator, default container image. There is no misconfiguration to explain away. If you shipped Apache 2.4.66, you shipped this.

The patch is out. If you are running 2.4.67, you are done. If you are not, the interim control is a single configuration line. Neither step requires significant effort. What requires effort is knowing whether you are affected across every environment where Apache runs, including the ones built from a container image months ago and running since then.

If you want to talk about what continuous coverage for your web infrastructure looks like, you can reach us at pragma-core.com.


Sources

Related posts
CVE-2026-74820: how an unsanitized ORDER BY clause turned ServiceNow's AI Platform into an unauthenticated database backdoor
Sep 18, 2026
CVE-2026-85978: how one path normalization mismatch turned Akana's admin console into unauthenticated remote code execution
Sep 9, 2026
CVE-2026-78174: how an unredacted session token in a diagnostic log turned a low-privileged WatchGuard Dimension admin into super admin
Sep 1, 2026

Start securing your codebase today

Connect your repositories and let AI agents handle continuous scanning, research, and triage.

Have questions? Get in touch →