
What is CVE-2025-55182 (React2Shell)?

The CVE-2025-55182 vulnerability, known as React2Shell, occurs in React Server Components (RSC) used in the latest version of React. Attackers can achieve Remote Code Execution without any authentication (Pre-authentication) against the web services using the vulnerable version, and the attack complexity is not high. For these reasons, this vulnerability has received a maximum severity score of 10.0 on the Common Vulnerability Scoring System (CVSS).
Furthermore, the Next.js framework, which is widely used in the current Node.js ecosystem, is also based on React, exposing it to the same vulnerability. This has a very large impact and poses high risks in real service environments. Thus, quickly identifying the scope of this vulnerability and applying patches is of utmost importance.
Before React2Shell…
Before diving into the React2Shell vulnerability, a light understanding of the concept of React Server Component, which may be somewhat unfamiliar to security personnel, and Prototype Pollution, which may be unfamiliar to developers, is needed.
React Server Component & Flight Protocol
When providing web services, processing the web pages displayed to users entirely on the server side and providing the completed DOM is called Server-Side Rendering (SSR). In contrast, delivering only data in an API format and processing the actual DOM composition on the user's web browser (Client-side) is called Client-Side Rendering (CSR).
CSR enables a richer web service experience and interaction by providing the skeletal structure of the page to the user, while all actual DOM composition is performed on the user's web browser. However, as frontend functionalities become increasingly complex, the amount of computations that the browser needs to handle has increased, leading to greater consumption of the user's device resources and decreased user experience due to performance degradation.
To address these issues, React introduced React Server Components (RSC), which handle significant portions of rendering on the server rather than the client. RSC is a technology where the execution of React components occurs on the server side, and the execution results are rendered on the client. It is a concept that combines the existing SSR and CSR, rendering the state of the new page only up to the React Component form on the server, and allowing clients to render the components, thereby reducing client-side load.
Although JSON is an excellent serialization format for handling data, it is unsuitable for dealing with complex React Components. To handle React Components appropriately, it must be able to process complex types and references such as Promise, Blob, and Map, beyond simple strings, dictionaries, and arrays. Therefore, RSC uses a unique protocol and serialization format called the Flight Protocol.
Expression | Type | Example | Description | |
|---|---|---|---|---|
$$ | Escaped $ | "$$hello" → "$hello" | Literal string starting with $ | |
$@ | Promise/Chunk | "$@0" | Reference to chunk ID 0 | |
$F | Server Reference | "$F0" | Server function reference | |
$T | Temporary Ref | "$T" | Opaque temporary reference | |
$Q | Map | "$Q0" | Map object at chunk 0 | |
$W | Set | "$W0" | Set object at chunk 0 | |
$K | FormData | "$K0" | FormData at chunk 0 | FormData object |
$B | Blob | "$B0" | Blob at chunk 0 | Blob object |
$n | BigInt | "$n123" | BigInt value | |
$D | Date | "$D2024-01-01" | Date object | |
$N | NaN | "$N" | NaN value | |
$I | Infinity | "$I" | Infinity | |
$- | -Infinity/-0 | "$-I" or "$-0" | Negative infinity or negative zero | |
$u | undefined | "$u" | Undefined value | |
$R | ReadableStream | "$R0" | ReadableStream | |
$0-9a-f | Chunk Reference | "$1", "$a" | Reference to chunk by hex ID |
Prototype Pollution
Objects in Javascript are quite distinct from the object style commonly known as 'object-oriented' in Java or C++. In Javascript, when objects are created, they are not inherited from a class but from another object. In other words, new objects do not clone from a specific template (Class) but expand their functionality based on another object they reference.
In this inheritance structure, a Prototype is the parent object referred to by an object, and it is the target where a lookup continues for properties or methods not directly possessed by the object. For example, in Javascript, an array uses Array.prototype as its prototype, where methods like toString and push are implemented, allowing them to be used through the prototype.
Due to this characteristic of Javascript, if a property can be set on a prototype object through any means, it can seem as if newly created objects have that property set. This act of polluting the prototype or accessing it inappropriately is called Prototype Pollution. It might be a somewhat unfamiliar concept at first, but it can be easily understood through the example code below, and we'll explore Prototype Pollution in more detail later.
Cause Analysis
Diff Analysis
Before analyzing the cause of the vulnerability, it's worth noting that the React2Shell vulnerability was patched through commit 7dc903c in the GitHub repository facebook/react (GitHub Commit). Among the changes made with this commit, modifications related to the Flight Protocol and Prototype were made in packages/react-server/src/ReactFlightReplyServer.js.

caption - Addition of property value checks in getOutlinedModel function implementation within ReactFlightReplyServer.js
Processing Flight Protocol - Gateway to RSC
When the getOutlinedModel function is called among the Flight Protocol data passed to the react-server, it is preprocessed through function calls like below in ReactFlightReplyServer.js.
initializeModelChunk()Initialization of Chunk when Flight Protocol request occursreviveModel(): Restores Model from request dataparseModelString(): Creates Model from string data (deserialization)getOutlinedModel(): Handles Chunk References occurring during deserialization
Raw Chunk Reference
In the earlier overview of the Flight Protocol, expressions like $@0 were explained to refer to Chunk 0. Looking at the actual implementation, the parseModelString() function appears as follows. (ReactFlightReplyServer.js:929)
For References starting with @, it is implemented as a Raw reference that accepts and returns the Chunk Promise itself. Through this, a reference to the Promise can be obtained. (CAUSE #1)
Unserialize & Prototype Pollution - To the essence of Chunk
The implementation of the getOutlinedModel() function just before the vulnerability patch is as follows. (ReactFlightReplyServer.js:595)
In this function, when chunk.status is INITIALIZED, it can be seen that path members obtained with reference.split(':') continue to be referenced within value. Because checks like hasOwnProperty are absent in this process, it is possible to achieve Prototype Pollution through the __proto__ member. (CAUSE #2)
For instance, if the reference expression is $1:__proto__:aaa, it would reference the member aaa of the prototype of Chunk 1.
At this time, if Chunk 1 is a Promise type object as seen with $@0 earlier, then $1:__proto__ would represent (Chunk0).__proto__, which eventually means that Chunk.prototype can be accessed.
Using CAUSE #1 and CAUSE #2, an attacker becomes capable of accessing Chunk.prototype. (PRIMITIVE #1)
Chunk.prototype - Make initializeModelChunk Great Again
The information obtained about Chunk.prototype through PRIMITIVE #1 can also be confirmed within the same ReactFlightReplyServer.js file. (ReactFlightReplyServer.js:125)
Chunk is essentially a Promise object, and the .then() method is divided into different behaviors depending on this.status.
Meanwhile, by using PRIMITIVE #1, if $1:__proto__:then is referenced, it becomes possible to make any property of the chunk into the Chunk.prototype.then function, thereby allowing a property named then to point to Chunk.prototype.then.
If a Chunk is set as exemplified above, then is essentially Chunk.prototype.then, and since this.status is resolved_model within then, an attacker can call any initializeModelChunk function if only this chunk (actually a Promise) can be resolved. (PRIMITIVE #2)
initializeModelChunk - Once More
By utilizing PRIMITIVE #2, an attacker can call the initializeModelChunk function using fully-controllable values once more. The implementation of this function is as follows. (ReactFlightReplyServer.js:446)
At this time, the attacker can completely control the value of resolvedModel, allowing them to call the reviveModel function with any JSON object. Furthermore, chunk._response was likewise a manipulatable value since the initializeModelChunk() call process, so PRIMITIVE #2 reduces to an arbitrary reviveModel function call.
reviveModel - Blob
The reviveModel() function internally calls parseModelString() as before. Within this parseModelString() is logic for handling Blob data. (ReactFlightReplyServer.js:446)
At this time, recalling that the response being referenced in the code block is a manipulatable value by the attacker, blobKey can also be manipulated in the form of (desiredString) || (arbitraryInteger), and response._formData.get can also be manipulated with appropriate value.
Since response._formData.get needs to be a callable function, CAUSE #1 can be recalled and applied.
As above, since $1:constructor:constructor becomes Function.constructor, if a chunk is organized as below, it becomes possible to create an arbitrary function using Function.constructor and assign it to value.
If the above _response is processed through Blob, eventually, the function Function.constructor("console.log(1337);//1") is returned, resulting in the following structure.
Here, the attacker can create any desired JavaScript function and further make value itself a Thenable with the then as a function.
Also, looking back at the Chunk.prototype.then() function,
Upon completion of the initializeModelChunk call where Blob is processed and value is a Thenable with a then made by the attacker's arbitrary function, the line resolve(chunk.value) ends up executing an arbitrary JavaScript function. (PRIMITIVE #3)
Sum Everything, Next Resolves Everything
At this point, it's necessary to revisit what the attacker gains in terms of Primitive information.
Regardless of form, if the then of Chunk is resolved initially, PRIMITIVE #2 leads to PRIMITIVE #3, thereby enabling an arbitrary function call.
Consider this configuration of the attacker's Chunk:
In such a configuration, provided then is correctly invoked, the flow below enables arbitrary code execution server-side.
.then()is executed with the whole asthissince it's essentiallyChunk.prototype.thenvalue = JSON.parse("{\\"then\\": \\"$B1\\"}"), followed byreviveModelcallWithin
reviveModel, set$B1337withFunction.constructor("console.log(1);//1")The
thenofvalueis invoked again ⇒ triggersFunction.constructor("console.log(1);//1)()executionExecutes arbitrary javascript code found in
_response._prefix
If we examine a very representative framework using React, such as Next.js, we notice that, should a Next-Action header be delivered, the action handler includes the following code. (action-handler.ts:879)
In this case, the decodeReplyFromBusboy function processes multipart/form-data type requests to return a chunk.
That is, if the above Chunk was provided in multipart/form-data form, the decodeReplyFromBusboy function parses the chunk and ends up returning the chunk below.
This object becomes a Thenable since it contains a then member and is a Function as defined in JavaScript. (MDN - Thenable)
Hence, through the initial Chunk.prototype.then, access to a properly arranged second initializeModelChunk can be achieved, leading to the execution of the console.log(1) code eventually.
The JavaScript executed here is not client-side code, but code running server-side through node.js, thus enabling the attacker to run arbitrary code on the server, such as process.mainModule.require('child_process').execSync('id > /tmp/test');.
Am I vulnerable too?
All servers operating on a React-server basis, including Next.js, are exposed to the React2Shell vulnerability. This vulnerability has a very high severity and impact, with multiple Proof of Concepts (PoCs) already published and real attack attempts confirmed, making swift inspection and response essential.
It is difficult to effectively defend against this vulnerability by merely adding WAF rules. In the process of handling Flight requests in RSC, the server converts the attacker's input using the JSON.parse function as shown below.
Due to this process, the attacker can craft an Exploit Payload using JSON syntax and manipulate it with notations like \\uXXXX to evade detection by disguising the malicious payload.
Therefore, the most critical and urgent solution is to patch the vulnerable version of RSC/Next.js for a fundamental response.
According to Next.js, this vulnerability has been patched in the following versions of Next.js.
While diagnosing the React2Shell vulnerability can be done by directly utilizing the publicly available PoC code, it can also be securely and simply checked for vulnerabilities using the emergency scanner provided by the OFFen ASM attack surface management solution by Enki Whitehat.
OFFen ASM Emergency Scanner Page

Reference
https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components
https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71912&menuNo=205020
https://github.com/facebook/react/commit/7dc903cd29dac55efb4424853fd0442fef3a8700
https://gist.github.com/HerringtonDarkholme/87f14efca45f7d38740be9f53849a89f#flight-reference-types
https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3
Popular Articles









