# Root Cause Analysis: CVE-2025-55182

## Vulnerability Summary

**CVE:** CVE-2025-55182
**Product:** npm:react-server-dom-webpack (also react-server-dom-parcel, react-server-dom-turbopack)
**Affected Versions:** 19.0.0, 19.1.0-19.1.1, 19.2.0
**Fixed Versions:** 19.0.1, 19.1.2, 19.2.1
**Severity:** Critical (CVSS 10.0)
**Impact:** Pre-authentication Remote Code Execution
**Surface:** API Remote (HTTP POST to Server Function endpoint)

## Root Cause

The vulnerability exists in the Flight protocol deserialization code of
`react-server-dom-webpack/server`, specifically in the `decodeReply()`
function that processes attacker-controlled `multipart/form-data` payloads
from HTTP requests to Server Function endpoints.

The core flaw is in **reference resolution**: when the Flight protocol
encounters references like `$1:property:property`, it traverses object
properties **without checking `hasOwnProperty()`**. This allows an attacker
to traverse the JavaScript prototype chain and reach dangerous built-in
constructors:

- `$1:__proto__:then` resolves to `Chunk.prototype.then` (the real
  `then` method on Chunk objects, accessed via the prototype chain)
- `$1:constructor:constructor` resolves to `Function` (the JavaScript
  Function constructor, accessed via `Object.constructor.constructor`)

## Exploit Mechanism

The exploit (based on the public PoC by researcher maple3142) crafts a
`multipart/form-data` body with two form fields:

1. **Field "0"**: A JSON model object containing:
   - `then: "$1:__proto__:then"` — sets the chunk's `then` to
     `Chunk.prototype.then` via prototype chain traversal
   - `status: "resolved_model"` — triggers `initializeModelChunk()` on
     the fake chunk when it is awaited
   - `_response._prefix` — the attacker's code string (e.g.,
     `process.mainModule.require('child_process').execSync('id')`)
   - `_response._formData.get: "$1:constructor:constructor"` — resolves to
     the `Function` constructor via prototype chain

2. **Field "1"**: `"$@0"` — creates a Chunk reference to field "0"'s value

When `decodeReply()` processes this FormData:
1. Field "1" (`$@0`) creates a Chunk reference to field "0"
2. The model object from field "0" is parsed
3. When the chunk is awaited, `Chunk.prototype.then` runs with the fake
   model object as `this`
4. `status: "resolved_model"` triggers `initializeModelChunk()`
5. `initializeModelChunk()` accesses `_response._formData.get` (which is
   the `Function` constructor) with `_response._prefix` (the code string)
6. `new Function(codeString)()` executes arbitrary code on the server

A single unauthenticated POST request with a `Next-Action` header triggers
this chain, achieving remote code execution.

## Evidence of Reproduction

### Vulnerable Version (react-server-dom-webpack@19.2.0)
- **Server started**: Health check passed (`HEALTHY 19.2.0`)
- **Exploit sent**: POST with `Next-Action: x` header and crafted
  `multipart/form-data` body
- **RCE confirmed**: The `id` command executed on the server, writing
  `uid=1000(vscode) gid=1000(vscode) groups=1000(vscode),962(962)` to a
  marker file
- **Stack trace**: Server log shows the code executed via `eval` at
  `parseModelString` in `react-server-dom-webpack-server.node.unbundled.development.js`

### Fixed Version (react-server-dom-webpack@19.2.1) — Negative Control
- **Server started**: Health check passed (`HEALTHY 19.2.1`)
- **Same exploit sent**: Identical POST request
- **RCE blocked**: `decodeReply()` completed normally, returning
  `[object Object]` without executing any code
- **No marker file created**: The `hasOwnProperty()` checks added in the
  patch prevent prototype chain traversal, blocking the `Function`
  constructor access

## The Fix

The patch (in versions 19.0.1, 19.1.2, 19.2.1) adds explicit
`hasOwnProperty()` checks before returning values from module export
objects during reference resolution. This prevents attacker-controlled keys
from resolving inherited prototype properties, blocking the prototype-chain
access that enabled the `Function` constructor gadget.

Additionally, the patch improves multipart payload decoding robustness by
adding error handling around field and file processing.

## Reproduction Environment

- **Node.js**: v24.15.0
- **OS**: Ubuntu 26.04 LTS
- **Vulnerable package**: react-server-dom-webpack@19.2.0
- **Fixed package**: react-server-dom-webpack@19.2.1
- **Server**: Minimal HTTP server calling `decodeReply()` with
  `--conditions react-server` flag
- **Exploit**: Python client sending the crafted multipart POST

## Artifacts

- `logs/server_vuln.log` — vulnerable server log showing decodeReply call
- `logs/server_fixed.log` — fixed server log showing normal completion
- `repro/artifacts/request_vuln.txt` — raw HTTP exploit request
- `repro/artifacts/response_vuln.txt` — HTTP response (timeout, expected)
- `repro/artifacts/request_fixed.txt` — same request against fixed version
- `repro/artifacts/response_fixed.txt` — HTTP 200 with normal result
- `repro/artifacts/rce_marker_vuln.txt` — `id` command output proving RCE
- `repro/runtime_manifest.json` — structured runtime evidence
