While React Server Components rely on the custom Flight protocol to stream interactive user interfaces, this exact mechanism introduces powerful deserialization sinks that attackers can exploit. Security researcher Durgesh Pawar has broken down the mechanics behind the CVSS 10.0 "React2Shell" vulnerability, demonstrating how protocol manipulation can lead directly to remote code execution. The findings also underscore a practical, ranked set of defenses—ranging from strict schema validation to cross-site request forgery hardening—for securing modern React applications against these deeply structural architectural risks.
React Server Components do not send traditional HTML to your browser, nor do they send standard JSON. When a server component renders, the data traveling across the wire is actually a custom streaming protocol known as Flight. It functions as a line-delimited format equipped with its own unique type system, reference resolution rules, and specific instructions for reconstructing executable behavior directly on the client side. Most React developers have never opened their browser’s network tab to closely inspect a Flight payload. It typically appears as a perplexing mix of JSON fragments, dollar-sign-prefixed references, and module pointers that the React runtime silently reassembles into a live component tree. Because the framework transparently handles the heavy lifting, few teams have paused to question what that underlying level of architectural trust actually implies.
This blind spot came to light after CVE-2025-55182 was disclosed in December 2025. The security community quickly dubbed the flaw "React2Shell" for good reason. It was an unauthenticated, remote code execution vulnerability rated at a maximum CVSS score of 10.0, residing entirely within the Flight deserialization layer. A single crafted HTTP request directed at a Server Function endpoint granted an attacker full shell access without requiring any user credentials whatsoever. The Cybersecurity and Infrastructure Security Agency promptly added the flaw to its Known Exploited Vulnerabilities catalog, while cybersecurity firm Sysdig tied in-the-wild exploitation directly to North Korean state-sponsored threat actors deploying fileless implants via the Ethereum blockchain.
A deep dive into the source code—specifically within the outlined model resolution and chunk retrieval logic where vital resolution paths live—reveals that React2Shell was never merely an isolated parsing bug. Instead, it serves as a symptom of a broader structural reality. Flight reconstructs executable references, lazy-loaded components, server remote procedure call endpoints, and asynchronous state directly from a continuous stream of text. Functionally speaking, that makes it a complex deserialization system. Consequently, the attack surface extends well beyond a single missing property check, exposing vulnerabilities in how modern web frameworks handle trust across the network boundary.
Flight On The Wire
To understand the mechanics of the protocol, one only needs to open the browser network tab on any Next.js App Router page and search for responses returning the specific content type for components. Flight is not a single, monolithic JSON blob. Rather, it is a streaming, line-delimited format where each line represents a self-contained row that the client-side React runtime processes dynamically as it arrives over an active network connection.
Every row follows a consistent syntax consisting of a numeric row identifier, a tag indicating the data type, and a payload. The tag informs the parser how to treat the incoming data, whether it represents a serialized virtual node, module metadata, an import directive, a resource preload hint, environment information, or a server-side exception. Behind these seemingly benign tags lies a sophisticated prefix system.
When the client-side parser encounters a string value beginning with a dollar sign, it intercepts the string, evaluates the prefix, and routes it through a specialized resolution path. For instance, single dollar signs denote model references to other chunks in the stream, while colons following the prefix handle property traversal. Other prefixes designate native JavaScript symbols, callable server actions acting as remote procedure call endpoints, deferred lazy components, or internal chunk wrapper objects. Exposing this level of internal framework plumbing directly through the protocol enables powerful flexibility, but it also creates significant architectural exposure.
Crucially, property access prefixes allow the protocol to specify paths that instruct the parser to resolve a chunk and subsequently traverse nested properties. Driven entirely by data contained within the incoming stream, this pattern allows arbitrary property traversal. For anyone familiar with auditing JavaScript codebases for prototype pollution, this mechanism immediately highlights potential security risks.
Why Flight Is A Deserialization Sink
The broader pattern of deserialization vulnerabilities is well-documented across enterprise software history, appearing in Java object streams, Python pickle serialization, and PHP object injection. The common thread across these architectures is a predictable lifecycle: an application deserializes attacker-controlled input, invokes unintended behavior during the reconstruction phase, and ultimately loses control of application execution.
JavaScript developers have historically assumed their ecosystem was largely immune to these classes of bugs because native functions like standard JSON parsing only produce plain data objects without firing constructors or executing magic methods. That assumption holds true for raw parsing, but it collapses the moment a framework introduces custom deserialization logic wrapped around the data layer.

Prototype-based inheritance in JavaScript compounds this risk. Every object maintains a link to its prototype, and property lookups walk up this chain. If an attacker injects prototype references during model reconstruction, they can modify shared base objects that all subsequent application data inherits from. Because Flight performs property traversal on deserialized objects by iterating through path segments, encountering unvalidated keys allows traversal straight up the prototype chain.
Furthermore, the JavaScript runtime engine treats any object with a callable property of a specific name as a promise-like thenable, automatically executing it when awaited. Because Flight resolves chunks asynchronously, injecting manipulated objects into the chunk resolution pipeline causes the runtime to execute attacker-controlled functions during routine asynchronous handling. The language semantics themselves inadvertently assist the execution chain.
The Mechanics Of React2Shell
CVE-2025-55182 demonstrated these theoretical risks in practice. The root cause of React2Shell resided within the server-side reply handling logic responsible for resolving deep property paths. When the parser encountered a colon-separated reference, it looped through the segments and traversed the parent object without checking whether properties existed on the object itself or further up the prototype chain.
An attacker supplying a sequence traversing from a plain JSON object up through the object prototype to the core function constructor could effectively leverage the constructor to execute arbitrary code, behaving similarly to an eval statement. Because the deserialization path lacked proper allowlists or prototype filtering, the gadget chain succeeded entirely through the composition of legitimate protocol features operating on untrusted input.
The real-world impact was immediate and severe. Threat intelligence reports soon linked the exploit vector to state-sponsored campaigns, including fileless implants leveraging blockchain networks for command-and-control communications and sophisticated backdoors designed to mimic legitimate kernel daemons on compromised Linux servers. The speed with which attackers weaponized a single unauthenticated HTTP request underscored the critical nature of flaws residing deep within framework deserialization layers.
The Framework Response and Defensive Strategies
The React team addressed the immediate vulnerability by caching the native prototype ownership check method at module load time and utilizing it explicitly for every property verification during deserialization. Even if an attacker attempts to shadow the property check on a malicious object, the runtime falls back to the original prototype method, successfully blocking prototype chain traversal. While this targeted patch effectively neutralizes the known gadget chain, it leaves the underlying property traversal model intact, illustrating the ongoing challenge of securing complex streaming protocols.
To mitigate these structural risks at the application level, development teams must implement rigorous input validation at the very beginning of every server action before any business logic or logging execution occurs. Using robust schema validation libraries ensures that incoming data strictly adheres to expected types, shapes, and boundaries. Validating raw arguments prior to any property destructuring prevents the application from processing unvalidated structures.
Additionally, isolating sensitive backend code using explicit server-only enforcement packages ensures that database credentials and internal logic never inadvertently cross the boundary into client bundles. Teams must also remain vigilant against common architectural traps, such as barrel re-export files that mix client-safe utilities with backend modules, which can inadvertently compromise security boundaries.
Cross-site request forgery hardening requires careful attention beyond basic framework defaults. Relying solely on automatic header checks can introduce edge cases, making explicit cookie configurations with strict site policies and dedicated token validation essential for state-changing operations. Developers must also avoid weakening framework configurations by permitting null origins, which can inadvertently reopen historical security bypasses.
Finally, while development-time guardrails like runtime taint tracking and web application firewalls provide helpful defense-in-depth layers, they should be treated as supplementary precautions rather than primary security boundaries. Because sophisticated attackers can bypass perimeter filters using padding techniques, maintaining updated framework versions and enforcing strict architectural discipline remain the most effective defenses against the complex structural risks inherent in modern server-driven UI protocols.

