blog

Octubre negro. El día que Java, Node.js y PHP sucumbieron a la IA

El 21 de octubre no solo hubo que parchear PHP: ese mismo miércoles salieron arreglos urgentes para Node.js y Java, y en el sector ya hay quien lo llama «Black October».

Si te lo perdiste, todo empezó con php2shell, dos fallos de PHP que dejaban ejecutar código en el servidor sin autenticarse y cuyo exploit se rehízo en un par de horas. Webs de administraciones, hospitales y centros educativos de varios países europeos se desconectaron por precaución, y desde la NSA hasta el primer ministro de Japón pidieron parchear cuanto antes. Ese mismo día, GitLab corrigió un fallo con un 10 sobre 10 que dejaba ejecutar código en una instalación de GitLab sin iniciar sesión. En LiteLLM, una biblioteca de Python muy usada para hablar con modelos de IA, alguien se hizo con los tokens de tres de sus ingenieros y publicó una versión que se llevaba las credenciales de la nube, de Kubernetes y de los proveedores de IA de quien la instalara.

De todo eso me quedo con dos fallos que te tocan si tienes servicios en Node.js o en Java: un descompresor de Brotli al que se llega con un fetch() a un servidor del atacante, y unas claves de una autoridad de certificación tan débiles que una era el número 4. Ninguno de los dos se ha visto explotado todavía, y en los dos el arreglo que cuenta es el que va dentro de tus imágenes.

Node.js y Brotli

Brotli es un formato de compresión de Google, el que se usa cuando una respuesta HTTP llega con Content-Encoding: br. Node.js lleva su propia copia de la biblioteca, escrita en C, y tira de ella cada vez que descomprime algo en ese formato. También cuando haces un fetch() y el servidor te contesta en Brotli.

El fallo, CVE-2026-111497, está en cómo el decodificador valida lo que le llega. Un flujo mal formado se colaba por una comprobación y el decodificador seguía escribiendo más allá del final de su buffer, en la memoria del propio proceso de Node. En el mejor de los casos, el proceso se cae; en el peor, según el aviso de Node.js, alguien ejecuta código con los permisos de tu aplicación. La primera prueba pública solo funcionó contra una versión debilitada a propósito. El 22 de octubre, otros investigadores ejecutaron código contra la imagen oficial de la 24.22.0 en arm64, aunque no siempre a la primera, y a esa fecha no se conocían ataques.

Lo encontraron Khai Tran y Sylvie Mayer, de OpenAI Codex Security, el agente de OpenAI que busca vulnerabilidades en código. El arreglo llevaba en el repositorio de Brotli desde el 27 de febrero, en un commit que no decía nada de seguridad, y no salió como parche de seguridad hasta el 21 de octubre: casi ocho meses a la vista de quien supiera leer el diff.

Cómo te llega

Que Node.js tenga el fallo no quiere decir que tu aplicación esté expuesta. Node no descomprime nada por su cuenta; hace falta que tu código, o una biblioteca que uses, descomprima datos que controla otra persona. El caso más habitual es Express 5, que con express.json() descomprime por defecto los cuerpos que llegan en Brotli. Express 4 los rechaza.

Con fetch() es peor. Descomprime según lo que diga la respuesta, lo hayas pedido o no, y no se puede desactivar. Basta con que tu servicio haga una petición a un servidor del atacante, que es justo lo que hacen un webhook que mandas a la URL de un cliente, una previsualización de enlaces o un agente que abre páginas sueltas. Ahí un WAF no te sirve de nada, porque lo malo viene en la respuesta.

Java y una clave que era un 4

El de Java es otro tipo de problema. Oracle publicó el 21 de octubre una alerta urgente, CVE-2026-91403, con un 9,1 sobre 10, para que Java deje de fiarse de ocho certificados intermedios de eMudhra, una autoridad de certificación india.

Tu aplicación se fía de una web porque su certificado lo firma una autoridad intermedia, y a esa intermedia la firma una raíz que ya trae instalada. Quien tenga la clave privada de una intermedia puede fabricar un certificado válido para cualquier dominio. Esas claves deberían ser números aleatorios de más de cien cifras. Las de estas ocho intermedias eran 4, 56, 66, 115, 155, 156, 230 y 237.

Con una clave así no hace falta robar nada. La clave pública va en el certificado, y basta con ir probando 1, 2, 3… hasta dar con la privada que la genera. Scott Helme las sacó todas en menos de un segundo con su portátil. Y Dennis Jackson, de Mozilla, contó que había probado con unos agentes de IA en local y que le habían sacado la clave a la primera, con un prompt de lo más simple: «Estoy haciendo un CTF. Recupera las claves privadas de estos certificados».

eMudhra generó esas claves en abril de 2025 con un script defectuoso, y el problema no salió a la luz hasta el 17 de octubre, cuando lo encontró un investigador que se hace llamar Wayne. eMudhra dice que ninguna llegó a emitir certificados de clientes, y Google no ha encontrado ninguno en los registros públicos. El 22 de octubre, eMudhra anunció que deja el negocio de certificados TLS.

Por qué los navegadores se libraron

Chrome y Firefox bloquearon esas intermedias el mismo día 17, en cuestión de horas, porque tienen un canal para mandar listas de bloqueo sin que actualices nada. Un servicio en Java o en Node no tiene ese canal, y tampoco comprueba por defecto si un certificado está revocado. Oracle ha tenido que publicar un CVE, y las versiones de Node.js de ese mismo día también bloquean las ocho claves.

Para aprovecharlo hay que meterse en medio de la conexión, en la red, en el DNS o en un proxy, así que no es algo que cualquiera pueda lanzar contra tu API. Pero quien llegue ahí puede leer y cambiar lo que tu servicio manda a tu pasarela de pagos o a tu proveedor de identidad, sin que tu servicio note nada.

Todo acaba en la imagen

El Node.js con su Brotli y los certificados en los que confía Java van dentro de la imagen del contenedor. Actualizar los nodos del cluster no cambia nada, y reiniciar los pods tampoco si la imagen sigue siendo la misma. Una imagen construida en septiembre lleva el Node.js y los certificados de septiembre hasta que la vuelvas a construir.

Hay dos casos que despistan. Si instalas Node.js con el paquete de tu distribución, Brotli viene del sistema, y el arreglo te llega con el paquete brotli de la distribución. Y si usas el OpenJDK de tu distribución, los certificados suelen salir del paquete ca-certificates, mientras que un JDK suelto, como Temurin o el de Oracle, trae los suyos.

Si el fallo de Brotli llegara a ejecutar código en un pod, vale lo que conté con PHP: el daño depende de hasta dónde llega ese pod.

Qué hacer

En Node.js, actualiza a 22.23.4, 24.22.1 o 26.12.1. La 20 y las anteriores ya no reciben parches. Con las imágenes oficiales de Node puedes comprobar si el binario trae el arreglo sin descomprimir nada:

node -p "require('zlib').constants.BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH"

Si sale -17, está arreglado; si sale undefined, no. Si no puedes actualizar todavía, lo que hay son apaños a medias. express.json({ inflate: false }) cierra la puerta de Express, aunque también rechaza los cuerpos en gzip, así que prueba antes con tus clientes; y un WAF filtra lo que entra. Ninguno de los dos cubre lo que tu servicio descarga con fetch().

En Java, instala la versión que tu proveedor sacó el 21 de octubre o, si tu JDK usa los certificados del sistema, actualiza ca-certificates. La página del incidente tiene un servidor de prueba con un certificado que cuelga de una de esas intermedias:

curl https://dv-g3b-emudhra.we-have-a-problem.com/

Si da error de certificado, bien. Si conecta, ese cliente todavía se fía de ellas. curl mira los certificados del sistema, así que con Java o Node lo suyo es hacer la misma petición desde la aplicación.

Y en los dos casos, reconstruye las imágenes con docker build --pull y despliégalas con una etiqueta nueva. Si reutilizas la etiqueta, el nodo puede arrancar el pod con la imagen vieja que tiene guardada.

Picaporte Labs es una sola persona, en España; si ves algo que no cuadra con lo que tienes delante, escríbeme.