So, it is here.
We have spent the last few years aggressively blurring the lines between the client and the server. We did it for "performance," "developer experience," and "isomorphic magic." We built Hydration, we built Server Actions, and we built the React Flight Protocol.
And in doing so, we accidentally built a massive, unauthenticated Remote Code Execution (RCE) cannon pointed directly at our own infrastructure.
Meet CVE-2025-55182, or as the industry has dubbed it, React2Shell / React4Shell. It is inherently marked as a CVSS 10.0. It requires no user authentication. And if you are running a default Next.js setup, you are likely to be affected by the vulnerability right now.
Let’s dig in.
The "Innovation" That Killed Us
At the core of this mess is React Server Components (RSC).
To make RSC work, React needs to talk to the browser. It does this using a text-based serialisation format called the "Flight Protocol." It looks like a fever dream version of JSON:
["$","div",null,{"children":"Please hack me"}]
Here is the problem: When you use Server Actions ("use server"), the communication goes the other way. The client sends data back to the server.
Because we apparently learned nothing from the Java deserialisation nightmare of the 2010s, React uses the same deserialisation logic for your trusted internal component tree as it does for dirty, untrusted user input coming from the Internet.
The Technical Bit (The "Gadget Chain")
The vulnerability is not just a simple injection. It is a gadget chain. We are constructing a JavaScript object that tricks the React server into eating its own face.
The flaw lives in a function called reviveModel. This function is supposed to wake up serialised objects. It has a special case for binary blobs (marked with $B). When it sees one, it executes this specific line:
response._formData.get(response._prefix + value)
Do you see it?
If we can control _formData, we can control what function gets called.
So, the exploit works like a Rube Goldberg machine:
- The Hook - We send an object that looks like a Promise (it has a
.thenmethod). - The Hijack - We point that
.thenmethod to a recursive reference of itself, forcing the parser to resolve it. - The Sink - We set the value to
$B(triggering the Blob logic). - The Payload - We set
_formDatato a malicious object where thegetproperty traverses the prototype chain ($1:then:constructor:constructor).
In layman terms - We trick React into thinking it’s looking up form data. Instead, it climbs the prototype ladder, finds the global Function constructor, and executes whatever string we passed it as code.
The Exploit
Forget the "PoCs" floating around that try to use __proto__. React sanitises that key specifically. The real exploit uses legitimate property paths to bypass the sanitiser entirely.
Here is what a weaponised request looks like. If you see this pattern hitting /_next/server-action in your logs, you have a problem.
POST /_next/server-action HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryAdvisory Example
------WebKitFormBoundaryAdvisory Example
Content-Disposition: form-data; name="1_args"
[{"status":"resolved_model","value":{"then":"$B"},"then":"$1:then","_response":{"_prefix":"console.log('Proof of Concept Executed');//","_formData":{"get":"$1:then:constructor:constructor"}}},"$@0"]
------WebKitFormBoundaryAdvisory Example--
Hunting Tip - If you are hunting for this in your SIEM, don't just look for constructor. Look for a POST to /_next/server-action that returns a HTTP Response 200 OK but spawns a shell as child process (e.g., sh, bash, etc.) immediately after. Legitimate React rendering almost never spawns shell commands.
WAFs Are Dead (Again)
If your "mitigation strategy" is to write a WAF rule to block the word constructor, please stop.
JavaScript is a Turing-complete nightmare for regex.
- I can write
_['c\u006fnstructor']. - I can write
(1).valueOf.constructor. - I can change
name="1_args"toname='1_args'and your WAF likely will not even see the payload, but Node.js will parse it happily.
You simply cannot mitigate this at the edge reliably. You have to fix it, at the code.
The Fix (Read Carefully)
The React team patched this by adding a hasOwnProperty check. It stops the parser from climbing the prototype chain. Simple and effective.
BUT HERE IS THE CATCH.
If you just run npm update react, you are probably still vulnerable.
Next.js (and other meta-frameworks) bundle and compile their own version of the React server code. It’s baked into the framework's distribution. You need to update the framework itself, not just the library.
Do this now:
Remediation - Patching
The React team has introduced a fix that utilizes hasOwnProperty.call to prevent prototype chain traversal during deserialisation.
Note: Updating the react package alone is insufficient for Next.js users. The framework bundles its own version of the React Server DOM. You must update the framework itself.
Recommended Update Commands:
# For Next.js users
npm install next@latest react@latest react-dom@latest
# Verify installation (Next.js 15 users should target v15.5.7+)
# Verify installation (Next.js 16 users should target v16.0.7+)
Temporary Mitigations
If immediate patching is not feasible, consider the following mitigations:
- Disable Server Actions - If your application architecture allows, disabling Server Actions in
next.config.jsremoves the attack vector.
module.exports = {
experimental: {
serverActions: false
}
}
- Container Hardening - Mitigate the impact of potential RCE by running the application container with a non-root user, a read-only filesystem, and strictly limited network egress policies to prevent reverse shells.
The Takeaway
We keep doing this. We keep taking serialization protocols—which are historically the most dangerous things in software—and exposing them to the public internet with zero authentication, all so we can avoid writing a proper API endpoint.
React2Shell (CVE-2025-55182) is a CVSS 10.0 because it works out of the box on default configurations. It is the Log4Shell of the frontend world.
Patch now. Detect actively. And stop trusting that your WAF will save you.
Further Reading
- https://react2shell.com/
- https://github.com/msanft/CVE-2025-55182 (Technical Analysis by Moritz Sanft)
- https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components (React Team Advisory)
- https://www.wiz.io/blog/critical-vulnerability-in-react-cve-2025-55182 (Wiz Research Threat Brief)
Disclaimer: This analysis is based on public threat intelligence, patch diffs, and community research. If you discover new bypasses or variants, please report them to the appropriate security teams.
Top comments (0)