Vulnerabilidad de Node.js: parche de Node2Shell y cómo comprobar la versión de Node.js (WordPress)

Aviso de afiliados v3.1
Aviso de afiliados v3.2 (móvil)
Entorno de prueba Familia RHELAlmaLinux 9.8Nginx + Apache · WordPress 7.1.3Verificado el 2026-10-23

Node2Shell es el apodo de CVE-2026-111497, una vulnerabilidad de Node.js cuyo parche se publicó el 21 de octubre de 2026; su título oficial describe un fallo de escritura fuera de límites en el decodificador Brotli de Google. Como descomprimir datos Brotli manipulados puede derivar en ejecución remota de código, lo primero es comprobar las versiones de Node.js, Bun y libbrotli de tu servidor y actualizarlas a las versiones parcheadas.

En un servidor Nginx + Apache con WordPress 7.1.3, comprobé qué paquetes usan el decodificador Brotli y por qué ruta pasan por libbrotli las peticiones remotas de WordPress.

1️⃣ Node2Shell, la vulnerabilidad de Node.js: cómo funciona el fallo y a qué afecta

El nombre Node2Shell es un apodo que le puso el equipo de investigación de la empresa de seguridad Imperva en un artículo del 22 de octubre. El título del aviso de nodejs.org es [Brotli decoder out-of-bounds write permits remote code execution]. El fallo está en el código fuente del decodificador Brotli de Google que incluye Node.js (c/dec/decode.c), así que no se soluciona cambiando el código de tu aplicación.

Según el registro de NVD, el decodificador de Google Brotli 1.0.3 a 1.2.0 escribe más allá del final del búfer al descomprimir un flujo manipulado y corrompe la memoria de estado del decodificador adyacente. Con este fallo, un atacante remoto sin iniciar sesión puede leer el contenido de la memoria del servidor, interrumpir el servicio y ejecutar código arbitrario.

Hay dos valoraciones de gravedad. El aviso de nodejs.org le asignó el nivel High, y Google, que emitió el CVE, le dio 9,2 (Critical) según CVSS 4.0. A fecha del 23 de octubre, NVD aún no ha hecho su propio análisis, así que todavía no hay puntuación de NVD. Los tipos de debilidad son un valor de retorno sin comprobar (CWE-252) y una escritura fuera de límites (CWE-787).

Según el aviso de seguridad de nodejs.org, todas las ramas con soporte de Node.js (22.x, 24.x y 26.x) están afectadas, y las versiones parcheadas son 22.23.4, 24.22.1 y 26.12.1. El mismo aviso indica que las ramas sin soporte se consideran siempre afectadas cada vez que se publica un aviso de seguridad.

Cualquier programa que use el mismo decodificador está afectado, también fuera de Node.js. El 21 de octubre, Google publicó tres versiones de la biblioteca Brotli que corrigen el mismo CVE: 1.0.10, 1.1.1 y 1.2.1. Bun incluyó Brotli en la lista de correcciones de seguridad del anuncio de la 1.4.4 el 22 de octubre, y una issue de su repositorio indica que esa versión contiene la corrección que actualizó brotli.

destinoVersiones afectadasVersión corregidaFuente
Node.jsTodas las ramas con soporte 22.x · 24.x · 26.x · ramas sin soporte22.23.4 · 24.22.1 · 26.12.1Aviso de seguridad de nodejs.org (2026-10-21)
Biblioteca Brotli de Google1.0.3 ~ 1.2.01.0.10 · 1.1.1 · 1.2.1Registro de NVD · versiones de google/brotli (2026-10-21)
BunBrotli en la lista de correcciones de seguridad del anuncio de la 1.4.41.4.4Anuncio de Bun (2026-10-22)
Paquete libbrotli de la distribuciónPaquetes compilados a partir de Brotli 1.0.3 a 1.2.0La versión que publique el aviso de seguridad de la distribución (aún no hay a fecha de 2026-10-23)Página del rastreador de seguridad de cada distribución

Node.js por sí solo no descomprime los datos Brotli que recibe. Según el aviso de seguridad de la empresa de hosting Vercel, los puntos expuestos son aquellos en los que una aplicación o biblioteca descomprime datos comprimidos que llegan de fuera. Por ejemplo, cuando el fetch() integrado recibe una respuesta br y cuando Express 5 o body-parser 2 descomprimen el cuerpo de una petición br; el servidor HTTP predeterminado, que solo recibe, no descomprime.

A fecha del 23 de octubre no se ha confirmado ninguna explotación real. Este CVE no figura en el catálogo de vulnerabilidades explotadas conocidas de CISA (KEV, versión del 21 de octubre). El nivel de explotación que CISA asigna en el registro del CVE cambió el 23 de octubre a [prueba de concepto (PoC) pública].

Dónde está el decodificador determina lo que tienes que hacer. Los binarios oficiales de Node.js llevan el decodificador integrado en el ejecutable, y también hay compilaciones que usan la libbrotli del sistema, como el paquete nodejs de Debian. Las distribuciones de Linux instalan el mismo código fuente como paquete libbrotli, que comparten programas como curl.

2️⃣ Los sitios WordPress y PHP también están afectados: la ruta por la que las peticiones remotas descomprimen respuestas con libbrotli

Incluso un servidor WordPress sin Node.js tiene el mismo decodificador instalado como paquete libbrotli. A fecha del 23 de octubre no hay ninguna mención oficial de este CVE por parte de WordPress, curl ni PHP, y tampoco figura en la lista oficial de vulnerabilidades de curl. Por eso esta sección solo confirma la estructura con el código del núcleo, la documentación de curl y mediciones en un servidor de pruebas.

En el servidor Nginx + Apache que medí, cinco paquetes requerían la biblioteca del decodificador (libbrotlidec), y el que conecta con WordPress es libcurl.

rpm -q --whatrequires 'libbrotlidec.so.1()(64bit)' | sed 's/-[0-9].*//'
freetype
brotli-devel
libjxl
libcurl
libsoup

libcurl requiere libbrotli para descomprimir las respuestas HTTP comprimidas. Según la documentación oficial de curl, libcurl compilado con la biblioteca brotli descomprime automáticamente las respuestas cuyo Content-Encoding es br. La extensión curl de PHP llama directamente a ese libcurl.

ldd /usr/lib64/libcurl.so.4 | grep brotli
    libbrotlidec.so.1 => /lib64/libbrotlidec.so.1 (0x00007f32803ed000)
    libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f327f858000)
php -i | grep BROTLI
BROTLI => Yes

La versión de libcurl que usa WordPress aparece en la fila [Versión de cURL] al desplegar la sección [Servidor] en la pestaña [Información] de [Herramientas › Salud del sitio] del panel de administración. Esa fila no muestra si hay soporte de brotli, así que compruébalo con comandos del servidor como arriba.

Pantalla de la pestaña Información de Salud del sitio: menú izquierdo Herramientas › Salud del sitio y filas Arquitectura del servidor, Servidor web, Versión de PHP, PHP SAPI y Versión de cURL de la sección Servidor — se comprueba en la fila Versión de cURL la versión de libcurl, la ruta del lado de WordPress de la vulnerabilidad de Node.js Node2Shell (WordPress 7.1.3)

La fila [Versión de cURL] de la imagen muestra la versión de libcurl junto con la biblioteca SSL con la que se compiló. Según la documentación de curl, br es compatible desde la 7.57.0, así que si este valor es menor, no puede descomprimir respuestas br. Si es igual o mayor, comprueba con comandos del servidor si la compilación incluye brotli.

Cuando la biblioteca Requests del núcleo de WordPress hace una petición a una URL externa, establece el valor [CURLOPT_ENCODING] del manejador de curl como una cadena vacía. Según la documentación de curl, una cadena vacía significa aceptar todos los métodos de compresión que admite ese libcurl. Por eso, si el otro servidor envía una respuesta comprimida con br, la libbrotli del servidor WordPress la descomprime.

grep -n CURLOPT_ENCODING wp-includes/Requests/src/Transport/Curl.php
110:            curl_setopt($this->handle, CURLOPT_ENCODING, '');
212:            curl_setopt($this->handle, CURLOPT_ENCODING, 'none');

La línea 110 es el valor predeterminado de todas las peticiones, y la línea 212 solo se ejecuta cuando una petición que no pudo descomprimirse se reenvía sin compresión. No hay forma de atacarla directamente desde fuera: primero tendría que estar comprometido un servidor del que WordPress recibe respuestas, como en las comprobaciones de actualizaciones del núcleo y los plugins, oEmbed o los feeds RSS.

🧩 Flujo del ataque Node2Shell
Diagrama explicativo
①Datos comprimidos que llegan de fueraUn cuerpo de petición comprimido con br o la respuesta de otro servidor
②La aplicación los descomprime con el decodificador BrotliNode.js fetch() · Express 5 · Bun fetch() · WordPress mediante libcurl → libbrotli
③Fallo del decodificador BrotliUn flujo manipulado escribe más allá del final del búferCVE-2026-111497
④Ejecución de código en el servidorFuga de memoria · interrupción del servicio · toma del servidor
✅ Las versiones corregidas bloquean este flujoNode.js 22.23.4 · 24.22.1 · 26.12.1Bun 1.4.4Brotli 1.0.10 · 1.1.1 · 1.2.1

El flujo empieza con datos comprimidos con br que llegan de fuera. El fallo se activa en el paso en que el fetch() de Node.js, Express 5, el fetch() de Bun o la libbrotli llamada por el libcurl de WordPress los descomprime. El resultado lleva a la ejecución de código en el servidor. Las versiones parcheadas bloquean este flujo, así que lo que hay que hacer es cambiar el decodificador por una versión parcheada.

El mod_brotli de Apache solo enlaza la biblioteca del codificador (libbrotlienc), que comprime las respuestas, así que una configuración que activa la compresión con este módulo no está en la ruta del fallo del decodificador.

ldd /usr/lib64/httpd/modules/mod_brotli.so | grep brotli
    libbrotlienc.so.1 => /lib64/libbrotlienc.so.1 (0x00007f34b4b79000)
    libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f34b4a79000)

3️⃣ Comprobar la versión de Node.js: si el servidor tiene Node.js, Bun y libbrotli, y en qué versión

Lo primero es hacer inventario de lo que hay instalado en el servidor. En Node.js, mira la versión con [node -v]; si el valor es inferior a 22.23.4, 24.22.1 o 26.12.1 según su rama, está afectado.

Tu entornoComando de comprobaciónQué se corrige
Instalador de nodejs.org · nvm · imagen oficial de Docker[node -v]El propio Node.js (22.23.4 · 24.22.1 · 26.12.1)
nodejs de la distribución · línea libbrotli en ldd[ldd $(command -v node)]Paquete libbrotli
nodejs de la distribución · resultado de ldd 0[ldd $(command -v node)]Paquete nodejs (tras el aviso de seguridad de la distribución)
Node.js Selector del hosting compartido (CloudLinux alt-nodejs)Anuncio del proveedor · número de release del paquetePaquete distribuido por el proveedor + reinicio de la aplicación
Bun[bun --version]Bun 1.4.4 o posterior
Sin Node.js (solo WordPress)Comprobar libcurl con [ldd]Paquete libbrotli + reinicio de php-fpm · hasta entonces, medida temporal
Hosting gestionadoAnuncio del proveedor · consultaPaquetes que actualiza el proveedor

En un PC (Windows o macOS), reinstala con el instalador de la versión parcheada de nodejs.org; con nvm, mira la versión con [nvm ls] y vuelve a instalar por número de rama, como [nvm install 24]. Las etiquetas 22, 24 y 26 de la imagen oficial node de Docker se reconstruyeron con las versiones parcheadas el 22 de octubre, así que descarga de nuevo la imagen, compila y despliega. El aviso de Vercel también recomienda actualizar a las versiones parcheadas las imágenes de contenedor que compilas tú, los entornos de CI y los entornos de desarrollo locales.

En un servidor donde instalaste Node.js con un stream de dnf module, [node -v] muestra una versión de esa rama; aunque la rama coincida, actualiza si los últimos dígitos no llegan a la versión parcheada.

node -v
v24.xx.x

Usa [ldd] para comprobar si Node.js usa la libbrotli del sistema o su decodificador integrado. Si la salida tiene una línea libbrotlidec, es una compilación que usa la biblioteca compartida; si da 0, como abajo, el decodificador va integrado y solo se corrige actualizando el propio Node.js.

ldd $(command -v node) | grep -c brotli
0

En Bun, comprueba la versión con [bun --version] y actualiza si es inferior a 1.4.4.

En los sistemas basados en RPM, mira la versión del paquete libbrotli con [rpm -q libbrotli]. Los números de versión de los paquetes los asigna la distribución, así que no los compares directamente con los números de versión de Google, sino con la versión que publica el aviso de seguridad de la distribución para este número de CVE.

rpm -q libbrotli
libbrotli-1.0.x-x.el9_x.x86_64

Comprueba si la distribución ha publicado un aviso con [dnf updateinfo info --cve CVE-2026-111497]. Si la salida está vacía, ese repositorio aún no tiene un aviso para este CVE.

dnf updateinfo info --cve CVE-2026-111497

Un paquete que solo incorpora la corrección sin cambiar el número de versión deja el número de CVE en su registro de cambios. Por eso se comprueba con [rpm -q --changelog libbrotli | grep CVE-2026-111497]; si aparece una línea, el paquete está parcheado.

rpm -q --changelog libbrotli | grep CVE-2026-111497

A fecha del 23 de octubre, los paquetes libbrotli de Debian, Ubuntu, Red Hat y AlmaLinux aún no tienen una versión parcheada para este CVE. Alpine solo tiene versión parcheada del paquete nodejs; la fuente es la página del rastreador de seguridad de cada distribución. Por eso, ahora mismo el orden es estar atento a que salga el aviso de seguridad de la distribución y actualizar cuando salga.

En Debian y Ubuntu el paquete se llama libbrotli1, y comprobé los mismos elementos en un servidor de pruebas con Debian 12 de un hosting gestionado. En este servidor también libcurl enlazaba libbrotlidec y la extensión curl de PHP admitía brotli. El registro de cambios se consulta con [zcat /usr/share/doc/libbrotli1/changelog.Debian.gz | grep CVE-2026-111497].

dpkg -l libbrotli1 | tail -1
ii  libbrotli1:amd64 1.1.0-2+0~20240305.8+debian12~1.gbp62df71 amd64        library implementing brotli encoder and decoder (shared libraries)
ldd /usr/lib/x86_64-linux-gnu/libcurl.so.4 | grep brotli
    libbrotlidec.so.1 => /lib/x86_64-linux-gnu/libbrotlidec.so.1 (0x00007fa2baac6000)
    libbrotlicommon.so.1 => /lib/x86_64-linux-gnu/libbrotlicommon.so.1 (0x00007fa2b9cb9000)

4️⃣ Actualizar Node.js: subir a la versión parcheada y volver a comprobar

En los hostings gestionados y compartidos, el proveedor actualiza los paquetes del servidor, así que confirma con sus avisos o con soporte si ya se aplicó el parche de Node.js y libbrotli. Si administras tú el servidor, actualiza Node.js a la versión parcheada del aviso de nodejs.org y Bun a la 1.4.4 o posterior con [bun upgrade].

El Node.js instalado desde un stream de módulos de la distribución se actualiza con algo como [dnf upgrade nodejs] cuando la versión parcheada llega a ese repositorio. En un servidor donde descargaste e instalaste Node.js directamente desde nodejs.org, sustitúyelo por la versión parcheada del mismo sitio. Las ramas sin soporte no reciben versión parcheada, así que pásate a una rama con soporte: 22, 24 o 26.

El Node.js Selector del hosting compartido (CloudLinux alt-nodejs) lleva su propia copia de brotli, así que actualizar la libbrotli del sistema no lo corrige. Según el anuncio de CloudLinux, el nuevo binario solo se ejecuta cuando ya está el paquete distribuido por el proveedor y se reinicia la aplicación. El mismo anuncio indica que en las versiones sin soporte el valor de [node --version] no cambia, así que se comprueba con el número de release del paquete.

Con el paquete libbrotli, cuando salga el aviso de seguridad de la distribución, actualiza ese paquete y reinicia los procesos que tienen la biblioteca cargada en memoria para que pasen a la nueva biblioteca. Las bibliotecas compartidas se leen cuando arranca un proceso, así que en un servidor WordPress el reinicio de php-fpm forma parte del mismo paso.

Después de actualizar, comprueba que [node -v] muestra la versión parcheada o posterior y que [rpm -q libbrotli] muestra la versión del aviso de la distribución. Comprueba si el número de CVE está en el registro de cambios con [rpm -q --changelog libbrotli | grep CVE-2026-111497]. Si reiniciaste php-fpm, revisa también el estado del servicio y la respuesta del sitio.

systemctl is-active php-fpm
active
curl -s -o /dev/null -w '%{http_code}\n' 'https://example.com/?r=1'
200

5️⃣ Medida temporal antes del parche: desactivar la descompresión br en las peticiones remotas de WordPress

La página del CVE de Red Hat recomienda configurar las aplicaciones para que no acepten, o no descompriman automáticamente, compresión br procedente de fuentes no fiables.

Aunque todavía no haya paquetes parcheados, ya puedes bloquear la ruta del lado de WordPress. WordPress ofrece el hook [http_api_curl], que pasa el manejador de curl en cada petición remota. Si ahí pones [CURLOPT_ENCODING] a null, se desactiva la descompresión automática de libcurl.

Si colocas el archivo de abajo en la carpeta [wp-content/mu-plugins/], se aplica a todas las peticiones remotas; el nombre del archivo es solo un ejemplo. Para deshacerlo, borra este archivo.

<?php
// no-brotli-decode.php — WordPress remoto en las peticiones libcurl Automático descompresión Desactivado(libbrotli parche versión publicada hasta)
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}
add_action( 'http_api_curl', function ( $handle ) {
    curl_setopt( $handle, CURLOPT_ENCODING, null );
}, 99 );

Es fácil pensar que limitar [CURLOPT_ENCODING] a [gzip, deflate] deja fuera br. Ese valor solo quita br de la cabecera Accept-Encoding de la petición, así que si el otro servidor responde con br, libcurl lo descomprime tal cual lo recibe. En el sitio de pruebas, al recibir httpbin.org/brotli con ese mismo valor, la respuesta con Content-Encoding br se descomprimió y se pudo leer.

Según la documentación de curl, el único valor que desactiva la descompresión automática es null; con null no se envía la cabecera Accept-Encoding y tampoco se descomprime el cuerpo recibido. Las respuestas gzip y deflate las descomprime directamente la biblioteca Requests de WordPress y se leen igual; solo las respuestas br quedan sin descomprimir.

Este hook es el que WordPress conectó al punto [curl.before_send], al que Requests llama justo antes de ejecutar curl. Se llama en la línea 174 de Curl.php y curl se ejecuta en la 205, así que el valor de cadena vacía que el núcleo puso en la línea 110 se sobrescribe entre medias.

Comprueba si se aplica con el archivo de prueba de abajo. httpbin.org es una dirección de pruebas que devuelve las cabeceras de la petición recibida y también respuestas comprimidas con br y gzip, así que se ve de una vez qué envía WordPress y qué descomprime.

<?php
// br-check.php — WordPress remoto petición qué compresión envía si descomprime Comprobar(httpbin.org prueba Dirección)
$h = json_decode( wp_remote_retrieve_body( wp_remote_get( 'https://httpbin.org/headers' ) ), true );
echo 'Accept-Encoding: ', isset( $h['headers']['Accept-Encoding'] ) ? $h['headers']['Accept-Encoding'] : '(none)', PHP_EOL;
foreach ( array( 'brotli', 'gzip' ) as $p ) {
    $r = wp_remote_get( 'https://httpbin.org/' . $p );
    $readable = strpos( wp_remote_retrieve_body( $r ), ': true' ) !== false;
    echo $p, ': Content-Encoding=', wp_remote_retrieve_header( $r, 'content-encoding' ), ' / ', strlen( wp_remote_retrieve_body( $r ) ), ' bytes / ', ( $readable ? 'readable' : 'not decoded' ), PHP_EOL;
}

Antes de añadir el archivo, Accept-Encoding incluye br y la respuesta br se descomprime y se puede leer.

wp eval-file br-check.php
Accept-Encoding: deflate, gzip, br
brotli: Content-Encoding=br / 316 bytes / readable
gzip: Content-Encoding=gzip / 317 bytes / readable

Después de añadir el archivo, desaparece la cabecera Accept-Encoding, la respuesta br queda sin descomprimir y la respuesta gzip se sigue leyendo.

wp eval-file br-check.php
Accept-Encoding: (none)
brotli: Content-Encoding=br / 199 bytes / not decoded
gzip: Content-Encoding=gzip / 272 bytes / readable

Cuando hayas actualizado al paquete libbrotli parcheado de la distribución y reiniciado php-fpm, borra este archivo para volver a la configuración original.

🔢 FAQ & contenido recomendado

Las actualizaciones automáticas de WordPress solo abarcan el núcleo, los plugins, los temas y las traducciones; libbrotli y Node.js son paquetes instalados en el servidor, así que los actualiza el administrador del servidor o el proveedor de hosting. Solo cuando se han actualizado y se ha reiniciado php-fpm, las peticiones remotas de WordPress pasan a procesarse con la nueva biblioteca.

Tanto si un plugin pasa por la WordPress HTTP API como si llama directamente a funciones curl, pasa por el mismo libcurl. Lo que hay que corregir es el paquete libbrotli del servidor, y hasta que salga la versión parcheada puedes desactivar la descompresión automática de las peticiones remotas de WordPress con la configuración del apartado 5️⃣. Según la documentación de curl, el código que llama directamente a funciones curl sin establecer [CURLOPT_ENCODING] no descomprime automáticamente las respuestas comprimidas. El código que las llama directamente y lo establece como cadena vacía queda fuera del hook [http_api_curl], así que revisa por separado solo las peticiones de ese plugin.

Los registros de CVE-2026-111497 en NVD y cve.org (CNA Google) recogen la ubicación del fallo (c/dec/decode.c), las versiones afectadas y el tipo de debilidad. Las notas de las versiones v1.2.1, v1.1.1 y v1.0.10 de google/brotli dejan constancia de la corrección. Los detalles para reproducir el ataque no se han incluido en este artículo.

ℹ️ Aviso de afiliados
El contenido de este sitio incluye enlaces de afiliado. Cuando un visitante compra un producto o servicio a través de uno de ellos, el sitio recibe una comisión del vendedor. El importe que paga el comprador(baja durante las promociones ↓)no aumenta. Los precios, descuentos y existencias publicados corresponden al momento de redacción y pueden variar, así que confírmalos con el vendedor antes de comprar. Los productos se eligen y evalúan con criterios propios, y las comisiones no influyen en el orden ni en el contenido de las reseñas.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Demuestra que eres humano: 6   +   7   =