Web Development

Weaponizing and Defending the React Flight Protocol: Unpacking the CVSS 10.0 React2Shell RCE and Structural Security Risks

The landscape of modern web development was fundamentally reshaped by the disclosure of CVE-2025-55182 in December 2025, a critical unauthenticated remote code execution (RCE) vulnerability in React Server Components, widely dubbed "React2Shell." This flaw, rated a maximum CVSS 10.0, exposed a profound structural risk within the custom "Flight" protocol, the underlying mechanism that streams interactive user interfaces between server and client in React applications. One meticulously crafted HTTP request to a Server Function endpoint was all it took for an attacker to gain shell access, bypassing authentication entirely.

Background: The Rise of React Server Components and the Flight Protocol

React Server Components (RSCs) emerged as a transformative paradigm, promising enhanced performance, improved user experience, and simplified data fetching by allowing developers to render components on the server and stream them efficiently to the client. Unlike traditional server-side rendering (SSR) that sends HTML, or client-side rendering (CSR) that relies on JSON APIs, RSCs introduce a novel approach. They do not transmit raw HTML or conventional JSON. Instead, when a server component renders, the data traveling over the wire adheres to a custom streaming protocol known as Flight.

Flight is a sophisticated, line-delimited format designed for efficiency and dynamic reconstruction. It boasts its own type system, a unique reference resolution mechanism, and a precise set of rules for reassembling executable behavior on the client side. For most React developers, the intricacies of Flight remain an abstraction; the framework silently handles its reassembly into a live component tree, often without scrutiny of its underlying wire format. However, as the React2Shell vulnerability starkly illustrated, this implicit trust in a custom deserialization system carries significant security implications.

The Flight protocol leverages various row tags to structure its stream, including J for JSON trees, M for module metadata, I for import directives, HL for hints/preloads, D for data, and E for errors. Crucially, it employs a $ prefix system that allows the client-side parser to interpret string values as model references ($), property accessors ($:), symbols ($S), server references ($F), lazy components ($L), promises/raw chunks ($@), and binary data ($B). This prefix system dictates the parser’s control flow, transforming a seemingly benign data format into a powerful instruction set for the client runtime.

The React2Shell Vulnerability: A Deep Dive into CVE-2025-55182

The React2Shell vulnerability (CVE-2025-55182) was rooted in the Flight protocol’s deserialization layer. Specifically, it exploited powerful deserialization sinks that allowed protocol manipulation to achieve RCE. The core of the problem lay in the way Flight reconstructs executable references, lazy-loaded components, server RPC endpoints, and asynchronous state from a stream of text. This complex process effectively functions as a deserialization system, a class of vulnerability historically prone to severe security flaws across various programming languages, including Java’s ObjectInputStream, Python’s pickle, PHP’s unserialize, and .NET’s BinaryFormatter.

At the heart of the exploit was the getOutlinedModel function, responsible for resolving deep property paths specified via the $:reference system within the Flight stream. When the parser encountered a reference like $1:user:name, it would split the string by colons and sequentially traverse the properties on the parentObject. The critical flaw was a missing hasOwnProperty check within this traversal loop, found in the server-side reply handling code (ReactFlightReplyServer.js):

for (key = 1; key < reference.length; key++)
    parentObject = parentObject[reference[key]];

This seemingly innocuous two-line loop lacked validation, allowing an attacker to inject __proto__ or constructor as path segments. By supplying a specially crafted input like $1:__proto__:constructor:constructor, the attacker could force the traversal to climb the JavaScript prototype chain. This allowed them to reach Object.prototype, then the Object constructor, and ultimately the Function constructor. In JavaScript, the Function constructor behaves like eval(), meaning Function("arbitrary code")() executes arbitrary code.

The gadget chain for React2Shell was a sophisticated orchestration of several legitimate Flight protocol features:

Weaponizing And Defending The React Flight Protocol: Deserialization Sinks In RSCs — Smashing Magazine
  1. Prototype Pollution: Achieved via the $:prefix, allowing __proto__ and constructor traversal.
  2. Raw Chunk Exposure: The $@ prefix, which exposes the internal Chunk wrapper object, provided a mutable handle.
  3. Thenable Abuse: By injecting a manipulated .then property into the exposed Chunk object, an attacker could force the React runtime to execute their function during normal await behavior, leveraging JavaScript’s duck-typing of Promises.
  4. Server Action Forgery: The $F prefix, intended for callable Server Actions, was then used to trigger the maliciously modified function as an RPC endpoint.

The vulnerability was not a simple parsing bug but a systemic issue arising from the protocol’s design, which enables direct control over the client-side runtime’s control flow through streamed data.

Immediate and Widespread Exploitation

The impact of React2Shell was profound and immediate. The federal Cybersecurity & Infrastructure Agency (CISA) promptly added CVE-2025-55182 to its Known Exploited Vulnerabilities catalog, signaling its critical nature and active exploitation in the wild. Security researchers and threat intelligence firms quickly began to document its weaponization.

Sysdig, a leading cloud security company, published detailed research linking in-the-wild exploitation to North Korean state-sponsored advanced persistent threat (APT) actors. These actors deployed EtherRAT, a novel file-less implant that leverages the Ethereum blockchain for command-and-control (C2) communication. This technique, dubbed "EtherHiding," makes takedown efforts incredibly difficult due to the decentralized and immutable nature of blockchain technology. The ability to deploy such a sophisticated implant through a single, unauthenticated HTTP request underscored the severity of the vulnerability.

Separately, Palo Alto Networks’ Unit 42 documented another backdoor, KSwapDoor, which masqueraded as [kswapd1] on infected Linux systems, blending seamlessly into process lists alongside the legitimate kswapd0 kernel swap daemon. Their analysis confirmed KSwapDoor’s use of RC4 encryption for internal strings and configuration, with C2 communications secured via AES-256-CFB and Diffie-Hellman key exchange across a P2P mesh network. The speed and sophistication of these state-sponsored campaigns, deploying novel implants through an RCE, highlighted why a CVSS 10.0 in a deserialization layer demands immediate patching.

The Framework’s Response: Patching the Critical Flaw

The React team responded swiftly and decisively. The fix for React2Shell was released in React versions 19.0.1, 19.1.2, and 19.2.1. The core change involved caching the genuine hasOwnProperty method at module load time:

var hasOwnProperty = Object.prototype.hasOwnProperty;

Subsequently, every property check in the deserialization path was modified to use .call() to invoke this cached reference:

hasOwnProperty.call(value, i);

This approach effectively neutralizes the prototype chain traversal that enabled the gadget chain. By using the original prototype method, even if an attacker attempts to shadow hasOwnProperty on a malicious object, the integrity check remains uncompromised. Developers were urged to immediately update their react, react-dom, and react-server-dom-webpack packages to the patched versions to mitigate the RCE risk. While effective, this fix addressed the symptom (missing ownership checks) rather than fundamentally redesigning the arbitrary property traversal model inherent in the $:prefix system. This leaves open the possibility for future vulnerabilities if new edge cases or interaction patterns are discovered within this powerful primitive.

Beyond React2Shell: A Cascade of Related Vulnerabilities

The disclosure of React2Shell triggered intense security audits across the React ecosystem, leading to the discovery and subsequent patching of several related vulnerabilities within the same deserialization surface. While none reached the severity of the initial RCE, they underscored the fragility of custom deserialization systems.

  • CVE-2025-55184 (CVSS 7.5, DoS): Disclosed shortly after React2Shell, this vulnerability involved an infinite recursion of nested Promises during Server Function deserialization, capable of hanging the Node.js event loop. It was initially patched in React 19.0.2, 19.1.3, and 19.2.2.
  • CVE-2025-67779 (CVSS 7.5, DoS): This CVE represented an incomplete fix for CVE-2025-55184. Researchers found edge cases that the initial patch missed, allowing the same infinite loop to be triggered. A second round of patching was required, landing in React 19.0.4, 19.1.5, and 19.2.4. This highlights the difficulty of securing complex deserialization parsers comprehensively.
  • CVE-2026-23864 (CVSS 7.5, DoS/OOM): Disclosed in January 2026, this vulnerability exposed a third denial-of-service vector. It involved unbounded request body buffering and zipbomb-style decompression, leading to memory exhaustion and server crashes. Patches were included in React 19.0.4+, 19.1.5+, and 19.2.4+.
  • CVE-2025-55183 (CVSS 5.3, Information Disclosure): A subtle but impactful flaw, this vulnerability allowed attackers to reflect Server Function source code. If a Server Function implicitly or explicitly stringified an attacker-controlled argument (e.g., for logging or debugging), a crafted request could cause the deserialization parser to reflect the function’s own source code back in the response. This could expose business logic, database queries, or even hardcoded secrets, posing a significant risk to intellectual property and operational security. Fixed in React 19.0.1, 19.1.2, and 19.2.1.
  • CVE-2026-27978 (CVSS 5.3, CSRF Bypass): This was a Next.js-specific vulnerability, a CSRF bypass in its Server Action handling. Next.js typically validates that the Origin header matches the Host header to prevent cross-site request forgery. However

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button