## https://sploitus.com/exploit?id=52F2BB74-6623-5675-966C-09A22AC1B474
# CVE-2025-55182
This repository contains a PoC reproduction of **CVE-2025-55182**, a critical security vulnerability in React Server Components (RSC) that allows unauthenticated arbitrary code execution.
## Description
The vulnerability exists in how React Server Components deserialize "Server Actions" from client requests. Specifically, the `requireModule` function failed to validate that the requested export name was a direct prop of the module. This allowed attackers to access the `constructor` prop of exported functions, obtaining a reference to the global `Function` constructor, which can be used to execute arbitrary code.
## Reproduction
This PoC uses a minimal Node.js environment to isolate the vuln in the `react-server-dom-webpack` library, just to make sure that the exploit demonstrates the bug in the library itself, NOT a misconfiguration in a framework.
### Prerequisites
* Node.js
* `npm`
### Installation
```bash
npm install
```
> [!NOTE]
> The `package.json` is pinned to the vulnerable version `19.0.0`.
### Proof of Concept
1. **Start the vulnerable server**
This script sets up a raw HTTP server that uses the vulnerable React runtime to decode requests.
```bash
# tty1
node --conditions react-server server.js
```
2. **Run the exploit script**
In a separate terminal, run the exploit. This sends a malicious Flight payload to the server.
```bash
# tty2
node exploit.js id
```
You should see the command output returned in the response:
**Expected Output:**
```text
Response: uid=0(root) gid=0(root) groups=0(root)
```
## Analysis
**Why did the vulnerability happen?**
The `requireModule` function in `ReactFlightDOMServerNode.js` basically just trusted whatever `name` the client sent. It did `moduleExports[metadata[NAME]]` without checking whether that property was actually meant to be exposed. So if the client said "bro, I want this property", the server just said "sure thing! here you go, dawg".
**Why is letting people access any property a bad idea?**
Because it basically lets anyone reach into the prototype chain, even the **`constructor`**, which is super dangerous. If the module happens to export a function (like `module.exports = () => {}`), then its `constructor` is literally the global `Function` constructor.
**Why does getting the `Function` constructor mean RCE?**
Once an attacker grabs the `Function` constructor, they can abuse the "Bound Server Action" feature. They bind a string containing malicious JavaScript to it (basically turning it into `new Function("evil code")`). And once that runs, the server executes whatever code they put in.
**Why would React actually run that malicious function?**
Because Server Actions can be triggered by ID. If the attacker crafts a payload with an Action ID that points to their `module#constructor` reference, React resolves it like a normal action and executes it. That "action" is actually their malicious function.
**Why wasn't any of this validated?**
The system just assumed that `id` and `name` from the Server Reference metadata would always refer to valid exports defined by the developer. There was no safety check like `hasOwnProperty` to make sure the requested prop was actually a real export and not something inherited from the prototype chain.
### Why `server.js` instead of Next.js?
I use a raw `server.js` (and a helper `webpack-runtime.js`) to manually configure the React Server Components runtime. This allows us to:
1. **Force the vulnerable setup:** The exploit only works if a module is exported as a function (`module.exports = fn`)s. A real bundler might change how exports are wrapped, depending on its config tho.
2. **Isolate the bug:** This lets us show the problem is inside `react-server-dom-webpack`, not Next.js.
3. **Recreate the bundler environment:** `react-server-dom-webpack` assumes it's running inside a Webpack bundle. Our [webpack-runtime.js](/webpack-runtime.js) gives it the globals it expects (`__webpack_require__`, `__webpack_chunk_load__`).
This is not mocking the vuln, it's just giving the library the bare minimum runtime it needs to actually work.
### Notes
There has been discussion about "[Invalid PoCs](https://react2shell.com)" that only work if the developer purposely exposes dangerous stuff such as `child_process.exec`.
This PoC is not one of those. It works on a normal, safe setup.
1. The exposed function is harmless
The app exposes a simple `updateProfile` function that just returns a string and nothing sketchy, no shell commands.
2. The exploit fully escapes that function
The vulnerability lets the attacker ignore the safe export and jump straight to `updateProfile.constructor`, which is the global `Function` constructor.
3. The core issue is the property access
React should NOT have allowed access to `.constructor`. The developer did not intend to expose the `Function` constructor, instead, the insecure deserialization did that for them.
The only real requirement is that the module exports a function directly (`module.exports = fn`), which is super common in CommonJS and many bundler setups.
### The Payload
The payload in `exploit.js` crafts a React Flight message with three chunks:
* **Chunk 0:** Points to a Server Reference defined in Chunk 1.
* **Chunk 1:** Declares the Server Reference:
* `id`: `"user-profile-action#constructor"`, meaning "give me the constructor".
* `bound`: points to Chunk 2, which contains the arguments.
* **Chunk 2:** `["console.log('nice try, diddy!')"]`: the malicious code string.
When React deserializes this:
1. It resolves `user-profile-action`.
2. Reads the `.constructor` property => getting the global `Function`.
3. Binds the attacker-provided string to it.
4. Effectively executes:
`new Function("console.log('nice try, diddy!')")`
And that's the RCE!
## Mitigation
Upgrade immediately to the patched versions:
* `react-server-dom-webpack` >= 19.0.1
* `react-server-dom-parcel` >= 19.0.1
* `react-server-dom-turbopack` >= 19.0.1
The patch introduces `hasOwnProperty` checks to prevent accessing inherited properties and restricts `base64` file uploads.
If you run this PoC against a patched version, the server will crash or error with:
```console
$ node --conditions react-server server.js
Listening on http://localhost:3000
/path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726
resolvedValue = resolvedValue.bind.apply(
^
TypeError: Cannot read properties of undefined (reading 'bind')
at /path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726:43
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.19.3
```
This confirms that the exploit failed to access the `constructor` property (it returned `undefined` instead of `Function`), and thus the subsequent `.bind` call failed.
## Disclaimer
This code is for educational and testing purposes only. Do not use this exploit against systems you do not own or have explicit permission to test.
## License
Released under `DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE`.