React Server Components (RSC) inadvertently allowed unauthenticated attackers to access the Javascript 'constructor' via the Flight protocol. Combined with a build error that pushed unsafe experimental code to stable releases, this creates a trivial RCE vector (CVSS 10.0). Affects Next.js 14/15 and other RSC frameworks. Patch immediately.
A critical RCE in React Server Components (RSC) caused by unsafe deserialization in the Flight protocol, exacerbated by a build pipeline failure that shipped experimental code to production. Attackers can execute arbitrary code by manipulating property lookups during Server Action resolution.
React Server Components (RSC) were supposed to be the holy grail of frontend development: server-side logic seamlessly blended with client-side interactivity. The magic behind this is the 'Flight' protocol, a serialization format that lets the server and client talk about component trees and functions. But magic requires strict rules, and in late 2024, Meta forgot the most important one: don't trust the client.
Here is the setup: When a user triggers a 'Server Action' (like submitting a form), the client sends a serialized reference telling the server exactly which function to run. It effectively says, 'Hey server, load module X and run function Y.' Ideally, the server checks if 'function Y' is actually a valid export.
In reality? The server just blindly grabbed whatever property the client asked for. It’s the digital equivalent of a restaurant letting customers walk into the kitchen and cook their own food—except instead of cooking, they're lighting the stove on fire. This vulnerability, dubbed 'React2Shell', turns your fancy Next.js application into a public bash terminal.
The root cause is a classic tale of two failures: unsafe code and a broken build pipeline. First, let's talk about the code. The RSC renderer uses a function called requireModule to resolve the Server Action requested by the client. It takes a metadata object containing an ID (the module) and a NAME (the export).
In JavaScript, everything is an object. If I have a module export myAction, and I ask for myAction['constructor'], I get the native Object constructor. If I ask for that constructor's constructor, I get the global Function constructor. The vulnerable code accessed these properties using bracket notation without checking if they were legitimate exports.
But wait, it gets dumber. This unsafe code was arguably experimental. So why was it in your production app? Because of a script called scripts/rollup/build-all-release-channels.js. This build script used mergeDirsSync(experimentalDir, stableDir), effectively overwriting the hardened, stable code with the wild, unsafe experimental version. They quite literally pushed 'test code' to 'prod' for the entire ecosystem.
Let's look at the vulnerable logic found in bundler integrations like react-server-dom-webpack. The function simply takes the attacker-controlled NAME and accesses it on the module exports.
export function requireModule<T>(metadata: ClientReference<T>): T {
// 1. Load the module based on ID
const moduleExports = bundlerRequire(metadata[ID]);
// 2. BLINDLY return the property requested by the attacker
return moduleExports[metadata[NAME]];
}If metadata[NAME] is "constructor", the server returns moduleExports.constructor. Because RSC treats the resolved value as a function to be executed, the attacker can leverage this to invoke the Function constructor, which allows dynamic code evaluation.
The fix is deceptively simple: verify that the property actually belongs to the module and isn't inherited from the prototype chain.
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = bundlerRequire(metadata[ID]);
// 1. Check if the property is an "Own Property"
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
// 2. Fail safely if it's a prototype property
return (undefined: any);
}By wrapping the access in hasOwnProperty.call, they ensure that you can only request things the developer explicitly exported, not the underlying machinery of the JavaScript language.
Exploiting this requires no authentication, just a way to send a POST request to an RSC endpoint. The attacker constructs a 'Flight' payload that mimics a Client Reference. Instead of asking for a legitimate function like submitForm, we ask for constructor.
Here is a simplified view of the attack chain:
name field of the reference is set to constructor.Object.constructor, climbs to Function, and creates a new function from a string provided in the payload arguments.Sophisticated actors (like the Rondodox botnet) are already automating this. They scan for the endpoint, send the payload, and drop a persistent web shell before you've even finished your morning coffee.
This is a CVSS 10.0. There is no "configuration tweak" that fixes this reliably. You cannot just block the word "constructor" in your WAF because the Flight protocol is complex and encodings vary. You must patch the underlying library.
Immediate Actions:
19.0.1, 19.1.2, or 19.2.1 immediately.15.0.5+ or 15.1.0+. If you are stuck on Next.js 14, look for patch release 14.2.35.package-lock.json or yarn.lock to ensure react-server-dom-webpack is not pinned to a vulnerable version.If you are unable to patch immediately, take the application offline. Leaving a React2Shell vulnerable app exposed to the internet right now is professional negligence.
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 Components Meta | 19.0.0 - 19.2.0 (exclusive of patch) | 19.0.1, 19.1.2, 19.2.1 |
Next.js Vercel | 14.3.0 - 15.0.4 | 15.0.5 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2025-55182 |
| CVSS v3.1 | 10.0 (Critical) |
| CWE | CWE-502 (Deserialization of Untrusted Data) |
| Attack Vector | Network (Pre-auth) |
| EPSS Score | 0.47368 (High) |
| Exploit Status | Active / Weaponized |
| KEV Listed | Yes (2024-12-05) |
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.