React2DoS (CVE-2026-23869): When the Flight Protocol Crashes at Takeoff
Executive Summary
In this article, we disclose a new high severity unauthenticated remote denial‑of‑service vulnerability we identified and reported in React Server Components that we’ve dubbed “React2DoS”.
Chrome, Vivaldi, and the challenge of changing browsers
Executive Summary
In this article, we disclose a new high severity unauthenticated remote denial‑of‑service vulnerability we identified and reported in React Server Components that we’ve dubbed “React2DoS”. In this blog, we’ll analyze its impact and place it in the broader context of recently found Flight protocol vulnerabilities, especially CVE‑2026‑23864.
Introduction
We are in a phase of the web where performance and developer experience are no longer trade-offs, they’re expectations. Modern frameworks compete to ship less JavaScript, reduce client-side complexity, and move logic back to the server.
React, as one of the dominant forces in frontend development, has been at the forefront of this evolution. With the introduction of React Server Components (RSC), the ecosystem embraced a new model: components that execute exclusively on the server, access databases and secrets directly, and stream a serialized UI representation to the client.
This architecture promises smaller bundles, cleaner separation of concerns, and more efficient rendering. Instead of hydrating everything on the client, Server Components emit a structured stream that the browser reconstructs locally.
At the heart of this mechanism lies a custom streaming protocol known as Flight. Through Flight, React can serialize complex structures, like arrays, maps, object references, even promises and async boundaries, allowing the server to describe rich UI trees in a compact format.
This is powerful.
But history has shown that when we introduce custom serialization formats and complex parsers, we also introduce risk. The server must deserialize and reconstruct object graphs from client-controlled input. And complex parsing logic has long been fertile ground for vulnerabilities.
In our research we discovered a denial-of-service vulnerability that allows an attacker to impose disproportionate computation to the remote server.
React2Shell and subsequent DoS vulnerabilities
Earlier this year, the disclosure of React2Shell caught much of the community off guard, triggering emergency patches and intense scrutiny of the React Server Components architecture, amplified by waves of low-quality AI-generated analysis that blurred the line between verified facts and speculation. This episode also prompted deeper investigations into and led to new discoveries related to the security of the Flight protocol and related parsing mechanisms.
CVE‑2026‑23864 (CVSS 3.1 of 7.5), stood out as a notable example and serves as a useful reference for understanding the mechanics behind the issue we explore in this research.
Among other vectors, this vulnerability concerned the BigInt deserialization path in Flight:
$n markers denote BigInt values
No limit was enforced on digit length
Therefore, sending a million‑digit BigInt could cause a significant computation cost, and CPU exhaustion. An example payload could look like this:
0:”$n9999999999…[repeated 1 million times]”
In our setup, a single query like this could delay the server’s execution by several seconds if the inbound payload reaches the maximum allowed size (1MB with Node.js runtime, 10MB with Edge runtime).
This was the starting point of our research, and we tried to find payload that would trigger a similar, or superior cost to the server. This is exactly what we found, actually more computationally-intensive by several orders of magnitude.
React2DoS
React relies on a mechanism known as the React Flight Protocol to serialize values that are sent to Server Functions.
On the client side, data is transmitted to the server as small pieces (or “chunks”), for example through form submissions:
payload = {
“0”: (None, ‘[“$1”]’),
“1”: (None, ‘{“category”:”vehicle”,”model”:”$2:modelName”}’),
“2”: (None, ‘{“modelName”:”tesla”}’),
}
As illustrated above, these chunks can reference one another.
After deserialization on the server, the reconstructed object looks like this:
{ “category”: “vehicle”, “model”: “tesla” }
At first, we tried to measure the cost of execution of every type of reference supported by the Flight protocol. Among them, we looked at two promising ones: $Q and $W, respectively instantiating new Maps and Sets from the client request payload.
The first observation we made was that it was possible to reference the root element in the root element itself (!), which paved the way to recursive expressions:
“0” : [“$Q0”]
This, would cause the execution of the following JavaScript expression:
New Map([null])
Which makes perfect sense, because at the time of resolution of $Q0, $0 is not known yet.
However, what surprised us, was the fact that the following expression:
“0” : [“$Q0”, “$Q0” …, “$Q0”] (x n)
did trigger the execution of the Map constructor n times!
Indeed, the ReactFlightReplyServer uses a `consumed` attribute to prevent multiple computations of the same reference and prevent abuse. But this mechanism only enters in action when the reference is successfully resolved (see Fig 1).
Fig. 1: Exception doesn’t prevent recomputation of the same faulty Map
Because the `new Map` expression failed (new Map([null]) is not a valid JavaScript expression), this outcome was not stored anywhere. But surprisingly, the deserialization is not interrupted by this exception!
The execution of the expression `new Map ([null])` is pretty cheap, it takes our server around 0.03ms. Virtually instant. But this is neglecting the fact that a threat actor can insert more than 100,000 instances in a 1MB payload, leading to the cost of several seconds, comparable to the CPU exhaustion issue behind CVE‑2026‑23864 and described above.
Considering this, we submitted a first report to Meta, sharing this POC and demonstrating the impact.
But soon after, we realized there was a way more impactful payload we could generate by exploiting our original idea.
Instead of sending a series of “$Q0” that would immediately trigger the exception, we decided to introduce a series of valid map entries at the start of the root entry, to force the Map constructor to iterate over them before triggering the expected exception (see Fig. 2).
Fig. 2: Internal recursive resolution of “$0”
By doing so, we achieved a quadratic complexity, and a much more expensive payload ! The optimal number setting is n/2 valid maps and n/2 map references to the 0 object (“$Q0”).
CVE‑2026‑23864 (CPU exhaustion) vs React2DoS (CVE-2026-23869)
With our new attack vector, the computation could easily last several minutes. Therefore, with only small payloads of tens of kilobytes, it was possible to initiate impactful DoS attacks.
To give ourselves an idea of the impact of this attack vector, we computed a chart showing the comparison between CVE‑2026‑23864 (CPU exhaustion) and React2DoS. The result showed that after only a few kilobytes, React2DoS starts to stand out, and when the payload size reaches hundreds of kilobytes, it is already more powerful by several orders of magnitude (see Fig. 3).
Fig. 3: Comparison React2DoS – CVE‑2026‑23864
Therefore, with a single request, a threat actor can trigger a computation that will take minutes to handle. By repeating this, complete denial of service can be achieved.
Mitigation
The React team fixed this issue via verifying if the consumed flag was set before any map/set constructor was called.
The issue affects React Server Components version 19.2.4 and below. We recommend that you update to the latest available version that patches this vulnerability as soon as possible.
If your application already sits behind an Imperva proxy, it is automatically protected against this attack.
Conclusion
This case highlights an important reality: the path to innovation inevitably introduces complexity, and therefore risk. As ecosystems evolve rapidly, staying up to date and remaining aware of newly discovered security issues is essential.
In a more personal way, it was a pleasure for me to delve into one of the most used framework in the world and discover a finding with meaningful impact. This wouldn’t have been possible if researchers before didn’t pave the way with their investigations and their recent findings (React2Shell, CVE‑2026‑23864…).
Disclosure Timeline
Feb 3 2026 – Report including first payload
Feb 5 2026 – Second payload reported
April 8 2029 – Vulnerability fixed in 19.2.5
The post React2DoS (CVE-2026-23869): When the Flight Protocol Crashes at Takeoff appeared first on Blog.
*** This is a Security Bloggers Network syndicated blog from Blog authored by Yohann Sillam. Read the original post at: https://www.imperva.com/blog/react2dos-cve-2026-23869-when-the-flight-protocol-crashes-at-takeoff/
