
This document describes my personal research process into CVE-2025-55182, including confirmed findings and experiments I carried out. This represents an honest account of my investigation, including initial incorrect assumptions and the eventual breakthrough.
After an extensive investigation into CVE-2025-55182 (CVSS 10.0), my initial conclusion was that automatic RCE had not been publicly demonstrated and that exploitation required application-specific gadgets. This conclusion was incorrect.
On December 5, 2025, after receiving additional insights from another independent researcher on X (@maple3142), I successfully reproduced full unauthenticated RCE on vanilla Next.js without requiring any application-specific code vulnerabilities.
My initial focus was on the first change in the React 19.0.1 patch:
Vulnerable (19.0.0):
return fn.bind.apply(fn, [null].concat(_ref));
Patched (19.0.1):
if (Array.isArray(promiseValue)) {
promiseValue = promiseValue.slice(0);
} else {
promiseValue = [];
}
I assumed the attack path was through fn.bind.apply() with malicious objects instead of arrays. I was able to demonstrate argument injection into Server Actions using $ACTION_REF_ with attacker-controlled bound:
curl -X POST http://localhost:9000/ \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"<ACTION_ID>","bound":["; id #","/etc/passwd"]}'
Result: The arguments were successfully injected into the Server Action. However, this only leads to RCE if the target function uses those arguments unsafely.
A deeper look at the patch revealed another important change in getOutlinedModel():
Vulnerable:
for (key = 1; key < reference.length; key++)
parentObject = parentObject[reference[key]];
Patched:
if (hasOwnProperty.call(value, name)) {
value = value[name];
}
This vulnerable behavior allowed prototype-chain traversal using references like:
$1:__proto__:constructor:constructor
During the research, I tested a thenable object containing a .then property:
{"then": "$1:__proto__:constructor:constructor"}
When JavaScript processes this through await:
.then property and treats the object as a Promiseobj.then(resolve, reject)then resolves to Function.constructor, JavaScript attempts to execute Function(resolve, reject)Observed result:
SyntaxError: Unexpected token 'function'
at Object.Function [as then] (<anonymous>)
When Function.constructor is invoked as:
Function(resolve, reject)
// resolve.toString() = "function () { [native code] }"
// Function attempts to parse this as code → SyntaxError
The arguments resolve and reject are always the native Promise functions. Function attempts to interpret the first argument as source code, which is invalid JavaScript.
This is where my research stalled. I concluded that controlling arguments to Function.constructor was impossible without an application-specific gadget.
After publishing my initial findings, another independent researcher pointed me to a critical piece I had missed: the $B (Blob) deserialization sink.
In the compiled React Flight server code (not visible in TypeScript sources), there exists:
case "B":
return response._formData.get(response._prefix + id);
Location:
[email protected]cjs/react-server-dom-webpack-server.node.unbundled.development.js[email protected]/dist/compiled/react-server-dom-webpack/This code allows React to call response._formData.get() with values derived from attacker-controlled input, without validation.
| Approach | Arguments to Function.constructor | Result |
|---|---|---|
| Thenable (Phase 3) | resolve, reject (native functions) | ❌ SyntaxError |
| Blob + Poisoned Response | _prefix (attacker-controlled string) | ✅ RCE |
By combining:
$1:__proto__:then → Chunk.prototype.then)_response object with:
_formData.get set to Function.constructor_prefix set to arbitrary JavaScript code$B referenceThe case "B": handler executes:
Function.constructor("<attacker code>" + id)
This bypasses the argument binding limitation entirely.
Popular GitHub PoCs claiming RCE use Action IDs such as:
"child_process#execSync""vm#runInThisContext"These are fake. Next.js only accepts Action IDs defined by the application. Invalid IDs produce:
TypeError: Cannot read properties of undefined (reading 'workers')
However, the real exploit does not require fake Action IDs. Any valid Server Action ID works.
I tested this exploitation chain on a minimal Next.js 15.0.3 + React 19.0.0 application with only:
async function myAction(data) {
"use server";
console.log("Server Action called with:", data);
return { success: true, received: data };
}
Result: Full RCE confirmed. The application contained no unsafe code, no eval, no execSync, no gadgets.
As of December 5, 2025, the $B sink still exists in Next.js 15.0.5 (the supposedly patched version).
Verification:
$ npm pack [email protected]
$ tar -xzf next-15.0.5.tgz
$ grep -A5 'case "B":' package/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js
Result:
case "B":
return response._formData.get(response._prefix + obj);
The code is identical to the vulnerable version.
Due to the discovery that the vulnerability may not be fully patched in the claimed "fixed" versions, I am withholding the complete proof-of-concept payload pending verification with the security teams at Vercel and Meta.
The technical details provided in this document are sufficient to understand the vulnerability mechanism but intentionally incomplete to prevent immediate exploitation.
| Technique | Status |
|---|---|
Argument injection via bound | ✅ Confirmed (limited impact) |
| Prototype traversal |
| Issue | Impact |
|---|---|
The $B (Blob) deserialization sink | ❌ Critical - enables argument control |
| Examining compiled vs. source code | ❌ The sink only exists in compiled output |
| Response object poisoning mechanism | ❌ Allows bypassing all protections |
CVE-2025-55182 is exploitable for full unauthenticated RCE on vanilla Next.js applications.
$B deserialization from independent researcher$B deserialization still accepts arbitrary _response objectsNext-Action headers$@, __proto__, $B patterns$B deserialization was provided by an researcher on X (@maple3142)Last Updated: December 5, 2025
| Aspect | Finding |
|---|
| Authentication required? | ❌ No |
| Application gadget required? | ❌ No |
| Works on vanilla Next.js? | ✅ Yes |
| Number of requests needed | 1 POST |
| Affected versions | Next.js ≤15.0.4 + React 19.0.0 |
| CVSS Score | 10.0 (justified) |
| ✅ Confirmed |
Access to Function.constructor via thenable | ✅ Confirmed (but not exploitable alone) |
| Detection of vulnerable versions | ✅ Confirmed |