Back to Blog
Security

React2Shell: How a Tiny Assumption Turned Into a 10/10 Server-Side Vulnerability

This week, the web dev and security communities got hit with one of the most serious vulnerabilities we've seen in modern React infrastructure: React2Shell — officially tracked as CVE-2025-55182 with a 10.0 severity score.

December 8, 2025
4 min read
By Balaji Sivasakthi

This week, the web development and security communities were hit with one of the most serious vulnerabilities we've seen in modern React infrastructure: React2Shell — officially tracked as CVE-2025-55182 with a 10.0 severity score.

This wasn't just another edge-case bug. This was full remote code execution on the server, triggered by a carefully crafted request body. No authentication bypass. No misconfigured headers. Just a deep, clever abuse of how JavaScript and React Server Components work under the hood.

And that's what makes it both terrifying and fascinating.


The Setup: JavaScript, React, and Flight

To understand why this exploit was even possible, you need three pieces of context:

1. JavaScript Is Extremely Dynamic

JavaScript doesn't just allow dynamic data — it allows dynamic execution.

Functions, constructors, prototypes, Promises… they're all objects that can be inspected, passed around, and abused if you're not careful.

That flexibility is powerful.

It's also dangerous.

2. React Server Components Use the "Flight" Protocol

React Server Components (RSC) move UI work to the server and stream results to the client. To make this possible, React uses a custom serialization system called Flight.

Flight doesn't just transfer JSON. It can stream:

  • Promises

  • Deferred data

  • References between chunks

  • Internal state markers

That's how React can render partial UIs instantly while the backend is still working.

Again: powerful — but that power becomes an attack surface.

3. Promises Have a Hidden Superpower

In JavaScript, anything with a .then method can pretend to be a Promise.

This is called a thenable.

React relies on this behavior heavily for async rendering.

Attackers rely on it too.


What Went Wrong (Without Getting Exploit-Heavy)

At a high level, the vulnerability came from one missing safety check inside React's Flight deserialization logic. Specifically:

  • React trusted incoming structured data too much

  • It failed to consistently validate property ownership

  • And it allowed attackers to reference internal runtime properties that should never be reachable from user input

By carefully shaping the serialized payload:

  • An attacker could trick React into resolving fake Promises

  • Hijack internal resolution behavior

  • Reach JavaScript's Function constructor indirectly

  • And execute arbitrary server-side code

Not "crash the app" bad.

"Run anything on your server" bad.


Why This Exploit Was So Dangerous

This vulnerability was especially nasty because:

  • ✅ The payload looked like normal Flight traffic

  • ✅ It abused legitimate React internal mechanisms

  • ✅ It didn't rely on obvious red flags like eval

  • ✅ It weaponized Promises and prototype access

  • ✅ It triggered server-side execution, not just browser issues

In other words, it blended in perfectly with normal React Server Component behavior while quietly breaking down the walls between "data" and "code."

That's the worst class of vulnerability you can have.


The Real Root Cause

If you strip away all the clever trickery, the lesson is brutally simple:

User input was allowed to interact with JavaScript runtime internals.

Specifically:

  • Prototype access paths

  • Constructor references

  • Thenable resolution logic

  • Internal React state markers

All because key ownership checks were missing in the right place.

This is the JavaScript version of an out-of-bounds memory bug in C.

Different language. Same risk.


What Was Done to Fix It

The response across the ecosystem moved fast:

  • ✅ React shipped a patch to properly lock down property access

  • ✅ Next.js updates followed immediately

  • ✅ Platform-level firewalls and mitigations rolled out

  • ✅ Industry security teams coordinated on detection patterns

Defense-in-depth was crucial here. Framework fixes, platform safeguards, and runtime monitoring all worked together.


What This Means for Dev Teams

If you're running:

  • Next.js

  • React Server Components

  • Or anything using React Flight

Upgrading is not optional.

This is not a "we'll get to it next sprint" issue.

Beyond patching, the bigger engineering lessons are:

  • Don't blindly trust serialized client input

  • Prototype access is still a critical security risk in JS

  • The combination of async operations, streaming, and deserialization massively expands your attack surface

  • Framework abstractions don't eliminate fundamental language risks


Massive Credit Where It's Due

Huge respect goes to Lachlan Davidson for the incredible amount of research that went into discovering and responsibly disclosing this. This kind of vulnerability doesn't emerge from fuzzing — it takes deep, painstaking understanding of how systems really behave.

Also, a shoutout to the broader security community that moved quickly to validate, patch, and harden the ecosystem.


Final Thought

React2Shell wasn't caused by sloppy coding.

It happened because:

  • The system was complex

  • The abstractions were powerful

  • And one small trust assumption slipped through

That's usually how the worst security failures happen.

ReactSecurityVulnerabilityCVEServer-SideNext.js