Share
## https://sploitus.com/exploit?id=C75EB9F8-89A7-56DE-BA21-0347ABB66EBE
# 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`.