Ejecuta esto ahora mismo. Si tu versión está por debajo del umbral mínimo de la tabla, la vulnerabilidad de RCE en Brotli de Node.js (CVE-2026-111497) te afecta, y los atacantes ya la están explotando activamente.
node -v | Línea de versión | Vulnerable | Corregida a partir de | Última versión (5 nov. 2026) |
|---|---|---|---|
| 26.x | Anterior a 26.12.1 | 26.12.1 | 26.13.0 |
| 24.x | Anterior a 24.22.1 | 24.22.1 | 24.22.2 |
| 22.x | Anterior a 22.23.4 | 22.23.4 | 22.23.4 |
| 25.x, 20.x y anteriores | Fin de ciclo de vida (EOL): sin parchear y sin evaluar | No existe parche | Migrar a 24 o 26 |
Para obtener una segunda comprobación, consúltale directamente al binario:
node -p "require('node:zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH" El parche introdujo esta constante. Lo probamos en nueve compilaciones oficiales de macOS de las tres líneas con soporte: cada versión corregida devolvió -17, mientras que las versiones anteriores mostraron undefined. Aun así, dale prioridad a node -v, ya que una distribución de Linux que incorpore el parche como backport podría no incluir esta constante.
No te fíes de process.versions.brotli. Mostró 1.2.0 tanto en 24.21.0 como en 24.22.1, y 1.1.0 tanto en 22.23.3 como en 22.23.4. Node.js aplicó el parche mediante un cherry-pick sin modificar la versión reportada, por lo que este campo no sirve para saber si tu sistema está protegido.
En resumen (TL;DR)
| Pregunta | Respuesta |
|---|---|
| ¿Qué es? | Escritura fuera de límites (out-of-bounds write) en el decodificador de Brotli integrado en Node.js, también conocida como Node2Shell o HTTP2Shell |
| Impacto | Caída del proceso (crash), filtración de memoria o ejecución remota de código (RCE) |
| Severidad | ALTA según Node.js; 9.8 CRÍTICA (CVSS 3.1) según la NVD |
| ¿Se está explotando activamente? | Sí. El catálogo KEV de CISA la incorporó el 27 de octubre de 2026 |
| ¿Requiere autenticación? | No, según el vector del NVD y el análisis de Akamai |
| ¿Quiénes están expuestos? | Aplicaciones que decodifican datos Brotli que no controlan: cuerpos de petición en Express 5, respuestas de fetch() o llamadas directas a node:zlib |
| Solución | Actualizar el runtime, recompilar imágenes de contenedor y reiniciar los procesos |
| ¿Existe alguna mitigación temporal? | El aviso oficial de seguridad no incluye ninguna. Las medidas provisionales que detallamos abajo reducen la exposición, pero no cubren todos los escenarios |
ELLIO reportó tráfico de exploits en sus sensores el 26 de octubre, apenas cinco días después del lanzamiento del parche. La carga útil (payload) interceptada apuntaba a Node.js 24.22.0 en arquitectura x86-64. Aunque la fase final de dicho payload falló en las pruebas de ELLIO, la propia firma confirmó la ejecución de código en compilaciones estándar de la versión 24.22.0. CISA fijó el 10 de noviembre como plazo límite para que las agencias federales de EE. UU. tomen medidas, marcando su uso en ransomware como «Desconocido». Además, la telemetría de Akamai reveló que el 61 % de las redes monitoreadas aún ejecutaban versiones vulnerables el 26 de octubre.
Cómo funciona Node2Shell: el impacto del fallo en Brotli
Node2Shell es una vulnerabilidad de escritura fuera de límites en el decodificador de Brotli integrado directamente en el runtime de Node.js. Al procesar un flujo comprimido con formato incorrecto, el decodificador escribe más allá de un búfer interno de ocho bytes, corrompiendo la memoria del proceso. Como consecuencia, puede provocar la caída del servidor, filtración de memoria o la ejecución remota de código bajo el control del atacante.
La causa raíz radica en una clasificación incorrecta del error: ante un cambio de bloque no válido, el decodificador interpretaba «necesito más datos de entrada» en lugar de «estos datos están corruptos», por lo que seguía añadiendo bytes al pequeño búfer interno.
El error reside en la biblioteca Brotli de Google, desde la versión 1.0.3 hasta la 1.2.0, por lo que no se limita exclusivamente a Node.js. El equipo de Node.js solucionó su implementación aplicando un cherry-pick del commit upstream 0ed4268a en la versión 24.22.1 y en sus versiones equivalentes. La vulnerabilidad fue descubierta y reportada por los investigadores Khai Tran y Sylvie Mayer, de OpenAI Codex Security.
Tus herramientas de análisis de dependencias no detectarán este fallo, ya que el decodificador forma parte del runtime y no de un paquete npm. De hecho, npm install indicó 0 vulnerabilidades en nuestro proyecto de prueba con Express 5 ejecutándose sobre una versión vulnerable de Node.js 24.15.0.
Quiénes están expuestos
Contar con un runtime vulnerable es solo una parte de la ecuación; la otra es contar con una vía que suministre datos Brotli no confiables al decodificador. En nuestro laboratorio probamos cada escenario sobre Node.js 24.22.1 enviando un cuerpo no válido e inofensivo (el texto not-valid-brotli) con la cabecera Content-Encoding: br, además de servir contenido Brotli válido a cada cliente.
| Vía de acceso | Versión probada | ¿Ejecuta el decodificador? | Resultado observado |
|---|---|---|---|
Express 5 express.json() | 5.3.0 (body-parser 2.4.0) | Sí, por defecto | 400, “Decompression failed” |
Express 4 express.json() | 4.22.3 (body-parser 1.20.9) | No | 415, codificación no admitida |
Koa @koa/bodyparser | 6.1.0 | Sí, por defecto | 500, error de BrotliDecoder en el registro |
Fastify con @fastify/compress | 9.3.0 | Sí, tras registrar el plugin | 400, FST_CP_ERR_INVALID_CONTENT |
| Fastify básico (sin plugins) | 5.12.8 | No | 400, JSON no válido |
Servidor nativo node:http o llamadas con http.get | 24.22.1 | No | Los bytes en bruto pasan sin procesarse |
fetch() integrado | 24.22.1 | Sí, siempre | Decodificó la respuesta |
| axios, got, node-fetch | 1.21.0, 16.2.0, 3.3.2 | Sí, por defecto | Decodificó la respuesta |
zlib.brotliDecompress*, createBrotliDecompress | Cualquiera | Sí | Es la propia API del decodificador |
Dos de los resultados merecen una atención especial. fetch() decodificó Brotli incluso cuando la solicitud enviaba Accept-Encoding: identity, dado que se guía por la cabecera de la respuesta y no por lo solicitado. Cualquier servidor externo al que llame tu aplicación puede alcanzar el decodificador, lo que deja expuestos servicios de prueba de webhooks, generadores de vistas previas de enlaces o importadores de URL. Además, una respuesta 400 por sí sola no garantiza nada: tanto Express 5 como Fastify sin plugins devuelven este código, pero solo Express 5 llegó a ejecutar el decodificador. Revisa siempre el mensaje de error.
Localiza tus puntos de exposición con estos tres comandos:
# Direct decoder use grep -rnE "brotliDecompress|createBrotliDecompress" src/ # Parsers that decode request bodies npm ls express body-parser @koa/bodyparser @fastify/compress # Base images and pinned versions grep -rnE "^FROM .*node" --include='Dockerfile*' . Una sola llamada adicional a fetch() puede cambiar por completo este escenario, por lo que estar «fuera de alcance» sigue exigiendo aplicar el parche.
Cómo solucionar CVE-2026-111497
Actualiza a la versión más reciente dentro de tu línea de lanzamiento, no te quedes en el umbral mínimo. Todas estas fuentes ya contaban con versiones corregidas el 5 de noviembre:
| Método de instalación | Acción recomendada |
|---|---|
| nvm | nvm install 24 && nvm alias default 24 |
| Homebrew | brew upgrade node (o node@24, node@22) |
| Docker | FROM node:24.22.2-slim y después docker build --pull |
| Paquetes de distribución Linux | Consulta los avisos de seguridad de tu distribución, ya que podría incorporar el parche manteniendo una numeración de versión anterior |
| CI | Actualiza .nvmrc, engines y node-version, y vuelve a compilar |
| Serverless | Registra process.version en los logs desde una función desplegada; no des por sentado que la plataforma lo haya actualizado |
Luego, asegúrate de resolver los dos descuidos más habituales:
- Reinicia todos los servicios. Un proceso en ejecución conserva el binario antiguo en memoria; por tanto, actualizarlo en disco no surte efecto hasta que pm2, systemd o tu orquestador lo reinicie.
- Reconstruye todo lo desplegado. Las imágenes antiguas mantienen el runtime vulnerable, y aquellas fijadas por digest requieren un escaneo de imágenes, ya que la etiqueta no te dirá si están parcheadas.
Para finalizar, ejecuta las comprobaciones indicadas al principio de este artículo dentro del contenedor desplegado, así como en los ejecutores de CI y en los entornos de desarrollo. Para consultar la versión de seguridad más reciente de cada línea:
curl -s https://nodejs.org/dist/index.json | jq -r '[.[] | select(.security == true and (.version | test("^v(22|24|26)\\.")))] | group_by(.version | split(".")[0]) | .[] | max_by(.date) | "\(.version) \(.date)"' ¿Sigues en Node.js 20 o 25? No existe ninguna versión corregida para ti. Consulta el plan de fin de ciclo de vida (EOL) de Node.js 20 y migra cuanto antes.
Medidas de mitigación en Node.js si no puedes actualizar hoy
Estas alternativas te permitirán ganar tiempo, pero ninguna sustituye a la actualización.
- Rechaza
bren el perímetro (edge). BloqueaContent-Encoding: bren tu proxy inverso o balanceador de carga, asegurándote de que la coincidencia no distinga mayúsculas ni minúsculas e incluya listas comogzip, br. - Recházalo en la aplicación. Coloca este middleware antes de los parsers del cuerpo de la petición. En nuestras pruebas con Express 5 respondió con un código 415:
app.use((req, res, next) => { const encoding = req.headers['content-encoding'] || ''; if (/\bbr\b/i.test(encoding)) return res.status(415).end(); next(); }); - Desactiva la descompresión automática. La configuración
express.json({ inflate: false })devolvió un 415 ante la misma petición. Ten en cuenta que también rechazará cuerpos comprimidos con gzip, así que verifica primero tus clientes. - Controla las respuestas salientes. axios admite
decompress: false, lo que mantuvo los bytes sin decodificar durante nuestras pruebas. En cambio,fetch()carece de un mecanismo estándar de desactivación, por lo que conviene limitar a qué hosts puede conectarse mediante listas blancas y reglas de egreso en el firewall. - Reduce el radio de impacto. Segmenta los servicios de Node.js y restringe el acceso de lectura a los volcados de memoria (core dumps), ya que ELLIO advierte que las caídas del sistema podrían dejar expuestos datos sensibles en memoria a través de ellos.
Mantente alerta ante el patrón inicial identificado por ELLIO: ráfagas de peticiones con cuerpos codificados en Brotli orientadas a mapear qué servidores los decodifican. Un tráfico inesperado con Content-Encoding: br en una API JSON o un aumento repentino en la caída de procesos worker justifican una investigación inmediata.
Comprueba que tu API resistió la actualización: deja que tu agente lo verifique
El cambio de versión del runtime puede alterar el comportamiento de TLS, DNS y HTTP; de hecho, la actualización de octubre introdujo cambios en los tres. No verifiques estas diferencias manualmente: delega en tu agente de desarrollo la ejecución de Apidog CLI antes y después del cambio para que reporte cualquier discrepancia.
npm install -g apidog-cli@latest apidog login --with-token <YOUR_TOKEN> npx -y skills add https://apidog.com # pick your agent when prompted Indícale a tu agente la siguiente instrucción:
Run Apidog test suite <id> in project <id> against staging and save the results to reports/before. After I upgrade Node.js, add a test case for POST /orders that sends the plain text "not-valid-brotli" with Content-Encoding: br, asserts a 4xx status, then checks GET /health returns 200. Rerun the suite to reports/after and list every request whose result changed. El agente ejecutará los siguientes comandos:
apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/before apidog cli-schema get test-case-create apidog cli-schema validate test-case-create --file case.json apidog test-case create --project <id> --file case.json apidog run --test-case <caseId> --project <id> -e <envId> -r cli,json apidog run --test-suite <id> --project <id> -e <envId> -r cli,junit --out-dir reports/after Ese caso de prueba adicional funciona como un health check, no como un exploit: el cuerpo es texto plano y, en nuestras pruebas, falló con el mismo error de relleno (padding) tanto en compilaciones vulnerables como corregidas. Aun así, permite detectar un problema real: en nuestra prueba con Koa obtuvimos un error 500 debido a que el fallo de decodificación no fue capturado, cuando una API pública debería responder con un 4xx. Ejecútalo únicamente en entornos de staging.
La referencia del comando run detalla cada opción disponible, y la guía de CI/CD te muestra cómo incorporar la misma ejecución con formato JUnit en tu pipeline. El plan gratuito de Apidog admite hasta 4 usuarios.
Por qué la fecha clave es el 11 de noviembre
El proyecto Node.js ha preanunciado una nueva tanda de actualizaciones para las líneas 26.x, 24.x y 22.x para el 11 de noviembre de 2026 o en fechas inmediatamente posteriores, calificadas nuevamente con severidad ALTA en el nivel máximo. Todavía no se han hecho públicos los CVE correspondientes. En nuestra guía sobre el lanzamiento de noviembre explicamos cómo prepararte.
Aplica el parche para Brotli de inmediato en lugar de posponerlo hasta ese nuevo lanzamiento. La vulnerabilidad ya cuenta con detalles públicos y tráfico de explotación activo en la actualidad; además, la actualización que realices esta semana servirá de ensayo para la siguiente: mismo inventario, misma reconstrucción de imágenes y la misma batería de pruebas. PHP2Shell y React2Shell siguieron un patrón idéntico.
Preguntas frecuentes (FAQ)
¿Se está explotando activamente CVE-2026-111497? Sí. CISA la añadió a su catálogo de vulnerabilidades explotadas conocidas (KEV) el 27 de octubre de 2026 bajo la entrada de Google Brotli, y ELLIO ya había registrado tráfico de exploits el 26 de octubre.
¿Es de severidad alta o crítica? Ambas calificaciones son válidas. Node.js la clasifica como ALTA. El NVD le otorga una puntuación de 9.8 CRÍTICA en CVSS 3.1, mientras que la puntuación emitida por Google en CVSS 4.0 es de 9.2. Planifica tus acciones considerando que ya está siendo explotada activamente.
Uso Express 4, ¿estoy a salvo? Los cuerpos de tus solicitudes entrantes no llegan al decodificador, pero las respuestas procesadas mediante fetch() o axios y las llamadas directas a node:zlib sí lo hacen. Además, esta misma versión corrige otras nueve vulnerabilidades (CVE), por lo que debes actualizar de todos modos.
¿Afecta a Node.js 20? El proyecto no evalúa versiones que han alcanzado el fin de su ciclo de vida (EOL) y no reciben parches. Considera que 20.x y 25.x están sin parchear y sin evaluar, y migra a una versión con soporte.
¿Afecta a otras tecnologías además de Node.js? Sí. El aviso de seguridad de GitHub incluye los paquetes de PyPI brotli y brotlicffi, y diversos proveedores ya han anunciado parches para Bun 1.4.4 y .NET. Actualiza cada decodificador de forma independiente, ya que actualizar Node.js solo protege su propia copia integrada.



