Executive Overview
For years, web developers have relied on the fundamental assumption that JavaScript object serialization is inherently safe. Unlike Java’s ObjectInputStream or Python’s pickle—notorious vectors for remote code execution (RCE) via insecure deserialization—JavaScript’s native JSON.parse() operates as a benign data container. It constructs plain data objects without executing constructors, invoking "magic" lifecycle methods, or walking prototype chains.
However, modern frontend architectures have evolved beyond traditional JSON. React Server Components (RSC) bypass standard browser delivery methods, replacing HTML and JSON payloads with a custom, streaming, line-delimited wire protocol known as Flight.
Flight is not merely a data serialization format; it is a behavioral protocol. It reconstructs module imports, lazy-loaded components, asynchronous state machines, and server-side remote procedure call (RPC) endpoints directly on the client.
This architectural complexity culminated in CVE-2025-55182, colloquially dubbed "React2Shell." Carrying a maximum CVSS score of 10.0, this unauthenticated RCE vulnerability weaponized the Flight deserialization layer, allowing threat actors to achieve full shell access via a single, crafted HTTP request.
State-sponsored cyber-espionage groups quickly weaponized the flaw, deploying file-less cryptocurrency miners and sophisticated rootkits in the wild. While core maintainers have since patched the specific gadget chain, the underlying structural risk remains: parsing untrusted network streams into executable behavior introduces deep systemic exposure.
This investigation breaks down the mechanics of the Flight protocol, dissects the React2Shell vulnerability, analyzes related subsequent CVEs, and provides an authoritative, ranked set of application-level defenses.
Detailed Chronology: From Discovery to In-the-Wild Exploitation
The Discovery of CVE-2025-55182
In December 2025, security researchers analyzing the React ecosystem published advisories detailing a critical vulnerability within the server-side reply handling code of React Server Components. Designated as CVE-2025-55182, the flaw exposed a catastrophic design oversight in how the framework resolves nested property paths during deserialization.
The security community quickly categorized it as "React2Shell" due to the immediacy and severity of its impact. Requiring zero authentication and no prior user interaction, an attacker could dispatch a specially crafted HTTP request to a Server Function endpoint, manipulating the protocol’s reference parser to execute arbitrary operating system commands.
Immediate Weaponization and State-Sponsored Attacks
The window between disclosure and widespread exploitation was measured in hours, not weeks. The Cybersecurity and Infrastructure Security Agency (CISA) promptly added the vulnerability to its Known Exploited Vulnerabilities (KEV) catalog, signaling imminent operational risk to federal civilian executive branch agencies and the private sector alike.
According to threat intelligence reports published by Sysdig, advanced persistent threat (APT) actors—specifically North Korean state-sponsored cyber-espionage units—began weaponizing React2Shell almost immediately. Operations such as the deployment of EtherRAT utilized the vulnerability to drop file-less implants communicating through the Ethereum blockchain—a novel command-and-control (C2) evasion tactic dubbed "EtherHiding."
Simultaneously, Palo Alto Networks’ Unit 42 documented the emergence of KSwapDoor, a malicious backdoor discovered on compromised Linux servers. KSwapDoor masqueraded as [kswapd1] in process listings to blend in with legitimate kernel swap daemons. The implant utilized RC4 string encryption and AES-256-CFB communications over a P2P mesh network, highlighting the high-stakes operational reality of unauthenticated RCE vulnerabilities in enterprise web frameworks.
The Subsequent Wave of Vulnerabilities
React2Shell was not an isolated parsing glitch; it opened the floodgates for deeper security audits of the Flight protocol. Throughout late 2025 and early 2026, researchers and maintainers uncovered a cascade of related flaws:
- CVE-2025-55184 & CVE-2025-67779 (CVSS 7.5): Denial of Service (DoS) vectors involving infinite recursion of nested Promises within Server Function deserialization. These flaws could be exploited to hang the Node.js event loop, resulting in persistent application unavailability. The initial patch proved incomplete, necessitating a second round of remediation.
- CVE-2026-23864 (CVSS 7.5): A memory exhaustion (OOM) vulnerability driven by unbounded request body buffering and zipbomb-style decompression payloads sent to Server Action endpoints.
- CVE-2025-55183 (CVSS 5.3): An information disclosure bug where crafted requests forced Server Functions to reflect internal source code when stringifying arguments, exposing database queries and hardcoded secrets.
- CVE-2026-27978 (CVSS 5.3): A Cross-Site Request Forgery (CSRF) bypass in Next.js. The framework incorrectly treated
Origin: nullheaders (generated by sandboxed iframes) as missing rather than cross-origin, enabling session hijacking attacks.
Supporting Context & Metrics: Dissecting the Flight Protocol
To understand how React2Shell operated, one must first examine the architecture of the wire format it exploited.
Flight On the Wire
When an application utilizes React Server Components (such as via the Next.js App Router), responses bearing the Content-Type: text/x-component header are transmitted across the network. Rather than a single monolithic JSON document, Flight utilizes a streaming, line-delimited format where each line represents a self-contained "row" processed sequentially by the client-side runtime.
A typical Flight payload structure appears as follows:
1:I["./src/components/ClientComponent.js",["chunks/main.js"],"default"]
2:J["$","article",null,"children":"$1"]
0:D"name":"RootLayout","env":"Server"
- Row 1 (
Itag): An import directive instructing the client runtime to load a specific client component module from the bundler chunk map. - Row 2 (
Jtag): A serialized JSON tree constructing a virtual DOM node (<article>), where the reference"$1"points back to chunk 1. - Row 0 (
Dtag): Defines the server execution context and environment metadata.
The Prefix System and Behavioral Deserialization
The attack surface of Flight centers on its string prefix system. When the parser encounters string values beginning with a dollar sign ($), it intercepts them and routes them through type-specific resolution handlers in ReactFlightClient.js:
$(Model Reference): Points to another chunk ID in the active stream.$:(Property Access): Recursively traverses properties on a resolved chunk (e.g.,$1:user:name).$F(Server Reference): Instantiates a callable Server Action (an RPC endpoint).$L(Lazy Component): Defers component rendering boundaries.$@(Promise/Raw Chunk): Exposes the internal wrapper object and its mutable resolution state rather than its resolved value.
Crucially, Flight is not merely a data format; it is a behavioral engine. It dictates code loading, asynchronous execution paths, and RPC binding based strictly on text streams originating from the network.
Official Statements and Framework Patches
React maintainers responded to CVE-2025-55182 with a targeted patch implemented across React versions 19.0.1, 19.1.2, and 19.2.1.
The core vulnerability resided in getOutlinedModel within ReactFlightReplyServer.js. When parsing colon-separated reference paths like $1:__proto__:constructor, the internal property traversal loop executed without safety checks:

for (key = 1; key < reference.length; key++)
parentObject = parentObject[reference[key]];
By omitting property ownership verification, an attacker could traverse from a plain JSON object up through Object.prototype, access the Object constructor, and ultimately reach the Function constructor. In JavaScript, the Function constructor evaluates strings as executable code—equivalent to an unconstrained eval().
The Framework Fix
The official patch hardens property access by caching the native hasOwnProperty method at module load time and invoking it via .call() on every resolution step:
var hasOwnProperty = Object.prototype.hasOwnProperty;
// Inside the deserialization loop:
hasOwnProperty.call(value, i);
This simple modification blocks prototype-chain traversal, neutralizing the specific gadget chain exploited in React2Shell. However, security researchers have noted that the fix addresses the symptom rather than the architectural design. The $:, $@, and $@ prefix handlers remain intact, meaning future parser vulnerabilities could emerge if edge cases in stream reconstruction are discovered.
Ranked Practical Defenses
Because relying entirely on framework-level parsing patches leaves applications vulnerable to zero-day parser anomalies, engineering teams must implement defense-in-depth measures. The following defenses are ranked from most impactful to least impactful.
1. Strict Schema Validation on Server Actions (Highest Impact)
The single most effective application-level mitigation is validating all inputs at the exact entry point of every Server Action, prior to executing any business logic or logging. If an argument triggers accidental stringification (exploiting CVE-2025-55183), logging it before validation can leak source code.
Utilize robust validation libraries such as Zod or Valibot:
"use server"
import z from "zod"
const UpdateProfileSchema = z.object(
name: z.string().min(1).max(100),
email: z.string().email(),
role: z.enum(["user", "editor"]),
)
export async function updateProfile(data: unknown)
const parsed = UpdateProfileSchema.safeParse(data)
if (!parsed.success)
return error: "Invalid input structure"
// Proceed exclusively with parsed.data
Crucial Rule: Always validate raw argument payloads as a whole rather than destructuring individual fields beforehand. Destructuring unvalidated inputs prematurely executes property lookups that can be leveraged by malicious parsers.
2. Mandatory Adoption of the server-only Package
To prevent sensitive modules (database drivers, internal API keys, core business logic) from accidentally leaking across the network boundary, import the server-only package at the top of every sensitive file:
import "server-only"
import db from "@/lib/db"
export async function fetchInternalRecords()
return db.query("SELECT * FROM secure_records")
Caveat: The server-only package protects code, not return data. If a Server Component retrieves secure records and passes them down as unconstrained props to a Client Component, that data will traverse the Flight stream. Return shapes must be explicitly pruned.
3. Hardened CSRF Mitigations
Framework defaults for CSRF protection can be fragile, as demonstrated by the Origin: null bypass. Developers must enforce strict cookie configurations and explicit application-level tokens for state-changing operations:
- Ensure session cookies are explicitly configured with
sameSite: "strict"orsameSite: "lax", accompanied byhttpOnly: true. - Implement cryptographically random CSRF tokens for sensitive mutations (e.g., password changes or financial transactions).
- Never add
'null'toexperimental.serverActions.allowedOriginsin Next.js configuration files, as this reopens sandboxed iframe CSRF vectors.
4. Version Verification and Patch Management
Verify your dependency tree immediately to ensure protection against both RCE and DoS vectors:
# Example pnpm audit check
pnpm ls react react-dom react-server-dom-webpack
Ensure your application is running React 19.0.4+, 19.1.5+, or 19.2.4+ (or corresponding patched framework builds) to guarantee mitigation against CPU exhaustion and memory OOM vulnerabilities.
5. Leveraging the React Taint API (Defense-in-Depth)
React’s experimental Taint API (taintObjectReference and taintUniqueValue) acts as a helpful development guardrail to prevent sensitive data from reaching the serializer:
import experimental_taintObjectReference as taintObjectReference from "react"
import "server-only"
export async function getUser(id: string)
const user = await db.users.findUnique( where: id )
taintObjectReference("Do not pass raw user records to client components.", user)
return user
Limitation: Taint tracking is strictly reference-based. Common transformations—such as object spreading ( ...user ), property extraction, or JSON serialization round-trips—will strip the taint flag. Treat it as a localized safeguard rather than a definitive security boundary.
6. Web Application Firewall (WAF) Rule Tuning
Deploying WAF rules to inspect POST requests carrying the Next-Action header can mitigate automated exploitation attempts. Useful patterns to block include:
- Request bodies containing prototype pollution signatures (
__proto__orconstructor:constructor). - Excessively large Server Action payloads (
Content-Length > 1MB) to mitigate zipbomb vectors. - Responses matching abnormal error templates (e.g.,
E{"digest") indicating internal exception leakage.
Future Outlook
The introduction of React Server Components and the Flight protocol solved genuine architectural challenges, enabling high-performance streaming, code splitting, and progressive rendering. However, the discovery of React2Shell serves as a stark reminder of the security risks inherent in complex deserialization architectures.
As more frameworks adopt server-driven UI patterns, the industry is going to need stronger primitives than "the server is trusted": cryptographic validation of serialized payloads, signed component trees, and content integrity checks on the Flight stream itself.
Relying on reactive patching to catch every edge case in a behavioral protocol is a fragile strategy. Moving forward, engineering organizations adopting server-driven UI paradigms must combine rigorous input validation schemas, strict boundary isolation, and continuous dependency auditing to defend against the structural risks of modern web serialization.
