React Server Components (RSC) failed to properly validate module exports during the deserialization of client-to-server payloads. By crafting a specific multipart request, unauthenticated attackers can bypass checks and execute arbitrary system commands. This is actively exploited in the wild (React2Shell).
A critical Remote Code Execution (RCE) vulnerability in React Server Components (RSC) allows unauthenticated attackers to hijack the deserialization process of Server Actions, turning a standard web request into arbitrary code execution.
React Server Components (RSC) were sold to us as the holy grail of frontend development: run your heavy logic on the server, send lightweight JSON to the client, and blur the lines between backend and frontend. It is a beautiful abstraction. But as security researchers know, every abstraction is just a place to hide a vulnerability.
At its core, RSC introduces a mechanism for the client to invoke functions on the server—effectively a proprietary Remote Procedure Call (RPC) protocol. The server needs to listen, decode the request, look up the function, and run it. CVE-2025-55182, affectionately dubbed React2Shell, is what happens when that lookup logic is too trusting.
This isn't just an XSS or a data leak. This is full unauthenticated Remote Code Execution. It turns your shiny Next.js or React app into a public terminal for anyone with curl and a malicious payload. The vulnerability exists in the foundational packages that handle this communication: react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack.
The vulnerability lives in how React handles "Server References." When a client triggers a Server Action, it sends a multipart form request. One part of this request contains a map—metadata telling the server, "Hey, I want to run the function exported as updateProfile from module user-actions."
The server-side decoder, specifically inside decodeReplyFromBusboy, parses this stream. The fatal flaw was a classic case of Insecure Deserialization mixed with Prototype Pollution. The server would resolve the module ID provided by the client and then blindly access the property name requested by the client.
In the unpatched versions, the code didn't rigorously check if the requested export actually belonged to the module. It just tried to grab it. If an attacker requested a property inherited from Object.prototype (like __proto__ or constructor) or pointed the module ID to something internal (like the process global), the JavaScript engine happily obliged. By the time the server realized something was wrong, the payload had already corrupted the execution flow.
Let's look at the "smoking gun" in the bundler configuration. The fix is embarrassingly simple, highlighting just how fragile dynamic property access can be in JavaScript.
Here is a simplified view of the vulnerable logic used to resolve a server reference:
// VULNERABLE LOGIC
function requireModule(moduleId, exportName) {
const mod = __webpack_require__(moduleId);
// If the attacker controls exportName, they can access
// inherited properties or internal getters.
return mod[exportName];
}And here is the patch implemented by Meta. Notice the introduction of hasOwnProperty:
// PATCHED LOGIC
function requireModule(moduleId, exportName) {
const mod = __webpack_require__(moduleId);
// Verify the export actually exists on the module instance
// and isn't something sneaky from the prototype chain.
if (mod && mod.hasOwnProperty(exportName)) {
return mod[exportName];
}
throw new Error('Export not found');
}Additionally, the patch hardened decodeReplyFromBusboy in ReactFlightDOMServerNode.js. Previously, if an error occurred during stream processing, the server might leave the stream open or partially processed. The fix now wraps the resolution in a try...catch block and explicitly calls busboyStream.destroy(error), nuking the connection the moment malformed data is detected.
Exploiting this requires understanding the RSC wire format. It's not standard JSON; it's a stream of tagged data. An attacker constructs a multipart/form-data request targeting a Server Action endpoint.
Step 1: The Setup
The attacker sends a payload defining a "Server Reference." They manipulate the id (the module path) and the name (the export name).
POST /rsc-endpoint HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="1_metadata"
{"id":"./node_modules/.../vulnerable-module.js","name":"constructor","chunks":[]}
------WebKitFormBoundary--Step 2: The Execution
By setting the name to constructor or utilizing a gadget chain within the available modules on the server, the attacker can instantiate arbitrary objects. If they can point the module ID to a system library (if the bundler configuration allows internal module resolution, which often happens in misconfigured Webpack setups), they can invoke child_process.exec.
Even without direct internal module access, attackers have used this to pollute Object.prototype, causing the next legitimate server operation to execute a command payload injected into the prototype chain. This is exactly what the "Rondodox" botnet is doing—spraying these requests to turn servers into mining zombies.
This is a CVSS 10.0. That is not a score handed out lightly. It means:
If you are running a modern React/Next.js stack with Server Actions enabled (which is the default in newer versions), you are likely vulnerable. The impact is total system compromise. Attackers can read environment variables (AWS keys, DB creds), delete data, or install persistent backdoors. CISA adding this to the KEV catalog confirms that ransomware groups are already using it to breach corporate networks.
There is no configuration workaround that is safe enough. You must patch. The vulnerability is in the core logic of how React talks to the server.
Upgrade Paths:
After patching, check your logs. Look for 500 errors on Server Action endpoints involving multipart parsing errors—this could indicate failed exploit attempts. Also, consider adding WAF rules to block multipart requests containing suspicious keywords in the name field (like prototype, __proto__, or constructor), though this is a fragile defense compared to patching.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
react-server-dom-webpack Meta | 19.0.0 - 19.0.0 | 19.0.1 |
react-server-dom-parcel Meta | 19.1.0 - 19.1.1 | 19.1.2 |
react-server-dom-turbopack Meta | 19.2.0 - 19.2.0 | 19.2.1 |
| Attribute | Detail |
|---|---|
| CVSS Score | 10.0 (Critical) |
| CWE ID | CWE-502 (Insecure Deserialization) |
| Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H |
| Exploit Status | Active / Weaponized |
| KEV Status | Listed (CISA KEV) |
| Attack Vector | Network (RSC Protocol) |
The application deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
Get the latest CVE analysis reports delivered to your inbox.