React Flight Protocol Vulnerabilities: How Deserialization Sinks Enable Remote Code Execution

The React Flight protocol powering Server Components contains deserialization sinks that enabled a CVSS 10.0 RCE. Here's how the attack worked and how to defend against it.

MiHiR SEN
MiHiR SEN
·3 min read
The React Flight protocol's deserialization sinks enabled a CVSS 10.0 RCE (React2Shell) through prototype chain traversal in property resolution. The article explains the attack chain, related CVEs, and ranks practical defenses from schema validation to CSRF hardening.

React Server Components do not send HTML or JSON to the browser. They send Flight, a custom streaming protocol with its own type system, reference resolution, and rules for reconstructing executable behavior on the client. Most React developers never inspect a Flight payload. It looks like JSON fragments, dollar-sign references, and module pointers that React silently reassembles into a live component tree.

After CVE-2025-27978 dropped in December 2025, the security community called it React2Shell. It was a CVSS 10.0 unauthenticated remote code execution vulnerability in the Flight deserialization layer. One crafted HTTP request to a Server Function endpoint, and an attacker had shell access. CISA added it to the Known Exploited Vulnerabilities catalog. In-the-wild exploitation was linked to North Korean state-sponsored actors deploying file-less implants through the Ethereum blockchain.

How Flight Works

Flight is a line-delimited format where each row has an ID, a tag, and a payload. When the client parser encounters a string starting with $, it does not treat it as literal text. It routes it through a type-specific resolution path. The $L prefix returns the internal Chunk wrapper object instead of its resolved value, giving attackers a mutable handle on React's internal state machine. The $ prefix with property access performs arbitrary property traversal, walking paths like $1:2:3 by resolving chunk 1, then property 2, then property 3 on the result.

The React2Shell Chain

The vulnerability sat in parseModelString, which resolved deep property paths without validating that properties existed on the object itself rather than up the prototype chain. An attacker supplied $O:1:constructor:constructor, traversing from a plain object to Object.prototype.constructor to Function, JavaScript's built-in eval equivalent. By combining $L to get a mutable chunk handle, setting its then to the hijacked constructor, and using the blob handler to trigger execution, the attacker achieved arbitrary code execution with Node.js process privileges.

The Response and Aftermath

The React team patched it by caching the genuine hasOwnProperty method at module load time, blocking prototype chain traversal. But the property traversal model remains intact. The fix treats the symptom; the structural risk persists.

Further audits revealed related vulnerabilities: CVE-2025-55183 exposed source code when Server Functions stringified arguments, CVE-2025-55184 and CVE-2025-67779 were denial-of-service flaws requiring multiple patch rounds, and CVE-2026-27978 was a CSRF bypass in Next.js Server Action handling. CVE-2026-23864 added an unbounded memory allocation vector.

Defenses, Ranked

The most impactful defense is strict schema validation at the top of every Server Action, before any business logic or logging. Validate types, shapes, lengths, and enumerated values. Use safeParse, not parse. Never destructure arguments before validation. Import server-only at the top of files containing sensitive logic. For CSRF, set sameSite: "strict" on session cookies and add per-session tokens for high-value operations. Never add "null" to allowedOrigins. Treat React's Taint API as a development guardrail, not a security boundary. WAFs help against automated scans but motivated attackers bypass them with padding or encoding tricks.

The pattern is familiar: a framework invents a custom wire format, assumes the server is the sole producer, and discovers the format can be manipulated in transit or that the server can be tricked into deserializing attacker input. Flight is the latest entry in this recurring story.