
Dieses Dokument beschreibt meinen persönlichen Forschungsprozess zu CVE-2025-55182, einschließlich bestätigter Erkenntnisse und Experimenten, die ich durchgeführt habe. Es stellt einen ehrlichen Bericht meiner Untersuchung dar, einschließlich anfänglicher falscher Annahmen und des letztendlichen Durchbruchs.
Nach einer umfangreichen Untersuchung von CVE-2025-55182 (CVSS 10.0) war meine anfängliche Schlussfolgerung, dass eine automatische RCE nicht öffentlich demonstriert worden war und dass die Ausnutzung anwendungsspezifische Gadgets erforderte. Diese Schlussfolgerung war falsch.
Am 5. Dezember 2025 gelang es mir, nach zusätzlichen Erkenntnissen von einem weiteren unabhängigen Forscher auf X (@maple3142), vollständige nicht authentifizierte RCE auf vanilla Next.js zu reproduzieren, ohne dass anwendungsspezifische Code-Schwachstellen erforderlich waren.
Mein anfänglicher Fokus lag auf der ersten Änderung im React-19.0.1-Patch:
Verwundbar (19.0.0):
return fn.bind.apply(fn, [null].concat(_ref));
Gepatcht (19.0.1):
if (Array.isArray(promiseValue)) {
promiseValue = promiseValue.slice(0);
} else {
promiseValue = [];
}
Ich nahm an, dass der Angriffspfad über fn.bind.apply() mit bösartigen Objekten anstelle von Arrays verlief. Ich konnte die Argumentinjektion in Server Actions mithilfe von $ACTION_REF_ mit einem angreiferkontrollierten bound demonstrieren:
curl -X POST http://localhost:9000/ \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"<ACTION_ID>","bound":["; id #","/etc/passwd"]}'
Ergebnis: Die Argumente wurden erfolgreich in die Server Action injiziert. Dies führt jedoch nur dann zu RCE, wenn die Zielfunktion diese Argumente unsicher verwendet.
Ein genauerer Blick auf den Patch offenbarte eine weitere wichtige Änderung in getOutlinedModel():
Verwundbar:
for (key = 1; key < reference.length; key++)
parentObject = parentObject[reference[key]];
Gepatcht:
if (hasOwnProperty.call(value, name)) {
value = value[name];
}
Dieses verwundbare Verhalten erlaubte die Traversierung der Prototypenkette mithilfe von Referenzen wie:
$1:__proto__:constructor:constructor
Während der Forschung testete ich ein Thenable-Objekt mit einer .then-Eigenschaft:
{"then": "$1:__proto__:constructor:constructor"}
Wenn JavaScript dies über await verarbeitet:
.then-Eigenschaft und behandelt das Objekt als Promiseobj.then(resolve, reject) aufthen zu Function.constructor aufgelöst wird, versucht JavaScript, Function(resolve, reject) auszuführenBeobachtetes Ergebnis:
SyntaxError: Unexpected token 'function'
at Object.Function [as then] (<anonymous>)
Wenn Function.constructor wie folgt aufgerufen wird:
Function(resolve, reject)
// resolve.toString() = "function () { [native code] }"
// Function attempts to parse this as code → SyntaxError
Die Argumente resolve und reject sind immer die nativen Promise-Funktionen. Function versucht, das erste Argument als Quellcode zu interpretieren, was ungültiges JavaScript ist.
Hier kam meine Forschung ins Stocken. Ich schlussfolgerte, dass die Kontrolle der Argumente für Function.constructor ohne ein anwendungsspezifisches Gadget unmöglich war.
Nach der Veröffentlichung meiner ersten Erkenntnisse wies mich ein weiterer unabhängiger Forscher auf ein kritisches Element hin, das ich übersehen hatte: den $B-Deserialisierungs-Sink (Blob).
Im kompilierten React-Flight-Servercode (in den TypeScript-Quellen nicht sichtbar) existiert:
case "B":
return response._formData.get(response._prefix + id);
Fundort:
[email protected]cjs/react-server-dom-webpack-server.node.unbundled.development.js[email protected]/dist/compiled/react-server-dom-webpack/Dieser Code erlaubt React, response._formData.get() mit Werten aufzurufen, die aus angreiferkontrollierter Eingabe abgeleitet sind, ohne Validierung.
| Ansatz | Argumente für Function.constructor | Ergebnis |
|---|---|---|
| Thenable (Phase 3) | resolve, reject (native Funktionen) | ❌ SyntaxError |
| Blob + vergiftete Response | _prefix (angreiferkontrollierter String) | ✅ RCE |
Durch die Kombination von:
$1:__proto__:then → Chunk.prototype.then)_response-Objekt mit:
_formData.get gesetzt auf Function.constructor_prefix gesetzt auf beliebigen JavaScript-Code$B-ReferenzDer case "B":-Handler führt Folgendes aus:
Function.constructor("<attacker code>" + id)
Dies umgeht die Grenze der Argumentbindung vollständig.
Beliebte GitHub-PoCs, die vorgeben, RCE zu ermöglichen, verwenden Action-IDs wie:
"child_process#execSync""vm#runInThisContext"Diese sind gefälscht. Next.js akzeptiert nur von der Anwendung definierte Action-IDs. Ungültige IDs erzeugen:
TypeError: Cannot read properties of undefined (reading 'workers')
Der echte Exploit erfordert jedoch keine gefälschten Action-IDs. Jede gültige Server-Action-ID funktioniert.
Ich testete diese Ausnutzungskette auf einer minimalen Next.js 15.0.3 + React 19.0.0-Anwendung mit nur:
async function myAction(data) {
"use server";
console.log("Server Action called with:", data);
return { success: true, received: data };
}
Ergebnis: Vollständige RCE bestätigt. Die Anwendung enthielt keinen unsicheren Code, kein eval, kein execSync, keine Gadgets.
Stand 5. Dezember 2025 existiert der $B-Sink weiterhin in Next.js 15.0.5 (der angeblich gepatchten Version).
Verifizierung:
$ 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
Ergebnis:
case "B":
return response._formData.get(response._prefix + obj);
Der Code ist identisch mit der verwundbaren Version.
Aufgrund der Entdeckung, dass die Schwachstelle in den behaupteten „behobenen" Versionen möglicherweise nicht vollständig gepatcht ist, halte ich das vollständige Proof-of-Concept-Payload zurück, bis die Verifizierung mit den Sicherheitsteams von Vercel und Meta abgeschlossen ist.
Die in diesem Dokument bereitgestellten technischen Details reichen aus, um den Schwachstellenmechanismus zu verstehen, sind jedoch absichtlich unvollständig, um eine sofortige Ausnutzung zu verhindern.
| Technik | Status |
|---|---|
Argumentinjektion über bound | ✅ Bestätigt (begrenzte Auswirkungen) |
| Prototypen-Traversierung |
| Problem | Auswirkung |
|---|---|
Der $B-Deserialisierungs-Sink (Blob) | ❌ Kritisch - ermöglicht Argumentkontrolle |
| Untersuchung von kompiliertem vs. Quellcode | ❌ Der Sink existiert nur im kompilierten Output |
| Mechanismus zur Vergiftung des Response-Objekts | ❌ Ermöglicht die Umgehung aller Schutzmaßnahmen |
CVE-2025-55182 ist für vollständige nicht authentifizierte RCE auf vanilla Next.js-Anwendungen ausnutzbar.
$B-Deserialisierung von einem unabhängigen Forscher erhalten$B-Deserialisierung weiterhin beliebige _response-Objekte akzeptiertNext-Action-Header$@, __proto__, $B$B-Deserialisierung wurde von einem Forscher auf X (@maple3142) bereitgestelltZuletzt aktualisiert: 5. Dezember 2025
| Aspekt | Ergebnis |
|---|
| Authentifizierung erforderlich? | ❌ Nein |
| Anwendungs-Gadget erforderlich? | ❌ Nein |
| Funktioniert auf vanilla Next.js? | ✅ Ja |
| Anzahl erforderlicher Anfragen | 1 POST |
| Betroffene Versionen | Next.js ≤15.0.4 + React 19.0.0 |
| CVSS-Score | 10.0 (gerechtfertigt) |
| ✅ Bestätigt |
Zugriff auf Function.constructor über Thenable | ✅ Bestätigt (aber allein nicht ausnutzbar) |
| Erkennung verwundbarer Versionen | ✅ Bestätigt |