GHSA-673q-mjm6-r5xr: RCE sin autenticación en funciones «use cache» de Next.js
GHSA-673q-mjm6-r5xr está calificado como crítico, CVSS v4.0 9.5, en GitHub Advisory Database: un atacante no autenticado puede ejecutar código en un servidor Next.js 16 que utilice App Router con Cache Components y tenga una función use cache que devuelva varios arrays tipados. La revisión de la base de datos clasifica el fallo como deserialización de datos no confiables (CWE-502) en el protocolo de transporte React Server Components incluido en Next.js. Corregido en Next.js 16.4.1, sin medidas provisionales disponibles.
Resumen
- Un atacante no autenticado puede lograr ejecución remota de código en una aplicación Next.js 16 que active Cache Components y tenga una función
use cacheque devuelva varios arrays tipados. La revisión de GitHub Advisory Database clasifica el fallo como CWE-502, deserialización de datos no confiables. - Están afectadas todas las versiones de Next.js 16 desde 16.0.0 hasta 16.4.0. GitHub Advisory Database lo califica como crítico, CVSS v4.0 9.5, explotable por red sin autenticación ni interacción del usuario.
- No existe prueba de concepto pública, no se conoce explotación en ataques reales a fecha del 23 de octubre de 2026 y el problema no figura en el catálogo CISA Known Exploited Vulnerabilities.
- Actualice ahora a Next.js 16.4.1. Vercel afirma que no existe ninguna medida provisional y recomienda actualizar inmediatamente aunque su aplicación no tenga funciones
use cacheque devuelvan arrays tipados.
¿Estoy afectado?
Está afectado si su aplicación ejecuta Next.js 16.0.0 a 16.4.0 con App Router y Cache Components activados. Cache Components es el modelo de caché de Next.js 16 en el que las funciones marcadas con la directiva use cache almacenan y restauran sus resultados entre solicitudes. La condición desencadenante del aviso es una función use cache que devuelve varios arrays tipados, como valores Uint8Array o Float64Array.
| Rama | Versiones afectadas | Versión corregida |
|---|---|---|
| Next.js 16.x | 16.0.0 – 16.4.0 | 16.4.1 |
Compruebe lo que está instalado:
# Installed Next.js version
npm ls next
node -e "console.log(require('next/package.json').version)"
# Lock file check (package-lock.json v2/v3 records the package under "node_modules/next")
grep -A1 '"node_modules/next"' package-lock.json
# Container image check
docker run --rm <your-image> node -e "console.log(require('next/package.json').version)"
Después compruebe si su código activa la funcionalidad afectada. Cache Components es una configuración opcional y use cache aparece directamente en el código del servidor:
# Cache Components enabled in the Next.js config
grep -n "cacheComponents" next.config.*
# Any use of the cache directive across the project, including src/ and shared libraries
grep -rn "use cache" --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" --exclude-dir=node_modules --exclude-dir=.next .
Una sola función que devuelva dos o más arrays tipados cumple la condición desencadenante publicada.
¿Cómo lo corrijo?
Actualice Next.js a 16.4.1 o posterior:
npm install next@16.4.1
# or: pnpm add next@16.4.1 | yarn add next@16.4.1
No existe ninguna medida provisional. El aviso de Vercel es explícito: actualice inmediatamente, aunque su aplicación no tenga esas funciones use cache. La misma versión también corrige la RCE sin autenticación de Image Optimization publicada ese día (GHSA-2464-fhr4-w9xw, causada por el fallo original CVE-2026-96889 de librsvg), por lo que una actualización resuelve ambos problemas.
¿Cuál es la causa raíz?
Next.js se basa en React Server Components (RSC), donde el servidor renderiza la salida de los componentes en un flujo de fragmentos serializados, normalmente llamado datos flight. Volver a leer ese flujo es deserialización: el entorno de ejecución recorre los fragmentos, resuelve referencias y reconstruye objetos, promesas, flujos, arrays tipados y referencias a funciones del servidor. Con Cache Components, el valor devuelto por una función use cache se almacena y restaura mediante ese mismo transporte, de modo que el deserializador gestiona datos cuyo origen es una entrada de caché. El aviso clasifica el fallo como CWE-502, deserialización de datos no confiables.
Vercel no ha publicado la ruta exacta de explotación, por lo que el análisis siguiente procede de leer la corrección. El parche, commit a4cea6a titulado «Mejorar el protocolo de transporte de Server Component», sustituye los entornos react-server-dom-webpack y react-server-dom-turbopack incluidos en las doce variantes de compilación por versiones que endurecen el límite de deserialización en cuatro lugares.
Primero, el antiguo deserializador resolvía cada entrada del modelo sin comprobar si una propiedad llamada then iba a hacer que un objeto ordinario pareciera una promesa. JavaScript trata cualquier objeto con una propiedad then invocable como una promesa (un thenable), y el código que lo espera o encadena llama a esa función. El nuevo código rechaza la confusión al analizarlo:
// after the fix, in the bundled react-server-dom client and server runtimes
function createModel(response, model, parentObject, key) {
return "then" === key && "function" === typeof model ? null : model;
}
Antes de la corrección, createModel devolvía el modelo sin cambios, de modo que un objeto deserializado con una función then permanecía activo en el grafo de objetos reconstruido y podía invocarse donde el entorno de ejecución o la aplicación lo trataran como promesa.
Segundo, la resolución de referencias de servidor, que asigna una referencia serializada a una función real del servidor y puede terminar cargando código mediante requireModule, ahora valida su entrada:
function loadServerReference(response, metaData, parentObject, key) {
if ("then" === key) return null;
var id = metaData.id;
if ("string" !== typeof id) return null;
...
Antes de la corrección, metaData.id llegaba a resolveServerReference y requireModule sin comprobación de tipo.
Tercero, el entorno dejó de exponer su objeto de respuesta interno mediante el grafo de fragmentos. El código anterior almacenaba directamente el objeto de respuesta activo como chunk.reason en cada fragmento de modelo resuelto; un recorrido de propiedades sobre objetos reconstruidos podía alcanzarlo y, mediante él, acceder al mecanismo de deserialización. El nuevo código oculta la respuesta detrás de un símbolo generado en tiempo de ejecución dentro de un objeto contenedor, por lo que un recorrido normal de propiedades no puede alcanzarla:
// before: new ReactPromise("resolved_model", value, response)
// after:
var RESPONSE_SYMBOL = Symbol();
function createResolvedModelChunk(response, value) {
var holder = {};
return new ReactPromise("resolved_model", value,
((holder[RESPONSE_SYMBOL] = response), holder));
}
Cuarto, el transporte separa ahora lo que antes compartía un único espacio de claves. Los payloads serializados viajan como entradas form-data cuyas claves son un prefijo más un identificador de fragmento. Los cambios, uno por aspecto:
- El antiguo deserializador resolvía el estado interno mediante una consulta directa a todas las entradas:
response._formData.data.get(response._prefix + id), y las lecturas de arrays tipados hacían lo mismo medianteresponse._formData.data.get(key).arrayBuffer(). - El nuevo código agrupa las entradas por clase al recibirlas: los fragmentos de flujo bajo una clave prefijada con el prefijo del campo más «S», los fragmentos de cadena y los blobs binarios en sus propios mapas, y las lecturas de arrays tipados pasan por
getBackingBlob(response._formData, key). - En la serialización, el cliente escribe las entradas de flujo bajo
formFieldPrefix + "S" + streamIden lugar deformFieldPrefix + streamIdsin distinción. - La serialización de arrays tipados copia un array de más de 4096 bytes (2048 bytes en compilaciones para navegador) cuando su vista no empieza al principio del búfer subyacente, no abarca todo el búfer o este es redimensionable.
En una frase: el deserializador confiaba en la estructura de las entradas que leía, aceptando objetos que se hacían pasar por promesas, referencias de servidor con tipos de identificador arbitrarios y entradas cuya clase dependía únicamente de su clave. El parche añade comprobaciones de tipo y clase en cada uno de esos puntos de confianza.
¿Cómo se explota?
No hay prueba de concepto pública y Vercel no ha documentado la ruta exacta de la solicitud, de modo que lo siguiente describe la clase de explotación del aviso y el parche, no una receta paso a paso.
- La aplicación activa Cache Components y define una función
use cacheque devuelve varios arrays tipados. Cada array se convierte en un fragmento binario independiente del payload serializado, la condición que Vercel señala como necesaria. - Una solicitud no autenticada llega a la aplicación. El vector CVSS (red, sin privilegios ni interacción del usuario) y la clasificación CWE-502 describen la superficie de ataque: la entrada del atacante alcanza datos que procesa el deserializador flight.
- Dentro de esos datos, el atacante proporciona entradas preparadas contra las brechas de confianza que cierra el parche: un objeto con una función
thenque el entorno llamará como promesa; una referencia de servidor cuyoidno es una cadena pero se resuelve a contenido elegido por el atacante; o una entrada que aprovecha el espacio plano de claves para que el deserializador la trate como fragmento interno. - El deserializador resuelve la entrada manipulada. Mediante la resolución de referencias de servidor, la ruta puede alcanzar
requireModule, la función que asigna una referencia serializada a código real del servidor. - La referencia elegida por el atacante se ejecuta con los privilegios del proceso servidor de Next.js: ejecución remota de código.
La condición de los arrays tipados encaja directamente en esta ruta: la parte de serialización de la corrección modifica cómo se emiten arrays cuyas vistas no abarcan todo el búfer subyacente, y la de deserialización cambia cómo se buscan sus blobs subyacentes. Ese es el código que se ejecuta cuando varios arrays tipados comparten un payload.
Flujo del ataque
ruta de Cache Components"] --> B["La función use cache devuelve
varios arrays tipados"] B --> C["El protocolo flight serializa el resultado
como entradas form-data fragmentadas"] C --> D["Una entrada influida por el atacante comparte
el espacio de claves con fragmentos internos"] D --> E["El deserializador resuelve la entrada como
modelo interno o referencia de servidor"] E --> F["Se acepta la confusión thenable o
un id de referencia sin validar"] F --> G["requireModule resuelve la
referencia y se ejecuta código"]
Las comprobaciones ausentes están en D y E: antes de la corrección, nada validaba la clase de una entrada ni el tipo del identificador de una referencia de servidor antes de resolverlos.
¿Qué puede hacer un atacante?
Una explotación exitosa ejecuta código arbitrario como el proceso servidor de Next.js. Ese proceso suele contener secretos de despliegue, conexiones de base de datos y credenciales de servicios, y puede leer y reescribir todo lo que alcance la aplicación. El vector CVSS v4.0 (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:N) puntúa como alto el impacto en confidencialidad e integridad tanto del sistema vulnerable como de sistemas posteriores, y como alto el impacto en disponibilidad solo del sistema vulnerable.
Las condiciones que necesita el atacante, hasta donde indica el aviso:
- La aplicación ejecuta Next.js 16.0.0 a 16.4.0 con App Router y Cache Components activados.
- La aplicación tiene una función
use cacheque devuelve varios arrays tipados. - El ataque no requiere autenticación ni interacción del usuario, pero el vector CVSS marca requisitos de ataque presentes, lo que coincide con la condición desencadenante dependiente de la funcionalidad descrita.
La instrucción de Vercel de actualizar incluso sin esas funciones sugiere que ese límite no debe considerarse una protección fiable, porque el código de la aplicación cambia y el transporte es infraestructura compartida.
¿Por qué funciona la corrección?
El commit a4cea6a sustituye el entorno React Server Components incluido en todos los destinos de compilación: servidor y cliente, webpack y turbopack, desarrollo y producción. Cada uno de los cuatro cambios cierra una brecha de confianza en el límite de deserialización:
-function createModel(response, model) {
- return model;
+function createModel(response, model, parentObject, key) {
+ return "then" === key && "function" === typeof model ? null : model;
}
function loadServerReference(response, metaData, parentObject, key) {
+ if ("then" === key) return null;
+ var id = metaData.id;
+ if ("string" !== typeof id) return null;
...
Se descarta una función then en los datos deserializados, por lo que ningún objeto reconstruido puede hacerse pasar por promesa. Una referencia de servidor debe llevar un identificador de cadena antes de resolverse o cargarse. El objeto de respuesta queda fuera del alcance del recorrido de propiedades. Y las agrupaciones de entradas, junto con la serialización corregida de arrays tipados, hacen que la clase de un fragmento dependa de dónde se almacena, no de la clave elegida por un atacante.
¿Es completa la corrección? Cierra todas las brechas que aborda el parche en las doce variantes del entorno incluido, que constituyen toda la superficie distribuida por Next.js. Lo que no puede verificarse con el material público es si el informe original utilizaba una sola de estas rutas o una combinación: Vercel no publicó detalles del exploit. La corrección cubre la clase en cualquier caso, porque toda ruta al deserializador pasa ahora por las comprobaciones añadidas.
¿Cómo compruebo si he sufrido una intrusión?
No se han publicado indicadores de compromiso para este aviso. A falta de orientación pública, busque estas señales generales:
- Tráfico POST inesperado hacia rutas bajo Cache Components, especialmente solicitudes con cuerpos form-data cuyos nombres de campo lleven prefijos de serialización en lugar de campos de formulario normales.
- Entradas del manejador de caché cuyas claves o payloads parezcan malformados o contengan identificadores de fragmento que no correspondan a escrituras legítimas, si el manejador registra las claves.
- Trazas de pila del servidor procedentes del deserializador flight, como errores al resolver referencias de servidor o inicializar fragmentos de modelo, que no coincidan con su código.
- Cualquier evidencia de ejecución de código en torno a esos errores, incluidos archivos nuevos, conexiones salientes del proceso servidor o cambios en el estado del entorno.
Si encuentra actividad sospechosa, considere el proceso servidor completamente comprometido: rote todos los secretos que pudiera leer, incluidos tokens de despliegue, credenciales de base de datos y claves de API, y reconstruya el despliegue desde un estado conocido como válido.
Cronología
- 14 de octubre de 2026: Vercel publicó GHSA-673q-mjm6-r5xr y Next.js 16.4.1, que contiene la corrección de transporte de Server Component (commit a4cea6a, creado el 14 de octubre de 2026).
- 22 de octubre de 2026: el aviso se publicó y revisó en GitHub Advisory Database.
- 23 de octubre de 2026: este artículo. No hay prueba de concepto pública, no se conoce explotación en ataques reales y el problema no figura en el catálogo CISA KEV.
Preguntas frecuentes
¿Existe un CVE para este problema?
A fecha del 23 de octubre de 2026, el aviso GHSA-673q-mjm6-r5xr no tiene identificador CVE; se registra mediante su identificador GHSA en GitHub Advisory Database.
¿Necesito tener Cache Components activado para estar afectado?
El aviso describe la vulnerabilidad en aplicaciones que utilizan App Router con Cache Components y una función use cache que devuelve varios arrays tipados. Sin embargo, Vercel indica a todos los usuarios que actualicen inmediatamente incluso sin esas funciones, y 16.4.1 también corrige la RCE de Image Optimization, por lo que la respuesta práctica es actualizar en cualquier caso.
¿Qué son los arrays tipados y por qué importa que haya varios?
Un array tipado, como Uint8Array, es una vista binaria de longitud fija sobre un bloque de memoria. En el protocolo flight, cada uno se serializa como su propio fragmento con su propia clave, por lo que una función que devuelve varios produce un payload con muchas entradas de fragmento, la forma descrita por la condición desencadenante publicada.
¿Puedo limitarme a eliminar los arrays tipados de mis funciones en caché?
Eso elimina la condición desencadenante publicada de su código, pero no es una corrección: Vercel afirma que no existe medida provisional y pide actualizar inmediatamente, y cualquier cambio de código puede reintroducir la condición. Actualice a 16.4.1.
¿Quién lo descubrió y corrigió?
El usuario de GitHub evolutionstorm comunicó el problema y figura como descubridor en la entrada de GitHub Advisory Database. Personal de Vercel creó la corrección en el entorno React Server Components incluido y el equipo de seguridad de Vercel publicó el aviso.
¿Qué popularidad tiene el software afectado?
El paquete next registró 44.586.009 descargas en npm en la semana del 16-22 de octubre de 2026, lo que lo convierte en uno de los frameworks web más desplegados del ecosistema JavaScript.
Escanee sus aplicaciones Next.js con Ostorlab para identificar la versión 16.x afectada y el uso de use cache en sus repositorios.
Referencias
- Aviso de Vercel GHSA-673q-mjm6-r5xr: https://github.com/vercel/next.js/security/advisories/GHSA-673q-mjm6-r5xr (consultado el 23 de octubre de 2026)
- Aviso de React GHSA-xqc2-xf4c-x3h3, la misma RCE de arrays tipados en react-server-dom-webpack, react-server-dom-turbopack y react-server-dom-parcel, corregida en 19.0.9, 19.1.10, 19.2.9 y 19.3.1: https://github.com/react/react/security/advisories/GHSA-xqc2-xf4c-x3h3 (consultado el 26 de octubre de 2026)
- Entrada de GitHub Advisory Database (severidad, CVSS v4.0 9.5 y CWE-502 confirmados el 24 de octubre de 2026): https://github.com/advisories/GHSA-673q-mjm6-r5xr
- Commit de corrección a4cea6a, «Mejorar el protocolo de transporte de Server Component»: https://github.com/vercel/next.js/commit/a4cea6aec5da89d116b8960e113189cf51745d2f (consultado el 23 de octubre de 2026)
- Notas de Next.js 16.4.1: https://github.com/vercel/next.js/releases/tag/v16.4.1 (consultado el 23 de octubre de 2026)
- Recuento de descargas npm de
next(44.586.009 descargas, semana del 16-22 de octubre de 2026): https://api.npmjs.org/downloads/point/last-week/next (consultado el 24 de octubre de 2026)