React Server Components RCE Proof Of Concept: Next.js & React vulnerability will break the internet, Who It Hits, And How To Patch Fast

An unauthenticated remote code execution bug in React Server Components lets an attacker send a single crafted request and run system commands on your server. It hits React 19 series stacks hard, including Next.js App Router setups, and it was scored CVSS 10.0 for a reason. The fix is available, so the practical move is to upgrade your React and Next.js versions right now and lock down public instances. With exposure numbers this big and working proof of concept code in the wild, waiting is how you get popped.
What Is The React Server Components RCE Exploit?
Ladies and gentlemen, boys and girls, this is it. The proof of concept is real, the exploit works, and seeing it land on a default install turned my stomach a little. The demo looks harmless at first glance, until you watch a calculator pop up from a single HTTP request.
Here is what I did, step by step. I spun up a fresh Next app with npm, pointing it at a setup that mirrors what people were calling the React 2 shell vulnerability. I pinned an older combo to keep the demo aligned with what was exploitable before the patch dropped, so I used a 16.0.6 base in my terminal. The install flagged one critical vulnerability during npm install, the kind that makes you take a deep breath even if you plan to throw the environment away afterward.
I booted the app and let it listen on http://localhost:3000. In a second terminal, I cd'ed into a folder labeled for the CVE, because that is where I staged the proof of concept script. Picture a small terminal window tucked in the corner, the code minimized just enough that you can see the command as it runs. Then I executed the PoC, a tiny Python file that sends a crafted request, and almost instantly the system calculator launches. That is the classic proof of remote code execution: run the calc app and let the window speak for itself.
In case you missed it while trying to keep up with the flood of tweets and repos, this was not a drill. A critical security hole in React Server Components let attackers hit servers without logging in. It does not show up every year on your bingo card, so yeah, a lot of folks were running around like a chicken with their head cut off. The shorthand that people latched onto is React2Shell, and the punchline is simple: send a special payload to the server, then run whatever command you want.
There was confusion at first. A GitHub repository made rounds that looked like the exploit, but it wasn't actually working code. It was like the Internet collectively held its breath waiting for the real deal. Then the full PoC landed, and it was clean, small, and completely devastating. The kind of thing that makes researchers smile and defenders groan at the same time.
Why Does This Vulnerability Work? React Flight, Chunks, And Prototype Tricks
I am not a front end person. I do not live in React or Next.js land day to day. So I am grabbing onto the coattails of the folks who dug into the guts of this, and I am trying to translate the parts that matter without pretending I am the authority. That said, the chain makes sense once you slow it down.
React Server Components offers server functions that behave a lot like RPC over HTTP. Think of it as a way to fetch data from other instances for low latency or to do authenticated requests the browser does not have credentials to do. The wire format for all of that is React Flight. It splits data into chunks, serializes each piece, and streams it down to the client or to other parts of the framework that put everything back together.
The format is quirky when you look at it raw. You will see entries like index 0 containing a string wrapped in something that looks like a reference, written as $1 inside square brackets, and that tells the deserializer to expect a structure later that fills in the details. Then a second chunk with id 1 lands and says, cool, this is a fruit, with a property called name, and that name is cherry. Put them together and the runtime reconstructs a plain object with the expected shape. That is how the protocol stays efficient, because it references things by id and resolves them as chunks arrive.
The bug lives in how React handles those references and how it verifies what keys are allowed when it is building objects from chunks. If you can control the keys that get applied during deserialization, you can try to step outside normal properties and touch the guts of JavaScript itself. People reached for the special stuff we have all seen in CTF writeups, like __proto__ and constructor. Touch the prototype, then slide over to the constructor, and if you are lucky you can grab the Function constructor or something that gives you a way to evaluate arbitrary code.
That was the theme in the early chatter. You cannot just call a function directly in this pipeline because the framework wants to hand you a safe value, not an executable. So the trick is to retrieve a function in a form that is not immediately executed, then trigger it later in the flow. The async and await behavior inside these server actions created just enough staging that you could queue up something nasty and then run it after a normal operation. The first time I saw that described, it felt like a magician palming a card while smiling right at the audience.
There were roadblocks though. One early snag people mentioned is slice. When the system deserializes a value and wraps or processes it, certain helper methods get in the way of a clean call path. Maple 3142 shared a smart pivot. They realized getChunk could be pointed at id 0 in a way that resolves to a fake chunk that has not been fully processed. That raw value bypasses the protection layers and lets you stitch together a fake object shaped exactly the way you want.
Once you can craft that fake object with just the right properties, you get to do the classic require chain. process.mainModule.require gives you child_process, and child_process.execSync is the silver bullet if you can land it. The PoC uses that to run calc in a tiny self contained demo, but swap the command for a curl to your box and you have a reverse shell. That is why researchers laugh at the calc pop, because it is the harmless version of something very real.
When And Where Does It Trigger In Real Apps? Next.js App Router And Server Actions
Let us talk scope, because the blast radius matters. The versions affected on the React side sit in the React 19 line, specifically 19.0, 19.1.0, and 19.1.1. The way this shows up in production is usually through frameworks that adopt React Server Components, and Next.js App Router is the big one that bubbled up in every thread.
People kept pointing at two identifiers. One advisory was tied to 55182 in the upstream React implementation. The other, 66478, focused on Next.js and how App Router used the feature. The short version is this: the upstream flaw makes the exploit possible, and the Next.js advisory tells you where you will actually see it trigger on live sites. If React is the match, App Router in Next.js was a dry stack of kindling sitting under your front porch.
What surprised me is how early the dangerous bit happens in the request lifecycle. The deserialization that sets the trap occurs before the rest of the request gets validated deeper down. People noted you could trigger it just by getting React Server Actions to do their normal thing, and even setting a header was enough to wake up the path that does the chunk handling. That means you do not need an authenticated session or some exotic state to line everything up. You only need to be reachable.
So if you are picturing a typical Next.js install, imagine a flat, default, vanilla configuration. The kind you get from running npx create-next-app with the App Router. No special middleware, no custom fancy auth, just the default. Those were vulnerable before the fixes shipped, which is why this felt like a firecracker tossed into a crowded room. Defenders love talking about hardening, but you cannot harden your way out of deserialization that triggers before your middleware runs.
Plug this into a simple mental model. If your app exposes any RSC driven endpoints over the public Internet, and you have not upgraded, someone can send that crafted request. The server loads the chunk, pokes the prototype path, resolves a constructor, and you are now executing a string on your host. That is why this got slapped with a CVSS 10.0. You cannot click your way out of this with a permission dialog or a rate limit. You must fix the code paths.
Who Is Affected And How Big Is The Blast Radius?
I am not trying to be alarmist here. The numbers are just loud on their own. The folks at Wiz looked across their cloud telemetry and said almost 40 percent of cloud environments contain instances of Next.js or React that are vulnerable. If you narrow it to Next.js, they said the framework shows up in nearly 70 percent of environments, and just over 60 percent of those are public facing.
Do the math and the picture gets ugly. That means roughly 44 percent of all cloud environments have a publicly exposed Next.js instance. You do not need to be a threat intel analyst to know how that plays out after a working PoC drops. You will have opportunistic scans, then spray and pray payloads, then specific targeting against high value hosts. The cadence is predictable because the playbook is older than most of the frameworks we use.
Let me call out the researchers and people who did the hard work. Credit where it is due, Lachlan Davidson reported this to Meta. That is not small. Moritz Sanft shared the full RCE proof of concept that actually runs, along with a clean explanation. Maple 3142 added the key idea around getChunk and the fake raw value, which acted like the missing puzzle piece. I saw Ed get a shout out during the chaos, and I am keeping that in because those little nods are part of how the community works.
Then there is the platform chatter. I saw Vercel people talk about their WAF rules and how the big G crew pushed out filters to blunt some variants. That is helpful, and it will block some payloads fast. But do not let a WAF lull you to sleep. If you run a public Next.js App Router stack or anything that uses React Server Components in the affected range, the fix is to patch. If the shield catches some arrows, great. You still need to stop leaving the door open.
How The Proof Of Concept Works: Terminal, Burp Suite, And The Calc Pop
Let me walk you through the demo slowly so you can picture it without a video. The left side of the screen is a terminal with a brand new Next.js app. npm install runs, throws a single line about one critical severity vulnerability, and you get that uneasy little knot in your stomach. The app starts listening on localhost 3000, just a clean boilerplate app. No custom code, no tricks.
On the right side, a second terminal is focused on a folder literally named for the CVE. Inside is a tiny Python script called something like poc.py. The font is small. The window is trimmed down to make room for a browser that is not the star of the show. You run the script and it fires off a POST with a crafted body that looks like a React Flight stream if you squint at it.
The crafted payload is doing several things in a tiny space. It defines chunk ids that hint at legitimate shapes, then at the right moment it injects keys engineered to touch __proto__ and constructor. That chain lines up a handle to a function constructor. The payload then arranges for the function to be executed using async steps that look like a normal wait for data. The result is a call to process.mainModule.require to fetch child_process, then execSync to run the command.
Because we are trying not to be villains on the Internet, the demo command is calc. If you are on macOS that is open -a Calculator, and on Windows it is calc, and on Linux you might pop xcalc. That visual kick is important for the human brain. A calculator window suddenly appears, and you do not need debug logs to feel the punch. The machine just did what the payload told it to do, starting from an unauthenticated HTTP request.
There is also a Burp Suite angle. Maple 3142 showed a tiny gist with the exact request that triggers this, tested right in Burp. They had OBS recording, Burp in focus, a target set to the local Next app, and a single click on Send. The body showed the prototype crafted chunk inline. Seeing it in Burp matters because it shows how little infrastructure the attacker needs. A browser is not required. A CLI is not required. Any tool that can send an HTTP request can deliver this.
If you want to extend that to a real attack, swap calc for a one liner that curls a payload from a hosted script and pipes it to bash or sh. Or run node to fetch and eval something from a CDN. Or use whatever your red team tool belt prefers for a reverse shell. The point is you change one string, and the behavior goes from a harmless pop to a persistent foothold. That is why POCs that feel like CTF puzzles still matter. The same toys become weapons in one edit.
How To Stop It Fast: Patching, Upgrading, And Practical Defenses
I am going to keep this part blunt because people sometimes want a checklist more than a think piece. The fix exists, and the vendors have shipped it. React patched the upstream deserialization bug. Next.js patched App Router usage with a guard that refuses to touch prototype shaped keys. The patch is not glamorous. It looks like a small if statement that says hey, do not allow __proto__ or prototype style keys during chunk handling. Simple, and very effective.
So what should you do. Upgrade React to the fixed 19 series patch or newer. Upgrade Next.js to the versions that include the 66478 advisory remediations if you use App Router. Redeploy every public instance that uses React Server Components. If you run on Vercel, redeploy to pull the new server code. If you self host, pin the patched versions in package.json and verify the lockfile actually resolves to them.
If you need to buy time, put your WAF in front of any public RSC endpoints. Vercel and the big G crowd already pushed rules to catch the common exploit patterns, and those do help. They will not save you from variants forever, but they can cut down the noise while you roll out upgrades. On top of that, add a temporary block list for requests that contain obviously malicious chunk keys that point at __proto__ or constructor. It is crude, but for a weekend of breathing room, crude is fine.
Verification matters. After you upgrade, reproduce the PoC against a staging environment and confirm it no longer pops calc. Check your logs for 4xx where the WAF or app rejected prototype keys. Scan your code for custom deserialization helpers that might be affected by the same class of bug. If you wrapped React Flight for something bespoke, treat that as suspect until proved otherwise.
Finally, keep your eyes on the street. Expect a flood of advisories, scanners, honey pots, and new PoCs. Watch for a Shodan census that lights up Next.js fingerprints. Track a few GitHub repos from the researchers I mentioned, because they will refine their writeups and tools. And patch your potatoes. Seriously, patch the poop out of it and move on with your life.
The Timeline, The People, And The Missteps The Internet Made
Let me put the story in order, because the back and forth confused a lot of folks. Early on, an advisory went live that said React Server Components had a critical bug with a CVSS score of 10.0. That is the ceiling. It is the kind of number you see and immediately ask your team to stop what they are doing. It named the React 19 line, including 19.0, 19.1.0, and 19.1.1.
Then a GitHub repository appeared that looked like the exploit. People retweeted it, blog posts referenced it, and defenders tried to reproduce. Except it was not actually the working exploit. It was a near miss that made a lot of noise. For a few hours, everyone was scratching their head asking if this was all theater or if the real thing was still hiding somewhere.
After that pause, Moritz Sanft posted an explanation and a full RCE PoC that did work. It showed the exact shaped request and walked through the deserialization path. Right around then, Maple 3142 chimed in with the clever getChunk idea that turned a theory into a reliable path. Together, those posts did what good research does. They showed the world exactly where the skeleton was hiding in the framework closet, and they did it with precise, minimal payloads that leave no room for doubt.
Behind the scenes, Lachlan Davidson had reported the bug to Meta. That detail matters because disclosure only works when someone does the handoff to the vendor and gives them a chance to fix it. The vendors shipped patches. Next.js tied the problem to 66478 for their App Router advisory. The upstream React entry aligned with 55182. The numbers do not mean much to non security folks, but they help teams anchor their tickets and patch pipelines.
Meanwhile, the platforms did what platforms do. Vercel talked about WAF coverage. The big G folks said their gizmos would block a variant. That buys time for people who cannot patch in an hour. It does not replace patching. The reality is you cannot WAF your way out of a serialization bug that happens before your code even runs. You can only fix it or remove the feature that exposes it.
What You Will See Next: Scans, Reverse Shells, And Lots Of Noise
Expect the Internet to do what it always does when a clean RCE lands. There will be scanners that look for Next.js fingerprints. You will see scripts that pull the PoC and swap calc for a one liner that curls a payload and pipes it to a shell. People will test on Linux first because it is easy to script. Then Windows, because there are plenty of Windows hosts under Node. Then macOS for the stragglers who expose local dev boxes by accident.
I already spotted a Python variant floating around that goes straight for a full reverse shell on Linux. That is about 10 lines different from the calc version. The gist from Maple 3142 shows exactly how little you need to change. Where it says xcalc in the friendly demo, you drop in bash -c and point at your server. It is not rocket science, and that is what makes it dangerous.
On defense, I expect a flood of resources. Blue team cheat sheets with curl commands you can copy and paste. Honeypots wired to alert on prototype keys inside React Flight shaped requests. Shodan snapshots that log exposed Next.js App Router banners. Vendor scans that light up your Jira with tickets tagged 66478 or 55182. It is going to feel like log4shell vibes in your feed, so apologies if that triggers some old trauma. Different bug, similar energy.
Do not let the noise drown out the fix. Upgrade, validate that the PoC no longer works, and keep an eye on your logs for a week while the traffic spikes. If you run a bug bounty program, consider pausing reports specific to this CVE for a few days so your triage team does not get buried in duplicates. And if you run a public status page, put a line item that says patching React Server Components RCE so your customers do not open tickets asking why you are redeploying on a holiday weekend.
Visualizing The Exploit Path Without The Video
Imagine the left monitor filled with a black terminal on a dark theme. You type npx create-next-app and watch the spinner churn. Warnings flicker by, then a single red line calls out a critical vulnerability. You run npm run dev and the server announces it is listening at localhost 3000. The screen is quiet. No errors, no drama.
Now look to the right. A tiny terminal window sits over a browser tab showing the Next.js welcome page. The terminal title reads CVE with a number you do not recognize yet. The cursor blinks after python3 poc.py and you hit Enter. For a split second nothing moves. Then a calculator icon bounces into view and lands center screen like a showy guest arriving late to the party.
Open Burp Suite and line up a Repeater tab. The Request line is POST, the Path is something your app exposes for server actions during RSC flows. The Body is not JSON you recognize. It is a sequence that looks like a streaming protocol, with bracketed references and ids that increment. Somewhere in that flow, a key appears that looks like a stranger crashed a family reunion. It reads like __proto__ and points at a constructor shaped path. That is the moment you feel the danger even if you cannot parse the rest by eye.
Flip back to the terminal that started the server. You expect a stack trace or at least a warning. You get nothing. The server thinks it did a normal thing. That is part of what makes this class of bug so nasty. It does not throw. It just complies.
FAQ: Quick Answers You Can Share With Your Team
Is this authenticated?
No. The exploit triggers pre auth in the RSC pipeline. That is why the CVSS score is 10.0 and why this got so much attention.
Which versions are affected?
React 19.0, 19.1.0, and 19.1.1 were called out. Next.js App Router usage tied to advisory 66478 was vulnerable before the fix. The upstream React issue lines up with 55182.
Does a WAF help?
Yes, as a speed bump. Vercel and the big G folks pushed rules that will stop common payload shapes. But a WAF is not a fix. You still need to upgrade.
How do I validate the patch?
Reproduce the PoC on a staging environment. If you were able to pop calc before, it should no longer execute after the upgrade. Also check logs for blocked prototype keys to confirm the guard is active.
Can this be used for reverse shells?
Yes. Swap the calc command for a reverse shell one liner in the PoC. I will not paste payloads here, but you know the drill. That is why this matters.
Conclusion: Patch Your Potatoes And Move
I am not here to posture like a React guru. I am the person who sat with popcorn waiting for the PoC to drop and then watched calc pop from a default Next app. The chain is clean, the exploit is tiny, and the exposure is huge. That combination does not show up every week, which is why people are buzzing.
So here is what to do. Upgrade React and Next.js across anything that uses React Server Components. Redeploy and verify the PoC no longer works. Use a WAF as a seat belt, not as a parachute. Keep an eye on the flood of scanners and take a breath once the curve peaks.
And give credit where it is due. Kudos to Lachlan Davidson for the report, to Moritz Sanft for the working RCE writeup, and to Maple 3142 for the getChunk trick that made the chain sing. If you are on the blue team side, watch for cheat sheets and scanners, maybe toss a honeypot on the edge to measure the noise, and keep one eye on Shodan. Then get back to building cool things, because nobody wants React2Shell to be the memory you take into Thanksgiving 2025.