1. Descripción técnica del ataque
React2Shell es el nombre de CVE-2025-55182, una vulnerabilidad de ejecución remota de código (RCE) en el procesamiento de solicitudes de React Server Components (RSC). Fue divulgada el 3 de diciembre de 2025 y recibió una puntuación CVSS de 10.0. El fallo se encuentra en la decodificación de entradas enviadas al servidor, dentro de los paquetes react-server-dom-*. Aviso de React.
Antes de entrar en el mecanismo, conviene aclarar tres ideas:
- Remota: la entrada llega por la red, mediante una solicitud HTTP a la aplicación.
- Ejecución de código: el servidor realiza operaciones elegidas por el atacante. En el laboratorio veremos esa capacidad ejecutando el comando
id. - Sin autenticación: la explotación no necesita iniciar sesión ni aportar una contraseña para alcanzar el fallo.
Una solicitud HTTP es el mensaje que un cliente envía a una aplicación web. Incluye una ruta, cabeceras con información adicional y, en muchos casos, un cuerpo con datos. Un atacante puede construir ese mensaje con una herramienta; no necesita utilizar los botones o formularios que ofrece la página.
1.1. React, Next.js, RSC y Server Functions
En una aplicación web hay al menos dos lugares donde puede ejecutarse código: el navegador del visitante, llamado cliente, y el servidor que atiende las peticiones. La ubicación importa porque el servidor puede acceder a archivos y secretos que el navegador no debería recibir.
React permite construir la interfaz con componentes: piezas reutilizables como una lista, un menú o un formulario. Next.js integra React con la parte que organiza páginas y atiende solicitudes en el servidor. En el laboratorio, esa parte utiliza Node.js, un entorno que permite ejecutar JavaScript fuera del navegador.
Los Server Components preparan partes de la interfaz fuera del cliente; pueden ejecutarse durante la construcción de la aplicación o al atender una petición. Por ejemplo, un componente puede consultar datos y preparar una lista para mostrarla. Documentación de Server Components.
Las Server Functions permiten que una interacción del cliente solicite la ejecución de una función en el servidor. Por ejemplo, pulsar «Guardar» puede enviar los datos necesarios para actualizar una nota. El framework convierte esa llamada en una solicitud de red. Documentación de Server Functions.
Conviene distinguir los componentes que aparecen en la enumeración:
| Concepto | Función | Qué buscamos al investigar |
|---|---|---|
| React | Biblioteca para construir interfaces | Su presencia en el navegador aporta contexto, pero hace falta identificar el servidor |
| Next.js | Framework que integra React y funciones de servidor | Cabeceras, rutas /_next/, configuración y dependencias |
| React Server Components | Componentes procesados en el servidor | Uso de la infraestructura RSC por el framework |
| Server Functions / Server Actions | Funciones ejecutadas en el servidor desde interacciones del cliente | Rutas que reciben y decodifican sus solicitudes |
| Flight | Formato de intercambio de valores entre cliente y servidor | Procesamiento de referencias, tipos y objetos recibidos |
Una aplicación que únicamente utiliza React en el navegador queda fuera de esta vulnerabilidad. La infraestructura RSC puede estar expuesta aunque el desarrollador no haya definido expresamente Server Functions. Alcance descrito por React.
1.2. Cómo una entrada termina ejecutándose
Primero: qué significa deserializar
Un objeto en memoria no puede viajar por la red tal como existe dentro de un programa. Serializar significa convertir sus datos en un formato que se pueda transmitir. Deserializar significa reconstruir esos datos al recibirlos.
Este ejemplo sencillo utiliza JSON:
const datos = { nombre: 'Ana', cantidad: 2 }
// Convertimos los datos en texto que se puede enviar.
const mensaje = JSON.stringify(datos)
// Reconstruimos un objeto a partir del texto recibido.
const recibido = JSON.parse(mensaje)
console.log(recibido.nombre) // "Ana"JSON.stringify produce texto y JSON.parse lo interpreta como datos. Ese ejemplo muestra una transformación normal. Referencia de JSON.
Después: qué añade Flight
React utiliza Flight para intercambiar valores más complejos. Un chunk es una parte identificada del mensaje; una referencia indica que un valor debe recuperarse de otra parte. Una referencia a una propiedad pide además un dato concreto de ese valor.
Como analogía, «usa el objeto de la parte 1 y toma su campo nombre» evita repetir el objeto completo. Flight también representa valores como promesas, que pueden completarse más adelante. El servidor necesita resolver esas instrucciones para reconstruir los valores. Descripción de Flight por el descubridor.
Finalmente: dónde aparece el fallo
La entrada de red está bajo control del cliente. El decodificador debe procesarla manteniendo límites sobre los objetos y funciones que permite alcanzar.
En la implementación vulnerable, el atacante podía combinar estos pasos:
- Enviar referencias manipuladas. El mensaje solicitaba propiedades que alcanzaban métodos heredados o internos.
- Reconstruir un objeto propio con esos métodos. La entrada controlaba propiedades que influían en su comportamiento.
- Provocar la invocación de
then. La resolución de promesas podía llamar a ese método. - Hacer que métodos internos operaran sobre estado falsificado. El objeto del atacante se trataba como si contuviera datos internos de React.
- Encadenar llamadas hasta ejecutar código controlado. Crear una función y conseguir invocarla completaba la RCE.
Esta secuencia resume el mecanismo, sin reproducir una petición de explotación completa. Investigación de Lachlan Davidson.
El límite de confianza es la separación entre los datos enviados por el cliente y las operaciones internas que decide el servidor. Aquí se rompe porque la reconstrucción de datos permite influir en llamadas a funciones dentro del servidor.
flowchart TD
A["Cliente sin autenticar"] --> B["Solicitud con datos Flight manipulados"]
B --> C["Decodificador RSC vulnerable"]
C --> D["Referencias y estado de objetos controlados"]
D --> E["Invocación de funciones durante la resolución"]
E --> F["Código ejecutado en el servidor"]
F --> G["Permisos del proceso de la aplicación"]1.3. Propiedades heredadas y objetos thenable
Propiedades propias y heredadas
Un objeto agrupa valores con nombres llamados propiedades. Si una propiedad contiene una función, normalmente la llamamos método. En objeto.toString(), toString es el método y los paréntesis indican que se está llamando.
En JavaScript, un objeto puede obtener propiedades de otro objeto, su prototipo. Cuando una propiedad no está en el objeto, JavaScript continúa buscándola en esa cadena de prototipos. Herencia en JavaScript.
const objeto = { nombre: 'Ana' }
console.log(Object.hasOwn(objeto, 'nombre')) // true
console.log(typeof objeto.toString) // "function"
console.log(Object.hasOwn(objeto, 'toString')) // falseLa primera línea confirma que nombre pertenece directamente al objeto. La segunda muestra que podemos acceder a toString, aunque no lo añadimos. La tercera confirma que ese método es heredado.
Esta diferencia importa para un decodificador: permitir «recupera esta propiedad» puede dar acceso a métodos que no formaban parte de los datos esperados. Acceder a un método significa obtener la función; invocarlo significa ejecutarla. La cadena necesita lograr ambas cosas.
Promesas, await y then
Una promesa representa un resultado que estará disponible más adelante. Por ejemplo, una aplicación puede esperar a que termine una consulta. await permite esperar ese resultado dentro de una función async.
JavaScript también admite objetos que se comportan como promesas porque tienen un método then invocable. Se llaman thenables. Al resolverlos, el mecanismo de promesas llama a ese método y le entrega funciones para comunicar el resultado o el error. Resolución de thenables.
async function ejemplo() {
const pendiente = {
then(resolve) {
console.log('Se invocó then')
resolve('resultado')
},
}
const valor = await pendiente
console.log(valor) // "resultado"
}
ejemplo()Aquí await pendiente provoca la llamada a then. La función resolve comunica que el resultado ya está disponible y vale "resultado". Así, una operación que parece «esperar un valor» puede ejecutar un método del objeto.
En React2Shell, el problema aparece cuando ese objeto y sus referencias se reconstruyen desde datos manipulados y alcanzan comportamiento interno del decodificador.
Crear una función y ejecutarla son pasos distintos
Este ejemplo local ilustra la diferencia:
const calcular = new Function('return 2 + 3')
const resultado = calcular()
console.log(resultado) // 5La primera línea crea una función a partir de texto; la segunda la ejecuta. La cadena de React2Shell necesita conseguir la creación y la invocación de código controlado dentro del servidor. Las referencias y los objetos thenable ayudan a conectar esos pasos. Cadena de investigación.
Los ejemplos de esta sección explican comportamientos de JavaScript. Para reproducir la vulnerabilidad hace falta además la implementación RSC afectada y una solicitud que alcance su decodificador.
1.4. Qué cambió el parche
El parche oficial endureció la resolución de Flight y añadió comprobaciones de propiedad propia con hasOwnProperty al recuperar exportaciones de módulos. También revisó la resolución de ciclos y el manejo de errores. El cambio afecta a varias rutas del decodificador; copiar una comprobación aislada dentro de la aplicación no equivale a instalarlo. Parche oficial de React, PR #35277, cambios en los archivos.
La comprobación de propiedad propia pregunta: «¿Este nombre pertenece directamente al objeto que estoy consultando?». Esto permite distinguir las exportaciones esperadas de métodos heredados alcanzados a través del prototipo. Los demás cambios refuerzan cómo se reconstruyen y resuelven los valores.
Un parche es una modificación del software que corrige el fallo. Para aplicarlo, la aplicación debe utilizar los paquetes que incluyen esa modificación, y el servidor debe ejecutar el nuevo despliegue. Editar un formulario o validar un campo concreto no cambia por sí mismo el decodificador incluido en el framework.
2. Impacto de la vulnerabilidad
Un proceso es un programa que está ejecutándose. El sistema operativo lo asocia a una cuenta y decide qué archivos, conexiones y operaciones permite a esa cuenta.
Cuando se explota React2Shell, el código se ejecuta dentro del contexto de la aplicación. Si el servicio funciona como bot, los comandos obtenidos tendrán los permisos de bot. Un archivo que esa cuenta no puede leer seguirá sujeto a esa restricción.
Para evaluar el impacto en una máquina concreta, conviene revisar:
| Recurso accesible al proceso | Consecuencia posible |
|---|---|
| Archivos del proyecto | Lectura de configuración, scripts y código |
| Variables de entorno | Exposición de secretos de la aplicación |
| Directorio de trabajo escribible | Alteración de archivos que el servicio utiliza |
| Red interna | Acceso a otros servicios desde el servidor |
Estas consecuencias dependen de los permisos reales. En 146-react, la salida de id demuestra ejecución como bot, con UID y GID 1000. El alcance de React2Shell queda limitado por los permisos y recursos accesibles al proceso de la aplicación.
Una variable de entorno es un valor que el proceso recibe o conserva para su configuración, como el puerto de escucha o una clave de acceso. Por eso, la capacidad de ejecutar código en el servidor puede exponer información aunque la página nunca la muestre.
3. Historia y relación entre los CVE
- 29 de noviembre de 2025, hora del Pacífico: Lachlan Davidson comunica el fallo a Meta.
- 3 de diciembre de 2025: se divulga CVE-2025-55182 y se publican parches.
- Diciembre de 2025: circulan scanners y PoC; algunas demostraciones iniciales requieren funciones peligrosas expuestas expresamente por la aplicación y no reproducen el fallo original.
El descubridor documenta estas fechas y advierte sobre PoC inválidas y falsos positivos. Sitio del descubridor. Sylvie Mayer también documenta su participación en la investigación y las comprobaciones iniciales sobre Next.js. Relato de Sylvie Mayer.
Una PoC, o prueba de concepto, es una demostración de que un comportamiento se puede reproducir. Un scanner automatiza comprobaciones para identificar posibles objetivos afectados. Un resultado positivo erróneo es un falso positivo; pasar por alto un servidor vulnerable es un falso negativo.
3.1. CVE-2025-55182 frente a CVE-2025-66478
Un CVE es un identificador público de una vulnerabilidad concreta. Permite relacionar avisos, versiones corregidas y herramientas que hablan del mismo fallo. Que un identificador se marque como duplicado significa que ya hay otro registro para ese problema.
CVE-2025-55182 identifica el fallo en React. CVE-2025-66478 se publicó para comunicar su impacto en Next.js y posteriormente fue rechazado como duplicado del primero. El identificador antiguo sigue apareciendo en herramientas y avisos. Registro oficial de CVE-2025-66478.
En 146-react, el texto cita CVE-2025-55182, mientras que el banner de React2Shell Ultimate muestra CVE-2025-66478. Esto no demuestra dos vulnerabilidades independientes.
Next.js incorpora una copia de React en su distribución; revisar solo las dependencias directas puede dejar esa copia fuera del inventario. Explicación del descubridor.
4. Versiones afectadas y condiciones necesarias
Para leer las tablas, una versión como 19.1.2 tiene tres números. Una rama agrupa versiones relacionadas: 19.1.x incluye las que empiezan por 19.1, con distintos valores en el último número. La x es una forma de expresar esa familia; no es un número de versión que se instale.
4.1. Paquetes de React
| Paquete | Versiones afectadas por la RCE original | Primeros parches de la RCE |
|---|---|---|
react-server-dom-webpack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1 |
react-server-dom-parcel | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1 |
react-server-dom-turbopack | 19.0.0, 19.1.0, 19.1.1, 19.2.0 | 19.0.1, 19.1.2, 19.2.1 |
Fuente: advisory oficial de React.
4.2. Next.js
El aviso original incluye aplicaciones con App Router en Next.js 15.x, 16.x y las versiones canary desde 14.3.0-canary.77. Excluye Next.js 13.x, 14.x estable, Pages Router y Edge Runtime para esta RCE. Aviso de Next.js.
| Rama | Primer parche de la RCE |
|---|---|
15.0.x | 15.0.5 |
15.1.x | 15.1.9 |
15.2.x | 15.2.6 |
15.3.x | 15.3.6 |
15.4.x | 15.4.8 |
15.5.x | 15.5.7 |
16.0.x | 16.0.7 |
Canary 15.6 | 15.6.0-canary.58 |
Canary 16.1 | 16.1.0-canary.12 |
Fuente: versiones corregidas en Next.js.
Los primeros parches son una referencia histórica Después se divulgaron fallos de DoS y exposición de código en RSC: CVE-2025-55184, CVE-2025-67779, CVE-2026-23864 y CVE-2025-55183. React indica que el parche de la RCE sigue siendo efectivo y documenta correcciones posteriores en
19.0.4,19.1.5y19.2.4. Para mantener una aplicación, hay que consultar los avisos vigentes de su rama. Aviso actualizado de React.
App Router y Pages Router son formas de organizar las rutas de una aplicación Next.js. Esa configuración ayuda a determinar qué infraestructura utiliza el servidor. Canary identifica versiones de desarrollo publicadas para probar cambios; hay que compararlas con el alcance específico del aviso.
La columna «Primer parche» señala cuándo se corrigió la RCE en cada rama. Para actualizar una aplicación hoy, se debe revisar además el mantenimiento y los avisos posteriores de esa rama.
4.3. Condiciones que debemos comprobar
- Existe un servidor que procesa solicitudes mediante RSC.
- Su despliegue contiene una implementación afectada.
- Podemos alcanzar una ruta que llegue al decodificador.
- La petición atraviesa los controles existentes y llega al proceso vulnerable.
El puerto 3000, la cabecera X-Powered-By: Next.js o una interfaz de ejemplo no establecen la versión instalada ni la explotabilidad.
5. Detección y enumeración
5.1. Reconocimiento del servicio
En el laboratorio de 146-react, la aplicación Next.js se encuentra en el puerto 3000. Para identificar ese servicio:
nmap -sCV -p 3000 192.168.1.233
curl -i http://192.168.1.233:3000/Las opciones del escaneo ayudan a entender qué estamos comprobando:
| Opción | Qué hace |
|---|---|
-sV | Intenta identificar el servicio y su versión a partir de sus respuestas |
-sC | Ejecuta el conjunto de scripts predeterminados de Nmap |
-p 3000 | Limita el escaneo al puerto indicado |
-sCV combina -sC y -sV. El número 3000 identifica el puerto al que nos conectamos; no identifica una vulnerabilidad. Detección de versiones de Nmap, scripts de Nmap.
curl -i solicita la página e incluye las cabeceras de respuesta junto con el contenido. Las cabeceras son campos que el servidor devuelve para describir la respuesta. Opción --include de curl.
Entre los indicadores observados aparecen:
X-Powered-By: Next.js
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
Content-Type: text/html; charset=utf-8Las referencias HTML a /_next/static/ refuerzan la identificación del framework. En este punto tenemos un candidato que investigar.
En esta respuesta, X-Powered-By anuncia Next.js. Vary enumera campos de la solicitud que pueden influir en qué respuesta guarda o entrega una caché; los nombres RSC y Next-Router-* aportan contexto sobre la infraestructura. Content-Type: text/html indica que el contenido devuelto es HTML.
Estas pistas justifican revisar RSC, pero no incluyen la versión exacta ni demuestran que el decodificador sea vulnerable.
5.2. Revisión local de versiones
Cuando se dispone del código o de acceso al despliegue, estos comandos permiten reunir información sin enviar una PoC:
npm ls next react react-dom
npm ls react-server-dom-webpack react-server-dom-parcel react-server-dom-turbopack
node -p "require('next/package.json').version"npm gestiona los paquetes que utiliza el proyecto. npm ls muestra las versiones instaladas y su relación en el árbol de dependencias; los nombres que siguen al comando seleccionan los paquetes que queremos consultar. Documentación de npm ls.
El comando node -p evalúa una expresión de JavaScript e imprime su resultado. Aquí lee el campo version del archivo de metadatos de Next.js. Debe ejecutarse donde Node.js pueda encontrar ese paquete.
package.json declara las dependencias del proyecto; un lockfile, como package-lock.json, registra las versiones resueltas para reproducir su instalación. Un contenedor es un entorno en el que puede estar ejecutándose la aplicación; su instalación puede diferir de la que vemos en otro directorio.
Hay que ejecutarlos en el proyecto o contenedor que realmente sirve la aplicación. Registrar también el lockfile, la imagen desplegada y el comando de arranque evita atribuir al servidor una versión que solo existe en otro checkout.
Un resultado vacío de npm ls react-server-dom-* requiere revisar cómo el framework incluye RSC. No basta para descartar el problema.
5.3. Scanner usado en el writeup
El writeup utiliza React2Shell Ultimate:
python react2shell-ultimate.py -u http://192.168.1.233:3000Resultado relevante de la sesión documentada:
[VULNERABLE] http://192.168.1.233:3000
Version: detected (version unknown) | Status: 500 | Method: safe_side_channelEl parámetro -u indica la URL objetivo. En la salida, cada campo tiene un significado:
| Campo | Cómo interpretarlo |
|---|---|
[VULNERABLE] | Clasificación que devuelve la herramienta al reconocer su patrón |
version unknown | La comprobación no obtuvo la versión exacta |
Status: 500 | El servidor respondió con un error interno HTTP |
safe_side_channel | La comprobación utiliza una señal indirecta del comportamiento |
Un canal lateral permite deducir información observando efectos, como un error específico, en lugar de ver directamente la operación que se quiere confirmar. En este caso hay que distinguir la señal detectada de la salida de un comando realmente ejecutado.
La herramienta informa una detección mediante safe_side_channel y reconoce que la versión es desconocida. Un 500 aislado puede tener muchas causas; no demuestra ejecución de código. Para interpretar la señal hace falta conocer el patrón que evalúa esa versión del scanner y conservar la solicitud y respuesta.
El descubridor advierte que algunas protecciones del proveedor actúan en el runtime y pueden cambiar la explotabilidad sin cambiar la versión visible. Limitaciones de los scanners.
5.4. Contraste con el scanner de Assetnote
React2Shell Scanner de Assetnote ofrece dos comprobaciones diferentes:
--safe-check: utiliza indicadores de error, incluido un digest concreto.- Modo predeterminado: envía una PoC que calcula
41*271y busca11111enX-Action-Redirect.
python3 scanner.py -u http://192.168.1.233:3000 --safe-check
python3 scanner.py -u http://192.168.1.233:3000 -o resultado-react2shell.jsonEl segundo comando ejecuta una comprobación de RCE. Las opciones de este scanner y las de React2Shell Ultimate pertenecen a herramientas distintas. Fuente: documentación de Assetnote.
El digest es un identificador de error que la comprobación espera reconocer. La combinación de estado y digest aporta más información que observar únicamente un 500.
En el modo predeterminado, el scanner busca una respuesta asociada al cálculo enviado. La cabecera X-Action-Redirect comunica una redirección relacionada con una acción; esta PoC la utiliza para devolver el resultado. -o indica el archivo local donde guardar las evidencias. Así podemos revisar qué petición produjo la respuesta.
5.5. Evidencias que permiten formular una conclusión
| Evidencia | Qué permite afirmar |
|---|---|
| Cabeceras de Next.js y RSC | Hay indicios del framework y su infraestructura |
| Versión instalada y configuración del servidor | El despliegue coincide con el alcance del aviso |
Resultado safe_side_channel | El scanner reconoce su patrón de detección |
| Respuesta con un resultado calculado por una PoC | Hay evidencia de ejecución para esa comprobación |
Salida de id obtenida mediante explotación | Se ejecutó un comando y conocemos la identidad del proceso |
En un informe conviene registrar la URL y ruta exactas, fecha, versión o commit de la herramienta, método utilizado, solicitud, respuesta y salida obtenida.
6. Explotación documentada en React de HackMyVM
Los siguientes pasos proceden de 146-react. La IP pertenece a ese laboratorio y las salidas son extractos del writeup.
La herramienta se ejecuta en nuestra máquina y envía solicitudes HTTP al servidor. Si el exploit funciona, el comando elegido se ejecuta en la máquina que atiende la aplicación y la herramienta presenta su salida.
6.1. Confirmación con un comando
python react2shell-ultimate.py --god -u http://192.168.1.233:3000 --cmd iduid=1000(bot) gid=1000(bot) groups=1000(bot)En la versión utilizada en el writeup, --god habilita el modo de ejecución, -u selecciona el objetivo y --cmd id indica el comando remoto. El nombre --god es una opción de la herramienta: los permisos obtenidos se determinan por la cuenta del servidor.
id consulta la identidad del proceso que lo ejecuta. La respuesta se lee así:
| Fragmento | Significado |
|---|---|
uid=1000(bot) | Identificador del usuario y su nombre: bot |
gid=1000(bot) | Identificador y nombre del grupo principal |
groups=1000(bot) | Lista de grupos a los que pertenece el proceso |
El número 1000 es un identificador de Linux. Lo relevante para evaluar el acceso es la cuenta y sus permisos efectivos.
Esta salida confirma ejecución de comandos como bot. No consta una versión exacta de Next.js en la evidencia aportada, así que no debe inventarse a partir del banner ni de la fecha de los archivos.
6.2. Modo de shell y directorio de trabajo
python react2shell-ultimate.py --god -u http://192.168.1.233:3000 --shellLa opción --shell abre una interfaz donde podemos introducir varios comandos. La herramienta los transmite al servidor mediante el mecanismo de explotación.
El texto react2shell:192.168.1.233:3000$ es el prompt, que indica dónde escribir; no forma parte del comando que introducimos. En este ejemplo, el comando es únicamente ls, que lista las entradas del directorio de trabajo.
Dentro de la interfaz:
react2shell:192.168.1.233:3000$ ls
app
eslint.config.mjs
next.config.ts
next-env.d.ts
node_modules
package.json
package-lock.json
postcss.config.mjs
public
README.md
start.sh
tsconfig.jsonEl directorio de trabajo es la carpeta desde la que se ejecuta un comando cuando no indicamos otra ruta. Por eso ls muestra archivos del proyecto.
En la salida, app y public son directorios de la aplicación; node_modules contiene paquetes instalados, y los archivos next.config.ts y tsconfig.json contienen configuración. Reconocer estos nombres ayuda a interpretar el contexto obtenido mediante la RCE.
La lista sitúa el trabajo en el proyecto de la aplicación. package.json y el lockfile serían candidatos para documentar versiones, pero el writeup no incluye su contenido.
Una interfaz --shell puede ejecutar cada comando mediante peticiones independientes. Conviene comprobar si el directorio de trabajo y las variables de entorno persisten entre solicitudes.
6.3. Alcance de la ejecución confirmada
La salida de id confirma la identidad del proceso, y ls demuestra que la herramienta puede ejecutar comandos en el directorio de la aplicación. Estos resultados documentan el impacto directo de React2Shell en el laboratorio: ejecución remota de comandos como bot.
La posibilidad de leer o modificar otros archivos depende de los permisos de esa cuenta. La versión exacta de Next.js permanece desconocida en las salidas aportadas.
7. Detección de actividad y revisión de registros
Durante una investigación del propio servidor, conviene correlacionar:
- Solicitudes POST inusuales hacia rutas de la aplicación.
- Errores de decodificación repetidos cerca del momento investigado.
- Procesos hijos inesperados de Node.js.
- Lecturas o cambios de scripts, archivos de configuración y credenciales.
- Conexiones salientes inesperadas del proceso de Node.js.
Un proceso hijo es un programa que otro proceso inicia. Si Node.js inicia un intérprete de comandos al recibir una solicitud sospechosa, relacionar ambas acciones puede aportar evidencia de ejecución.
Los registros, o logs, son eventos guardados por el servidor. Correlacionarlos significa comparar sus tiempos y datos: qué petición llegó, qué error produjo y qué proceso apareció después. Una petición multipart contiene varias partes en el cuerpo, un formato también usado por formularios y cargas de archivos.
Son señales de investigación. Una petición multipart o una respuesta 500 también pueden pertenecer a tráfico legítimo.
Para revisar los procesos y conexiones asociados a la ejecución de comandos:
ps -eo user,pid,ppid,args --forest
ss -tpnps muestra procesos. user indica la cuenta; pid, el identificador del proceso; ppid, el de su padre; y args, la línea de ejecución. --forest presenta sus relaciones en forma de árbol.
ss -tpn muestra conexiones TCP (-t), información de procesos cuando los permisos lo permiten (-p) y direcciones numéricas (-n). Son observaciones del momento actual; un comando de corta duración puede haber terminado antes de revisarlas.
Si el servicio está gestionado por systemd:
journalctl -u <unidad-de-la-aplicacion> --since "<inicio>" --until "<fin>"journalctl consulta registros de systemd. -u selecciona la unidad del servicio, y --since y --until delimitan el periodo. Los valores entre < > son marcadores que debemos sustituir por el nombre real del servicio y las fechas de la investigación.
La retención y el nivel de detalle de los registros limitan las conclusiones. Si no se guardaron cuerpos de solicitudes o eventos de procesos, puede faltar evidencia aun cuando haya ocurrido una explotación.
8. Mitigación
8.1. Corregir las dependencias y desplegar el cambio
- Identificar la versión que atiende peticiones. Revisar el proyecto o contenedor desplegado y cómo incluye RSC.
- Elegir una versión corregida de una rama mantenida. Consultar el aviso del proveedor antes de decidir el destino de la actualización.
- Actualizar dependencias y lockfile. Registrar los cambios para que otra instalación no vuelva a resolver los paquetes vulnerables.
- Reconstruir la aplicación o imagen. Generar el despliegue con los paquetes nuevos; un archivo ya construido puede conservar la copia anterior.
- Desplegar y reiniciar todas las instancias. Si hay varios servidores o réplicas, todos deben pasar a ejecutar la versión corregida.
- Comprobar la versión en funcionamiento. Confirmar el resultado en el entorno que recibe las solicitudes.
Desplegar significa poner la aplicación preparada en el entorno donde la utilizan los clientes. Cambiar una dependencia en el equipo de desarrollo no actualiza automáticamente ese entorno.
El aviso de Next.js exige actualizar a una versión corregida y recomienda rotar secretos después de parchear y volver a desplegar. Acciones del proveedor. React también indica que las mitigaciones temporales del alojamiento no sustituyen la actualización. Aviso de React.
8.2. Limitar el impacto de la ejecución remota
Estas medidas reducen los recursos accesibles si se explota la RCE:
- Ejecutar la aplicación con una cuenta de servicio y permisos limitados.
- Restringir la lectura y escritura a los archivos que necesita la aplicación.
- Proporcionar al proceso únicamente los secretos necesarios para su función.
- Limitar las conexiones salientes y el acceso a servicios internos.
La corrección del decodificador vulnerable requiere actualizar los paquetes afectados.
8.3. Si se confirma una explotación
Contener significa impedir que siga utilizándose el punto de entrada mientras se corrige. Rotar un secreto significa sustituirlo y dejar de aceptar el valor anterior; si alguien lo copió durante la explotación, el reemplazo corta su utilidad.
Tras contener y corregir el acceso, revisar los recursos a los que podía acceder la cuenta del proceso y sustituir los secretos expuestos. La actualización corrige el punto de entrada; la revisión de archivos, procesos y accesos permite evaluar las acciones ya realizadas.
La revisión debe centrarse en los archivos, variables de entorno y secretos accesibles al proceso vulnerable durante el periodo de exposición.
9. Clasificación
| Campo | Clasificación |
|---|---|
| Nombre | React2Shell |
| CVE principal | CVE-2025-55182 |
| Identificador histórico en Next.js | CVE-2025-66478, rechazado como duplicado |
| CWE | CWE-502 — Deserialization of Untrusted Data |
| Severidad | CVSS 3.1: 10.0 |
| Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H |
| Privilegios previos | Ninguno para la RCE |
| Interacción de la víctima | No requerida |
| Contexto de ejecución | Permisos del proceso del servidor |
Fuente: advisory de React, registro del identificador duplicado.
CWE clasifica el tipo de debilidad: CWE-502 corresponde a deserializar datos no confiables sin garantizar suficientemente que el resultado sea válido. CVE identifica el fallo concreto y CVSS describe su gravedad con unas condiciones de referencia.
El vector CVSS resume esas condiciones:
| Parte | Significado |
|---|---|
AV:N | Se puede atacar a través de la red |
AC:L | La complejidad del ataque se clasifica como baja |
PR:N | No se necesitan privilegios previos |
UI:N | No se requiere interacción de otra persona |
S:C | La evaluación considera un cambio de ámbito de seguridad |
C:H/I:H/A:H | Impacto alto en confidencialidad, integridad y disponibilidad |
Confidencialidad trata de mantener la información accesible solo a quien corresponde; integridad, de impedir cambios no autorizados; y disponibilidad, de mantener el servicio utilizable. La puntuación describe la severidad del fallo. El alcance concreto de un laboratorio se demuestra con sus propias evidencias.