Jan 29, 2026·6 min read·324 visits
React's 'Flight' protocol failed to validate deserialization limits. Attackers can send malicious streams to Next.js/React servers that allocate massive arrays, process infinite BigInts, or traverse `__proto__`. This kills the server instantly. Patched in v19.0.4+.
Multiple Denial of Service (DoS) and Prototype Pollution vulnerabilities exist in the React Server Components (RSC) 'Flight' protocol implementation. These flaws allow attackers to trigger Out-of-Memory (OOM) crashes, excessive CPU consumption, or potentially pollute the object prototype chain via specially crafted RSC stream payloads.
React Server Components (RSC) are the current darling of the frontend world. They promise the best of both worlds: server-side logic with client-side interactivity, seamlessly blended. Frameworks like Next.js have bet the farm on this architecture via the App Router. But magic requires a medium, and for RSC, that medium is the 'Flight' protocol—a custom, streaming serialization format used to sync state between the server and the client.
Whenever a developer says, "We don't need JSON, let's write our own serialization protocol to handle complex object graphs and promises," a security researcher gets their wings. History has taught us that custom serialization is a graveyard of security assumptions. CVE-2026-23864 is the latest tombstone. It isn't just a single bug; it's a structural failure to respect the boundaries of physics (memory) and logic (prototypes) within the react-server-dom packages.
This vulnerability is particularly nasty because it targets the infrastructure itself. It doesn't steal user data directly, but it can knock your entire fleet of Next.js containers offline with a few kilobytes of data. It's the digital equivalent of clogging a toilet with a single paper towel.
The root cause here is the classic "Trust Me Bro" anti-pattern. The RSC server endpoints (typically exposed as Server Actions) accept a stream of data to reconstruct objects, arrays, and function references. The server implementation assumed that the incoming stream would play nice. Spoiler: it didn't.
1. The Memory Black Hole (OOM):
The server parser allowed the client to specify the size of an array before providing the elements. If the client sent a payload saying, "I'm about to give you an array with 10 billion items," the server dutifully attempted to allocate that memory space immediately. This leads to an instant JavaScript heap out of memory crash.
2. The CPU Burn (BigInt):
JavaScript supports BigInt for arbitrary-precision integers. The key word is arbitrary. The vulnerable parser didn't place a limit on the number of digits in a BigInt literal. An attacker could send a string representing a number with millions of digits. The single-threaded Node.js event loop would then lock up trying to parse this mathematical monstrosity, causing a denial of service.
3. The Prototype Pollution:
The fulfillReference logic allowed the client to specify paths to resolve within an object. It did not block the __proto__ key. This meant an attacker could traverse up the prototype chain and potentially pollute the base Object.prototype, which is the holy grail for logic bypasses in JavaScript.
Let's look at the smoking gun in the react-server-dom-webpack (and related) packages. The fix involved adding sanity checks that should have been there from day one.
The Prototype Fix:
In the original code, the resolution logic blindly followed the path provided by the client stream. The patch introduces a hard check for __proto__ and ensures we are only assigning to "own" properties.
// BEFORE: Blind traversal
// value = value[path[i]];
// AFTER: Trust, but verify
const __PROTO__ = '__proto__';
if (key === __PROTO__) {
// Nice try, hacker.
return undefined;
}
if (hasOwnProperty.call(value, key)) {
value = value[key];
} else {
throw new Error('Invalid reference.');
}The Resource Limits: Meta also introduced explicit constants to cap the madness. They realized that legitimate web applications rarely need to process numbers larger than the atoms in the universe.
const DEFAULT_MAX_ARRAY_NESTING = 1000000;
const MAX_BIGINT_DIGITS = 300;
const MAX_BOUND_ARGS = 1000;If your application logic relies on a BigInt with more than 300 digits, you aren't building a website; you're simulating dark matter physics, and you should probably do that in C++.
Exploiting this requires understanding the RSC wire format, but you don't need to be a wizard. You just need to send a malformed POST request to the server endpoint handling RSC actions.
Scenario 1: The OOM Crash
We construct a payload that defines a massive array. The format typically looks like a line-delimited list of JSON-like structures. We define a reference $A that claims to be a sparse array of doom.
POST /my-server-action HTTP/1.1
Content-Type: text/x-component
1:["$A", 9999999999]When the V8 engine underneath Node.js tries to allocate a sparse array of this magnitude, it panics and crashes the process. In a containerized environment (like Kubernetes), the pod dies and restarts. If the attacker keeps sending these requests, the service enters a CrashLoopBackOff state.
Scenario 2: The CPU Freeze
We target the BigInt parser. The format uses $n to denote a BigInt.
POST /my-server-action HTTP/1.1
0:"$n9999999999...[repeat 1 million times]"The server receives this, sees the $n marker, and hands the massive string to the BigInt() constructor. The CPU spikes to 100%, requests pile up, and the load balancer eventually marks the instance as unhealthy.
The danger of CVE-2026-23864 lies in its asymmetry. It costs an attacker effectively zero resources to generate these payloads—a simple Python script with a loop will suffice. However, the impact on the target is catastrophic resource exhaustion.
While Denial of Service is often dismissed as "just downtime," in the context of serverless functions (like Vercel or AWS Lambda) or auto-scaling clusters, this translates directly to financial damage. You are paying for the CPU cycles the attacker is forcing you to burn. Furthermore, the Prototype Pollution aspect (__proto__) is a sleeper agent. Depending on the other libraries present in your Node.js environment, polluting the prototype could lead to bypasses in authentication middleware, NoSQL injection, or other Remote Code Execution (RCE) gadgets.
This isn't just about crashing; it's about the instability of the foundation your shiny new React app sits on.
The mitigation is straightforward: Update your dependencies. This isn't a configuration tweak; the code itself was broken. You need to pull the patched versions of react-server-dom-webpack, react-server-dom-turbopack, or react-server-dom-parcel.
Fixed Versions:
19.0.419.1.519.2.4If you are using Next.js, updating to the latest patch release should pull these in transitively. Verify your package-lock.json or yarn.lock to ensure the deeply nested react-server-dom-* packages are actually updated. Don't trust the top-level version number; check the lockfile.
If you cannot update immediately (why?), you could theoretically deploy a WAF rule to inspect the body of POST requests to Server Action endpoints, specifically looking for the string __proto__ or regex-matching effectively long numeric sequences after $n. But that is a band-aid on a bullet wound. Just update.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
react-server-dom-webpack Meta | 19.0.0 < 19.0.4 | 19.0.4 |
react-server-dom-webpack Meta | 19.1.0 < 19.1.5 | 19.1.5 |
react-server-dom-webpack Meta | 19.2.0 < 19.2.4 | 19.2.4 |
| Attribute | Detail |
|---|---|
| CWE IDs | CWE-502 (Deserialization), CWE-400 (Resource Exhaustion) |
| Attack Vector | Network (HTTP POST) |
| CVSS v3.1 | 7.5 (High) |
| EPSS Score | 0.00603 (68.98%) |
| Exploit Status | PoC (Private) |
| Protocol | React 'Flight' RPC |
Deserialization of Untrusted Data causing Resource Consumption
CVE-2026-107725 is a critical security bypass in Hazelcast where missing authorization checks in the MapPermission class permit unprivileged clients to issue queries containing aggregators or projections. This architectural oversight allows attackers to run arbitrary code on the cluster servers under the privileges of the active Hazelcast process.
A stored Cross-Site Scripting (XSS) vulnerability was identified in Indico, an open-source event management system developed at CERN, prior to version 3.3.13. The vulnerability stems from weak URL validation in custom link fields and lack of HTML sanitization during Marshmallow serialization of event notes. This allows authenticated attackers with event modification privileges to inject malicious payloads that execute in the browser of users viewing the event pages or collaborating on notes.
A technical analysis of CVE-2026-107397, a stored Cross-Site Scripting (XSS) vulnerability in Indico's collaborative notes editor and custom link generation fields. Prior to version 3.3.13, Marshmallow serialization schemas omitted HTML sanitization during conflict resolution, and form validators failed to enforce strict URI schemes, enabling authenticated low-privilege attackers to execute arbitrary JavaScript.
An authorization bypass vulnerability exists in the legacy session export API of Indico, an open-source event management system developed at CERN. Due to a missing object-level access check, authenticated users can bypass configuration-level restrictions to extract private session metadata (including session titles, descriptions, and list of conveners) from events that they are otherwise authorized to view.
An incomplete Server-Side Request Forgery (SSRF) validation check in Indico prior to version 3.3.13 allows authenticated event organizers to bypass outbound network restrictions. By utilizing backslash characters within crafted URLs, attackers can exploit a parser differential between the application's validator and the downstream HTTP client library to access internal network resources.
CVE-2026-107717 represents a critical prompt boundary bypass and chat role injection vulnerability in the Banks Python package (versions prior to 2.5.0). The library parses generated template outputs line-by-line, attempting to validate each segment as a JSON-serialized ChatMessage object without validating the source boundaries of the text. If an application integrates user input directly into a prompt template, a remote, unauthenticated attacker can supply multi-line inputs with structured JSON payloads. This input is then parsed as high-privilege system instructions or tool execution responses, completely hijacking downstream Large Language Model behavior.