Imperva Red Team ejecutó código con privilegios de superusuario en la imagen oficial de Node.js 24.22.0 usando un solo flujo Brotli manipulado, sin depurador ni compilación alterada, en la demostración que publicó el 22 de octubre de 2026 con el nombre Node2Shell. El defecto, CVE-2026-111497, reside en el decodificador Brotli que Node.js integra en el runtime, alcanza a las tres ramas con soporte y se corrige en las versiones 22.23.4, 24.22.1 y 26.12.1 que el proyecto lanzó el 21 de octubre.

Cómo un cambio de bloque imposible rompe el decodificador Brotli
El decodificador valida mal un flujo comprimido que declara un único tipo de bloque y acto seguido solicita cambiar a otro, una transición que la propia estructura del stream vuelve imposible. En vez de rechazarla, el código la interpreta como una petición de más datos y continúa decodificando con el estado interno ya corrompido. Google etiqueta el defecto como CWE-787, escritura fuera de límites, y CWE-252, valor de retorno sin comprobar.
Su ficha en NVD describe que un atacante remoto no autenticado puede corromper la memoria contigua del decodificador, filtrar contenido sensible, provocar una denegación de servicio o llegar a ejecutar código con un stream Brotli diseñado para ello. La corrección es una sola comprobación en el propio Brotli. El commit 0ed4268a del proyecto de Google añade un código de error nuevo, BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH, que detiene la decodificación justo en la transición inválida. Node.js incorpora su propia copia de Brotli dentro del binario, de modo que actualizar un paquete de npm no sirve de nada y hay que cambiar el runtime completo.
Dato | Valor |
|---|---|
CVE | CVE-2026-111497 |
CVSS 4.0 (Google, CNA) | 9,2 crítica (AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H) |
Gravedad según Node.js | Alta |
Tipo de fallo | CWE-787 escritura fuera de límites y CWE-252 valor de retorno sin comprobar |
Producto | Decodificador Brotli incluido en Node.js |
Versiones afectadas | Node.js 22.x, 24.x y 26.x previas al parche; Google Brotli 1.0.3 a 1.2.0; Bun anterior a 1.4.4 |
Versiones corregidas | Node.js 22.23.4, 24.22.1 y 26.12.1; Brotli 1.0.10, 1.1.1 y 1.2.1; Bun 1.4.4 |
Explotación activa | No observada a 23 de octubre de 2026; fuera del catálogo KEV de CISA |
Imperva resume en un diagrama las dos superficies por las que un flujo manipulado alcanza el mismo decodificador nativo, una desde el lado cliente y otra desde el servidor.

Las tres vías por las que datos Brotli llegan al runtime
Un servidor HTTP básico de Node.js no descomprime por su cuenta los cuerpos Brotli que recibe, así que el riesgo depende de qué código del lado de la aplicación toca datos de terceros. Imperva y otros investigadores coinciden en tres caminos concretos.
El primero es el cliente HTTP. La función fetch() integrada descomprime de forma automática una respuesta con la cabecera Content-Encoding: br, de manera que un servidor controlado por el atacante, un webhook o una URL que introduce el usuario bastan para alcanzar el decodificador. El segundo es el cuerpo de las peticiones entrantes: desde Express 5, los middlewares express.json(), express.urlencoded(), express.text() y express.raw() descomprimen Brotli además de gzip y deflate, según la guía de migración a Express 5. El tercero es el uso directo de las funciones de node:zlib como brotliDecompress o createBrotliDecompress con entrada no fiable.
El analista Florian Roth (@cyb3rops) avisó en X de que la superficie es mayor de lo que parece, porque una configuración habitual de Express 5 ya expone el decodificador sin que el programador use Brotli de forma explícita en ningún punto de su código. Su recomendación cabe en tres palabras: «Patch your Node.js runtimes» (actualiza tus runtimes de Node.js).
I think this is more serious than many people realize.
— Florian Roth ⚡️ (@cyb3rops) October 22, 2026
Not every Node.js server is vulnerable, of course. The application needs to process untrusted Brotli-compressed data.
But how common is that? Well, common middleware configurations in Express 5 can already expose the… https://t.co/Jc6zJ6NL7u pic.twitter.com/V5lEhz0cnj
De la demostración de Imperva a las PoC públicas
Imperva probó un exploit independiente contra aplicaciones que corrían sobre la imagen oficial de Node.js 24.22.0 en Linux arm64, en contenedores recién creados y sin depurador. La aplicación de prueba no tenía código para crear ningún fichero marcador y, tras recibir los datos Brotli, el fichero apareció, prueba de que el código suministrado se ejecutó en el proceso. El análisis de Imperva Red Team reconoce que la ejecución no funcionó a la primera y que necesitó varios intentos, catorce en la grabación publicada, mientras que la versión 24.22.1 rechaza la misma entrada.
Las pruebas de concepto públicas aparecieron el mismo 22 de octubre. La primera solo lograba ejecución de código sobre una compilación deliberadamente debilitada y, en una build estándar, su autor describió una caída y una lectura limitada de memoria. Un repositorio posterior de dimasma0305 documenta ejecución de código en tres perfiles exactos de Linux x86-64, entre ellos Express 5.3.0 con body-parser 2.4.0 y un worker que consume fetch(), y deja el resto de escenarios en denegación de servicio.
Ese matiz importa. El mismo repositorio verifica solo caída del servicio en zlib asíncrono estándar sobre Node.js 20.19.5, 22.23.3, 24.22.0 y 26.12.0, y en clientes muy usados como Undici, Axios, Got, node-fetch, SuperAgent, NestJS, Koa y una ruta de Next.js. La ejecución de código es probabilística, depende del binario exacto y de acertar la disposición de memoria bajo ASLR, por lo que no implica que cualquier despliegue de Express sea explotable. A 23 de octubre no hay explotación activa registrada y el fallo no figura en el catálogo KEV de CISA.

Quién corre Node.js vulnerable, incluidas las distribuciones Linux
El fallo afecta a cualquier servicio que descomprima Brotli no fiable sobre Node.js 22.x, 24.x o 26.x sin parchear. Las ramas 25.x, 23.x, 20.x y anteriores están fuera de soporte y no reciben corrección, así que la única salida es migrar a una versión mantenida. El runtime alternativo Bun comparte el defecto hasta la versión 1.4.4, como recoge el parche que retroporta el arreglo de Brotli. Nada de esto es un zero-day. Hay parche disponible y el reloj corre desde la publicación del aviso.
Hay un detalle que afecta de lleno a los servidores Linux. El Debian Security Tracker marca como vulnerables tanto el paquete brotli como nodejs en bookworm, trixie y sid, y anota que «nodejs uses system provided brotli library» (nodejs usa la biblioteca Brotli del sistema). En esas distribuciones actualizar solo el binario de Node.js no basta y el arreglo llega cuando el mantenedor publica la biblioteca Brotli corregida, de modo que conviene seguir el aviso de seguridad de la distribución además del número de versión del runtime.

No hay un recuento público de servidores expuestos en el momento de escribir esta noticia, porque la exposición depende de si la aplicación descomprime Brotli ajeno, más que de la versión instalada. Tampoco consta un aviso específico de INCIBE-CERT ni del CCN-CERT sobre este CVE a 23 de octubre, aunque el fallo llega una semana después de la tanda de parches de emergencia de PHP y del aviso de Node.js sobre el mismo decodificador que ya habían puesto el tema sobre la mesa.
Qué actualizar en servidores, contenedores y CI
Las acciones, por orden de urgencia:
- Actualizar el runtime a 22.23.4, 24.22.1 o 26.12.1, o a una versión superior de cada rama, en servidores, imágenes de contenedor y cualquier artefacto que empaquete un binario de Node.js.
- Reconstruir las imágenes sobre una etiqueta base parcheada, porque un pin del tipo node:24.22.0 permanece vulnerable aunque se reconstruya.
- Revisar los runners de integración continua y las imágenes de contenedor efímeras, un punto ciego que ya pesó en el incidente de LiteLLM, comprobando la versión con node --version y docker run.
- Localizar los puntos de exposición con npm ls express body-parser, buscar en el código las llamadas a brotliDecompress y createBrotliDecompress, y listar las peticiones fetch() salientes hacia servidores que no controlas.
- Como medida temporal, rechazar en el proxy inverso, el balanceador o el middleware las peticiones con la cabecera Content-Encoding: br, teniendo en cuenta que eso no cubre las respuestas Brotli que la aplicación descarga con fetch().
- Migrar fuera de las ramas sin soporte en lugar de confiar en mitigaciones de borde, ya que esas versiones no recibirán el parche.
Para inventariar deprisa qué versión corre cada imagen sin arrancarla entera, este comando de comprobación basta:
docker run --rm node:24-alpine node --version
node --versionSi no es posible actualizar de inmediato, Node.js no ofrece ninguna mitigación dentro del runtime, pero sí se puede cortar la vía de las peticiones entrantes en un proxy. Vercel desplegó el 21 de octubre reglas de firewall que bloquean el vector entrante en todos sus proyectos y advierte en su boletín de seguridad de que esas reglas no cubren las respuestas que las funciones descargan de otros servidores. En Nginx, descartar un cuerpo comprimido con Brotli antes de que llegue a la aplicación se reduce a una condición:
location / {
if ($http_content_encoding ~* "br") { return 415; }
proxy_pass http://app_backend;
}La semana demuestra que el perímetro clásico ya no es el único foco. Mientras los equipos parcheaban PHP, el mismo tipo de corrupción de memoria que terminaba en ejecución de código aparecía en el motor de expresiones de GitLab y ahora en una biblioteca de compresión que casi nadie incluye a conciencia. El exploit probabilístico es lo de menos. Node2Shell le sirve a quien defiende como recordatorio de que una dependencia transitiva dentro del runtime amplía la superficie sin que aparezca en ninguna línea del package.json.
Por eso la defensa que aguanta este caso es la misma de siempre bien hecha: saber qué runtimes corren de verdad, incluidos los que viven dentro de un addon nativo de npm, mapear dónde entra contenido de terceros y mantener un inventario que permita parchear en horas, algo que sostienen las prácticas DevSecOps y la disciplina de proteger cada API que recibe datos comprimidos. El binario parcheado ya existe. Queda encontrar todas sus copias.



No hay comentarios todavía