Qué ha pasado
El 21 de octubre, Node.js publicó una corrección de urgencia para un fallo crítico (CVSS 9.2) que permite a un atacante sin login ejecutar código a distancia. Lo apodaron «Node2Shell» (CVE-2026-111497), lo reportaron investigadores de OpenAI, y el 27 de octubre CISA lo metió en su catálogo de vulnerabilidades explotadas, con fecha límite para la administración de EE. UU. el 10 de noviembre.
Pero el nombre engaña, y esa es la parte importante: el fallo no es de Node. Está en el decoder de Brotli, la librería de compresión de Google que descomprime las respuestas Content-Encoding: br que usa una enorme parte de la web. Node solo es uno de los sitios donde vive. La misma librería vulnerable viaja también en Python, Bun, Envoy, .NET, y en la propia librería de Google que usan curl, Firefox y casi todas las distribuciones de Linux. Si algo en tu stack descomprime Brotli —y hay más de lo que crees—, esto te toca.
No es teoría: el primer intento de explotación registrado, el 26 de octubre, llegó como un único POST de 23 millones de bytes de Brotli trucado.
Míralo por dentro
Antes de los comandos, mira cómo funciona. Abajo tienes el ataque paso a paso: a la izquierda, lo que pasa; en el centro, lo que envía el atacante; a la derecha, lo que responde el servidor. Cámbialo a «Corregido» y verás dónde se corta la cadena.
Análisis · cómo funciona el fallo
Ejecutar código con un cuerpo comprimido: el fallo de Brotli (no solo en Node)
¿Me afecta? Compruébalo tú
No es solo Node: mira tu runtime, pero también Python, Bun, Envoy, .NET y la librería Brotli del sistema.
MagicBoxDesk · magicboxdesk.com
El mecanismo: el decoder confunde «roto» con «mándame más»
Esto es casi de manual de por qué la seguridad es difícil. Brotli descomprime datos leyéndolos por bloques. El atacante le manda un flujo trucado que declara un solo tipo de bloque donde debería haber varios, de forma que un cambio de bloque es imposible. Cuando al decoder se le acaba la cuenta y se topa con esa situación imposible, devuelve exactamente la misma señal que usa para decir «me falta input, mándame más».
Y ahí está la trampa: quien llama al decoder se cree que faltan datos, así que le pasa los siguientes bytes del atacante a un buffer interno de solo 8 bytes… sin comprobar el tamaño. Cada trozo nuevo se escribe pasado el final de ese buffer, pisando lo que haya detrás en la memoria del decoder: punteros, contadores, el tamaño del ring buffer. Al corromper ese puntero, las escrituras normales de salida se convierten en una escritura en la memoria que controla el atacante —y de ahí a filtrar datos, tumbar el proceso o ejecutar código—.
Dicho en una frase: el decoder se cree que el flujo roto es un flujo incompleto, y se queda esperando más basura que mete donde no cabe.
No es «cosa de Node»: dónde vive Brotli de verdad
Aquí está lo que casi nadie cuenta. Para que esto te afecte, el atacante solo necesita una de dos cosas —y ninguna requiere usuario ni contraseña:
- Que tu servicio reciba y descomprima un cuerpo comprimido. Un cliente manda un
POSTconContent-Encoding: bra una API en Express, Koa o similar, y el servidor lo descomprime. Entra por la puerta de delante. - Que tu servicio salga a buscar algo a un servidor del atacante. Un
fetch()a una URL que el atacante controla, que responde conContent-Encoding: brtrucado. Pasa en los sitios más tontos: webhooks, vistas previas de enlaces, unfurlers (eso que genera la tarjetita cuando pegas un enlace).
Y «Brotli» no es un rincón: es la compresión moderna de HTTP. La misma pieza vulnerable está en:
| Dónde | Corregido en |
|---|---|
| Node.js (22.x, 24.x, 26.x; EOL 20/23/25 sin parche) | 22.23.4 · 24.22.1 · 26.12.1 |
Python (Brotli / brotlicffi) | 1.2.1 / 1.2.1.0 |
| Bun | 1.4.4 |
| Envoy (con decompresor Brotli) | 1.40.0 / 1.39.3 / 1.38.6 / 1.37.8 / 1.36.12 |
| .NET 8/9/10 | 8.0.33 / 9.0.22 / 10.0.14 |
| Brotli de Google (curl, Firefox, distros…) | 1.0.10 / 1.1.1 / 1.2.1 |
Si gestionas cualquier cosa que hable HTTP moderno, lo más probable es que tengas Brotli metido en algún sitio sin haberlo elegido. Esos son los fallos que más cuestan: los de una pieza compartida que nadie recuerda que está ahí.
Cómo saber si te afecta
En Node, la comprobación es directa:
# versión del runtime
node --version
# ¿build parcheada? Devuelve un número si está corregido; "undefined" si es vulnerable
node -p "require('zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH"
Fuera de Node, mira la versión de la librería según la tabla de arriba (Python Brotli, Bun, Envoy, .NET, o la libbrotli del sistema). Y para detectar si lo han intentado, busca en tus logs: cuerpos Content-Encoding: br enormes (varios MB) en peticiones POST (el ataque real fueron 23 MB en una sola); un HTTP 400 con ERR__ERROR_FORMAT_BLOCK_SWITCH en un sistema ya parcheado; o procesos de Node que mueren con códigos de salida 134, 135 o 139 (señal de corrupción de memoria).
Qué hacer, por orden
- Actualiza el runtime: Node a 22.23.4 / 24.22.1 / 26.12.1 (o superior) y reinicia. Si estás en una rama EOL (20, 23, 25), no hay parche: toca saltar a una rama con soporte.
- No te olvides del resto del stack: Python (
Brotli1.2.1), Bun 1.4.4, Envoy, .NET, y lalibbrotlidel sistema. La misma pieza, varios sitios. - Mientras actualizas, limita el daño: pon un tope de tamaño a los cuerpos comprimidos que aceptas, y desconfía de lo que tu servicio descarga solo (webhooks, previews) de destinos que no controlas.
- Revisa que no te tocaron con las señales de arriba, sobre todo si tienes APIs que aceptan
POSTcomprimidos abiertos a internet.
La lección que no caduca
Node2Shell es el ejemplo perfecto de por qué un fallo con nombre pegadizo puede despistar. El titular dice «Node», pero el agujero está en una librería de compresión que se comparte entre medio ecosistema, y que la mayoría ni sabe que tiene corriendo. La seguridad moderna no va solo de tu código: va de las piezas que arrastras sin elegir, las que vienen dentro de otra cosa.
Por eso lo de siempre: saber qué piezas corren dónde y en qué versión —no solo tus aplicaciones, también sus tripas— es lo que separa taparlo en una tarde de enterarte por un informe de incidente. Esa foto, que casi nadie tiene, es la diferencia.
Si no sabes dónde tienes Brotli metido —en tus servidores, tus APIs o las webs de tus clientes—, escríbenos y lo miramos contigo. Es media hora, y te quedas sabiendo exactamente de qué lado estás.
Fuentes
Todo lo que se afirma aquí sale de estas fuentes. Si solo vas a abrir una, que sea la primera.
- NVD — CVE-2026-111497, la ficha oficial (CVSS 9.2). La fuente primaria.
- CISA — Catálogo de vulnerabilidades explotadas (KEV), donde entró el 27 de octubre con plazo federal el 10 de noviembre.
- SecPod — anatomía del fallo: el buffer de 8 bytes y la confusión «roto / mándame más».
- Sophos — Node2Shell en explotación activa.
- SecurityWeek — CISA avisa y los honeypots registran los ataques.
- Rescana — versiones corregidas de Node, Python, Bun, Envoy y .NET.
De la misma serie
- OpenSSH «sshole»: entrar por SSH sin saber la contraseña
- WordPress: cuando actualizar es, justo, lo que te deja entrar
- Cuatro avisos críticos en ocho días: nginx, el kernel, PHP y Node
Si no sabes qué versiones corren en tus servidores o en las webs de tus clientes, en MagicBoxDesk lo revisamos contigo: ciberseguridad y mantenimiento web gestionados. 📍 Alicante y Barcelona · hablemos 30 minutos.
