Sep 16, 2026·6 min read·2 visits
Unauthenticated remote peers can trigger heap memory exhaustion and Denial of Service in http4s Ember HTTP/2 implementations by sending frames with large declared size headers.
An uncontrolled resource consumption vulnerability in the http4s Ember HTTP/2 server and client implementation leads to unauthenticated heap memory exhaustion and denial of service. The vulnerability stems from deferring frame size validation until the entire declared payload size is buffered.
The vulnerability affects http4s, a minimal and idiomatic Scala interface for HTTP services. Specifically, the flaw exists within the HTTP/2 implementation of Ember, which is the built-in, native HTTP server and client backend for http4s. Ember exposes an attack surface to remote unauthenticated clients when configured to handle HTTP/2 traffic.
The vulnerability is classified under CWE-400 (Uncontrolled Resource Consumption) and manifests as an application-level Denial of Service. Because the read loop process fails to enforce limits early, it allows arbitrary remote hosts to dictate resource allocation boundaries before validating them. This behavior results in a high-severity availability issue that affects both servers and clients leveraging the vulnerable component.
HTTP/2 protocol specifications (RFC 9113) define communication through distinct binary frames. Each frame begins with a fixed 9-byte header followed by a variable-length payload. The first three bytes of this header encode a 24-bit integer representing the payload length. During connection negotiation, receiving endpoints advertise their maximum allowable frame size via the SETTINGS_MAX_FRAME_SIZE parameter. The default value is 16 KiB, and it may be negotiated up to 16 MiB.
In vulnerable versions of http4s Ember, the H2Connection.readLoop function reads incoming TCP streams and appends data to an internal accumulator. The read loop holds these bytes in memory until the accumulator length is at least equal to the declared length of the frame plus the 9-byte header. The code does not check the declared length against the configured SETTINGS_MAX_FRAME_SIZE until the reassembly of the frame is complete.
Consequently, a remote peer can declare a massive frame payload size of up to 16 MiB while providing minimal payload data. The server keeps the stream buffer open and continues allocating memory in an attempt to fulfill the expected payload length. If multiple malicious connections execute this sequence simultaneously, the accumulated buffers deplete the Java Virtual Machine heap, leading to severe memory pressure.
The vulnerability was addressed in commit 87cf334fa3f608ef7d3eb359e71e037ba3336d29 by shifting the frame size check into the earliest stage of the read loop.
Historically, the read loop evaluated the frame only after complete reassembly using the fromByteVector parser. This parsing method deferred validation until all bytes defined by the header were already buffered.
// Vulnerable Read Loop Behavior
// The loop accumulated socket data and only parsed it after
// receiving the entire declared frame payload
H2Frame.RawFrame.fromByteVector(acc) match {
case Some((rawFrame, remaining)) =>
// Post-reassembly check occurs here within specific handlers
// after the allocation has already occurred
}The security patch introduced a pre-buffering validation phase. It implemented a helper method named peekDeclaredLength within H2Frame.scala to extract the declared size directly from the first 3 bytes of the buffer without waiting for the payload.
// Patched Implementation in H2Frame.scala
object RawFrame {
/**
* The payload length a frame declares, decoded from the first three bytes.
* Allows an oversized frame to be rejected without reading that payload.
*/
def peekDeclaredLength(bv: ByteVector): Option[Int] =
if (bv.length >= 3)
Some((bv(2) & 0xff) | ((bv(1) & 0xff) << 8) | ((bv(0) & 0xff) << 16))
else None
}The H2Connection.readLoop was updated to perform a proactive check against this value immediately as bytes are received. If the declared size exceeds the local settings limit, the connection is instantly closed.
// Proactive Validation Check in H2Connection.scala
} else if (
H2Frame.RawFrame.peekDeclaredLength(acc).exists(_ > localSettings.maxFrameSize.frameSize)
) {
logger.warn(
"Received Frame Size Larger than Allowed Frame Size - Frame Size Error - Issuing GoAway"
) >> goAway(H2Error.FrameSizeError) >> F.pure(None)
} else
H2Frame.RawFrame.fromByteVector(acc) match {
// Process valid frame
}An attack targeting this vulnerability is carried out over an established HTTP/2 connection. No authentication credentials or special application-level privileges are required. The attacker only needs network-level access to the port on which the Ember-based service is listening.
The attack sequence is executed in the following steps:
During the attack, the peer transmits an initial frame header stating a size of 16 MiB. The attacker then either pauses transmission or trickles a minimal volume of bytes. The vulnerable server pauses processing of subsequent frames on that connection and retains the growing buffer in memory while waiting for the payload completion. By establishing multiple concurrent streams, the attacker triggers memory amplification up to 1024-fold per connection, leading to a denial of service.
The impact of successful exploitation is localized entirely to the availability of the application. There is no risk of unauthorized disclosure of confidential data, nor is there any opportunity for unauthorized modification of system data. This is reflected in the CVSS v3.1 vector string: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H.
The consequences on the JVM runtime include memory fragmentation, prolonged garbage collection pauses, and eventually an OutOfMemoryError. Once an OutOfMemoryError occurs, the JVM may become completely unresponsive or terminate abruptly, necessitating a manual restart or process-manager recovery.
At the time of writing, there are no known public weaponized exploit frameworks targeting this specific flaw. Additionally, the vulnerability is not listed in the CISA Known Exploited Vulnerabilities catalog.
The primary remediation strategy is upgrading the http4s dependencies to the patched releases. For projects using the 0.23.x release line, upgrade to version 0.23.37 or newer. For projects utilizing the 1.0.0 milestone line, upgrade to version 1.0.0-M48 or newer.
// Example sbt build definition update
libraryDependencies += "org.http4s" %% "http4s-ember-server" % "0.23.37"If upgrading is not immediately possible, you can mitigate the vulnerability by disabling HTTP/2 support on your Ember servers. This forces clients to fallback to HTTP/1.1, bypassing the vulnerable code path in H2Connection entirely.
// Ensuring HTTP/2 is not enabled on EmberServerBuilder
val server = EmberServerBuilder
.default[IO]
.withHost(host"0.0.0.0")
.withPort(port"8080")
.withHttpApp(httpApp)
// Do not call .withHttp2 or .withHttp2(true)
.buildTo detect exploitation attempts, check server log files for the warning message: Received Frame Size Larger than Allowed Frame Size - Frame Size Error - Issuing GoAway. This pattern indicates the updated security logic is rejecting connection streams.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
http4s-ember-server http4s | < 0.23.37 | 0.23.37 |
http4s-ember-client http4s | < 0.23.37 | 0.23.37 |
http4s-ember-server http4s | >= 1.0.0-M1 < 1.0.0-M48 | 1.0.0-M48 |
http4s-ember-client http4s | >= 1.0.0-M1 < 1.0.0-M48 | 1.0.0-M48 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-400 |
| Attack Vector | Network |
| CVSS Severity | 7.5 (High) |
| Exploit Status | None |
| Impact | Denial of Service |
The software does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed.
CVE-2026-61544 is a high-severity remote Denial of Service (DoS) vulnerability in libp2p-quic, the QUIC transport implementation of the official Rust networking stack for libp2p. It allows unauthenticated remote attackers to trigger an uncaught panic and crash listener applications.
CVE-2026-61554 is a high-severity uncontrolled resource consumption vulnerability in the http_poll transport component of the emp3r0r Command and Control (C2) framework. In affected versions prior to 4.2.5, the C2 server allocates session tracking resources, spawns execution routines, and routes incoming unauthenticated request bodies into the core dispatch engine before verifying the client's cryptographic authentication token. This logical ordering flaw allows unauthenticated remote attackers to exhaust critical host system resources and trigger a sustained denial of service.
A constellation of five distinct security flaws (F1 through F5) in `@zereight/mcp-gitlab` prior to version 2.1.30 allows unauthenticated remote access, read-only policy bypasses, DNS rebinding, and denial-of-service via session exhaustion.
A critical security vulnerability has been identified in the @zereight/mcp-gitlab implementation of the Model Context Protocol (MCP) server. Prior to version 2.1.30, the server lacks HTTP Host and Origin header validation on its Streamable HTTP transport interface (/mcp). This security omission permits remote attackers to bypass the Same-Origin Policy (SOP) via a DNS Rebinding attack. Under this vector, an attacker can route malicious API requests to the victim's local or internal MCP server, thereby gaining unauthorized control over the victim's GitLab account and resources.
A critical Server-Side Request Forgery (SSRF) vulnerability in @zereight/mcp-gitlab allows attackers to leak sensitive GitLab Private-Tokens by supplying an arbitrary external hostname via the X-GitLab-API-URL header when dynamic routing is enabled.
A critical memory leak vulnerability exists in the server-side DigestAuth middleware of the http4s library. Due to a logical inversion in the stale-nonce clean-up routine, the internal cache fails to evict stale nonces while prematurely purging fresh ones. Unauthenticated remote attackers can exploit this behavior by repeatedly prompting the server for authentication challenges, leading to unbounded memory consumption and application crashes via a java.lang.OutOfMemoryError.