โšก The Moment React Got a CVSS 10.0

On a seemingly normal day in the React ecosystem, a critical vulnerability emerged that sent shockwaves through the development community. CVE-2025โ€“5182 earned the rare and dreaded CVSS score of 10.0 โ€” the highest possible severity rating.

This wasn't just another XSS bug or a minor security oversight.

This was unauthenticated Remote Code Execution (RCE) in React Server Components, and it meant attackers could run arbitrary commands on your server without even logging in.

In this article, we'll:

  • Walk through a real exploitation demo
  • Break down the root technical flaw (Duck Typing)
  • Examine the controversial fix
  • Understand what this means for production React apps

๐ŸŽฏ Setting the Stage: A Live Exploitation

To truly understand the severity of CVE-2025โ€“5182, let's walk through a real demonstration of how this vulnerability was exploited.

๐Ÿ”ง The Setup

npx create-next-app@16.0.6 my-vulnerable-app
cd my-vulnerable-app
npm run dev

A fresh Next.js application with React Server Components was created using version 16.0.6 โ€” a version vulnerable to CVE-2025โ€“5182.

The app started on localhost:3000, appearing completely normal.

๐Ÿงจ The Attack Begins

The attacker opened the browser's developer console and executed:

// Step 1: Testing basic server-side execution
sendMaliciousPayload("console.log('meow meow')")

Result: "meow meow" appeared in the server terminal, not the browser console.

โžก๏ธ Arbitrary JavaScript was executing on the server.

๐Ÿš€ Escalating the Attack

// Step 2: Listing server files
sendMaliciousPayload("ls -la")

Result: The server's filesystem was exposed, revealing:

  • Application source files
  • Configuration files
  • Potential sensitive locations
// Step 3: Creating a file on the server
sendMaliciousPayload("touch I_was_here")

Result: A file named I_was_here appeared in the server directory.

โœ”๏ธ Read access โœ”๏ธ Write access โœ”๏ธ Command execution

๐Ÿ’ฅ The Implications

In seconds, an attacker could:

  • โœ… Execute arbitrary JavaScript on the server
  • โœ… Run OS-level commands
  • โœ… Read and write files
  • โœ… Steal .env secrets (DB creds, API keys)
  • โœ… Pivot to internal systems
  • โœ… Install persistent backdoors

No authentication. No permissions. No warnings.

๐Ÿฆ† The Technical Culprit: Duck Typing

At the heart of CVE-2025โ€“5182 lies a fundamental JavaScript concept: Duck Typing.

โ“ What Is Duck Typing?

"If it looks like a duck, swims like a duck, and quacks like a duck โ€” it's probably a duck."

In JavaScript, an object's "type" is determined by what it can do, not what it actually is.

๐Ÿงช Duck Typing in Action

// A real Promise
const realPromise = new Promise((resolve) => {
  resolve("I'm a real Promise!");
});

// A fake "Promise" (Thenable)
const fakePromise = {
  then: function(resolve) {
    resolve("I'm just pretending to be a Promise!");
  }
};
// Duck Typing check (VULNERABLE)
function isPromiseLike(obj) {
  return obj && typeof obj.then === 'function';
}
console.log(isPromiseLike(realPromise)); // true
console.log(isPromiseLike(fakePromise)); // true (PROBLEM!)

Both objects pass the check โ€” because both quack.

JavaScript doesn't care which one is real.

๐ŸŽญ How React Got Tricked

React Server Components rely on the Flight Protocol to serialize and deserialize data between server and client.

Promises are part of that protocol.

โš ๏ธ The Vulnerable Pattern

// VULNERABLE PATTERN (Simplified)
function deserialize(data) {
  if (data && typeof data.then === 'function') {
    return await data;
  }
}

If it had a .then(), React trusted it.

That was the mistake.

๐Ÿง  The Malicious Payload

const maliciousThenable = {
  then: function(resolve, reject) {
    const executeCommand = this.constructor.constructor;
    executeCommand(
      'require("child_process").exec("touch I_was_here")'
    )();
    resolve();
  }
};

What's happening here?

  1. this.constructor.constructor โ†’ Function
  2. Function(string) โ†’ arbitrary JS execution
  3. child_process.exec() โ†’ OS command execution

React called .then() โ€” and the server was compromised.

๐Ÿ”ง The Fix: Killing Duck Typing

โŒ Before (Vulnerable)

if (obj && typeof obj.then === 'function') {
  await obj;
}

โœ… After (Secure)

if (obj instanceof Promise) {
  await obj;
}

// OR

if (
  obj &&
  typeof obj.then === 'function' &&
  Object.prototype.hasOwnProperty.call(obj, 'then')
) {
  await obj;
}

๐Ÿ“ฆ The 712-Line Patch Controversy

The fix wasn't small.

  • 712 lines
  • Mixed refactors
  • Security + unrelated changes
  • Hard to audit
  • Hard to backport

Many developers argued:

Security patches should be minimal and isolated.

โ˜ฃ๏ธ The Triple Threat

  • CVE-2025โ€“5182 โ€” RCE (CVSS 10.0)
  • Denial of Service โ€” server crashes
  • CVE-2025โ€“55187 โ€” source code exposure

Same root cause: unsafe deserialization.

๐Ÿš‘ Immediate Action Items

1๏ธโƒฃ Check Your Version

npm list react react-dom

Vulnerable:

  • React 19.0.0โ€“19.1.0
  • React 18.2.0โ€“18.3.1 (Server Components)
  • React 16/17 experimental builds

2๏ธโƒฃ Update Immediately

npm install react@latest react-dom@latest

Safe versions:

  • React 19.1.1+
  • React 18.3.2+

3๏ธโƒฃ Audit Dependencies

npm audit
npm audit fix

4๏ธโƒฃ Review Server Components

grep -r "use server" ./src

Audit for:

  • User input
  • Serialization
  • External calls

5๏ธโƒฃ Harden Your Defenses

  • Log child_process usage
  • Rate-limit requests
  • Validate all inputs
  • Deploy WAF rules

๐Ÿง  Lessons Learned

Duck Typing Isn't Free

// โŒ Dangerous
if (obj && typeof obj.then === 'function')

// โœ… Safer
if (obj instanceof Promise)

Serialization Is Dangerous

Never deserialize untrusted data without strict validation.

Security Patches Must Be Clean

Small. Auditable. Backportable.

๐Ÿšจ Final Thoughts

CVE-2025โ€“5182 is a wake-up call.

React crossed into server territory โ€” and security mistakes became catastrophic.

If you use Server Components and haven't patched yet, your server is vulnerable.

๐Ÿ‘‰ Patch now

๐Ÿ”— Resources