โก 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 devA 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
.envsecrets (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?
this.constructor.constructorโFunctionFunction(string)โ arbitrary JS executionchild_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-domVulnerable:
- 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@latestSafe versions:
- React 19.1.1+
- React 18.3.2+
3๏ธโฃ Audit Dependencies
npm audit
npm audit fix4๏ธโฃ Review Server Components
grep -r "use server" ./srcAudit for:
- User input
- Serialization
- External calls
5๏ธโฃ Harden Your Defenses
- Log
child_processusage - 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