Skip to main content
Vulnerability Database/CVE-2026-69203

CVE-2026-69203: Http4s Ember Server DOS Vulnerability

CVE-2026-69203 is a denial of service vulnerability in Http4s Ember server that allows attackers to exhaust heap memory through unlimited HTTP/2 streams. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-69203 Overview

CVE-2026-69203 is a resource exhaustion vulnerability in http4s, a Scala interface for HTTP services. The flaw affects Ember servers with HTTP/2 enabled via withHttp2, which fail to enforce the SETTINGS_MAX_CONCURRENT_STREAMS limit for peer-created streams. A single unauthenticated connection can open an unbounded number of streams, each retaining per-stream state until the Java Virtual Machine (JVM) heap is exhausted. The same unchecked allocation is reachable in ember-client through server-initiated PUSH_PROMISE frames because enablePush is not enforced. The issue is tracked under [CWE-400: Uncontrolled Resource Consumption] and fixed in versions 0.23.35 and 1.0.0-M47.

Critical Impact

One unauthenticated remote client can drive a vulnerable Ember HTTP/2 server to heap exhaustion, causing denial of service for all users of the affected application.

Affected Products

  • http4s Ember server with HTTP/2 enabled through withHttp2 — versions prior to 0.23.35
  • http4s Ember client processing server-initiated PUSH_PROMISE frames — versions prior to 0.23.35
  • http4s milestone builds prior to 1.0.0-M47

Discovery Timeline

  • 2026-09-15 - CVE-2026-69203 published to the National Vulnerability Database (NVD)
  • 2026-09-16 - Last updated in NVD database

Technical Details for CVE-2026-69203

Vulnerability Analysis

HTTP/2 multiplexes multiple logical streams over a single TCP connection. The SETTINGS_MAX_CONCURRENT_STREAMS parameter, defined in RFC 7540, exists to cap how many concurrent streams a peer may open. In vulnerable http4s Ember builds, this setting is advertised but not enforced against peer-initiated streams.

Each new stream allocates per-stream state on the JVM heap, including flow-control counters, header tables, and stream metadata. When the enforcement check is missing, a client can open streams indefinitely on one connection. The server holds this state until the connection closes or the heap is exhausted, whichever comes first.

The symmetric flaw in ember-client is triggered by server-initiated PUSH_PROMISE frames. HTTP/2 defines SETTINGS_ENABLE_PUSH to allow endpoints to disable push. Ember-client does not enforce enablePush, so a malicious or compromised server can push unlimited promised streams into a connecting client and exhaust its heap in the same manner.

Root Cause

The root cause is missing enforcement of HTTP/2 concurrency limits in the stream allocation path within H2Server.scala and equivalent client code. The processCreatedStream function created new stream state without checking against the configured maximum. No semaphore or counter gated the allocation, allowing unbounded growth.

Attack Vector

Exploitation requires only network access to an Ember HTTP/2 endpoint and no authentication. An attacker completes a TLS and HTTP/2 handshake, then repeatedly sends HEADERS frames with new odd-numbered stream identifiers on the same connection. Each frame causes the server to allocate stream state that persists in memory. The attacker never needs to send END_STREAM, complete request bodies, or wait for responses.

scala
// Patch excerpt from H2Server.scala (source: http4s commit 4983de1)
     def processCreatedStream(
         h2: H2Connection[F],
         streamIx: Int,
+        maxStreams: Semaphore[F],
     ): F[Unit] = {
       def fulfillPushPromises(resp: Response[F]): F[Unit] = {
         def sender(req: Request[Pure]): F[(Request[Pure], H2Stream[F])] =

Source: http4s commit 4983de1f. The fix introduces a Semaphore[F] (maxStreams) that must be acquired before a peer-created stream is allocated, bounding concurrent stream state.

Detection Methods for CVE-2026-69203

Indicators of Compromise

  • Sustained growth in JVM heap usage on Ember HTTP/2 processes without a corresponding increase in completed requests or active client sessions.
  • Single client source addresses with abnormally long-lived HTTP/2 connections and a stream identifier count far exceeding advertised SETTINGS_MAX_CONCURRENT_STREAMS.
  • OutOfMemoryError entries in application logs originating from HTTP/2 stream handling classes.
  • PUSH_PROMISE frames arriving at ember-client instances that have enablePush set to false in configuration.

Detection Strategies

  • Instrument the http4s Ember pipeline with metrics for active stream count per connection and alert when values exceed the configured maximum.
  • Correlate JVM garbage collection pressure and heap trends with per-connection HTTP/2 frame counters at the reverse proxy or service mesh layer.
  • Inspect HTTP/2 traffic at the load balancer for connections that open large numbers of streams without completing requests.

Monitoring Recommendations

  • Export JVM heap, GC pause, and thread metrics from Ember services to a central telemetry backend and baseline normal ranges.
  • Enable HTTP/2 frame-level logging at reverse proxies fronting Ember services to capture per-stream identifiers and frame types.
  • Track version metadata for http4s and Ember modules across your build artifacts to identify hosts still running versions prior to 0.23.35 or 1.0.0-M47.

How to Mitigate CVE-2026-69203

Immediate Actions Required

  • Upgrade http4s to 0.23.35 on the stable branch or 1.0.0-M47 on the milestone branch as documented in the http4s security advisory GHSA-9vwc-pc8p-253q.
  • Audit all Scala services that call EmberServerBuilder with withHttp2 and prioritize patching internet-facing endpoints.
  • Restart Ember services after upgrading to release any lingering stream state accumulated before the patch.

Patch Information

The fix ships in the v0.23.35 release and the v1.0.0-M47 milestone release. The remediation introduces a bounded semaphore around peer-initiated stream creation in H2Server.scala and enforces enablePush on the client, closing both the server and client sides of the resource exhaustion path.

Workarounds

  • Disable HTTP/2 on Ember servers by removing withHttp2(true) from EmberServerBuilder and serving traffic over HTTP/1.1 until the upgrade is completed.
  • Front vulnerable Ember services with a reverse proxy such as NGINX or Envoy that enforces its own HTTP/2 stream limits and connection-level resource caps.
  • Restrict ember-client connections to trusted servers only, or route client traffic through a proxy that strips PUSH_PROMISE frames.
bash
# Update the http4s dependency in build.sbt to the patched version
libraryDependencies ++= Seq(
  "org.http4s" %% "http4s-ember-server" % "0.23.35",
  "org.http4s" %% "http4s-ember-client" % "0.23.35"
)

# Verify resolved versions after refresh
sbt "show http4s-ember-server/version" "show http4s-ember-client/version"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.