Tornar al Blog

El teu inventari no té una línia per a Brotli

El teu inventari no té una línia per a Brotli

El 24 d'octubre algú de fora de Microsoft es va llegir el descodificador empaquetat a .NET 9.0.21 i 10.0.13, va veure que conservava el comportament previ a la correcció i va obrir una incidència titulada amb una pregunta: «Afecta CVE-2026-111497 a System.IO.Compression.Brotli a .NET 9 i 10?». Microsoft va contestar l'endemà i el 25 ja hi havia compilacions corregides. Un tercer va haver de llegir el codi de l'entorn d'execució per preguntar al fabricant si el seu propi producte estava trencat, i va encertar.

La fallada, amb el número al davant

CVE-2026-111497. Escriptura fora de límits al descodificador Brotli de Google, el que descomprimeix el Content-Encoding: br que viatja per mitja web. Google, com a entitat assignadora, li va posar un 9,2 sobre 10 en CVSS 4.0, amb el vector AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H: per xarxa, sense privilegis, sense interacció de l'usuari. La classificació tècnica és CWE-787 —escriptura fora de límits— arrelada en CWE-252: un valor de retorn que ningú no va comprovar.

L'avís de Node.js ho explica així: «un canvi de bloc invàlid es reportava com una necessitat temporal de més entrada en lloc de com un error permanent de format, de manera que el descodificador en streaming continuava afegint bytes d'entrada a una memòria intermèdia interna de vuit bytes». Vuit bytes. El descodificador creu que li falta material, en demana més, i continua escrivint passat el final. D'aquí surten, per aquest ordre: corrupció de memòria, fuita del contingut del procés, caiguda i —en les condicions de monticle adequades— execució de codi.

El va trobar l'equip de seguretat d'OpenAI Codex —Khai Tran i Sylvie Mayer— i el van arreglar a Node Matteo Collina i Antoine du Hamel. Riu amunt, el projecte Brotli el va corregir a les versions 1.0.10, 1.1.1 i 1.2.1; afectava tot el que hi hagués entre 1.0.3 i 1.2.0. Aquesta forquilla cobreix tres branques majors i tot el que hi ha entremig.

Si vols veure la causa arrel sense intermediaris, el commit de correcció riu amunt es diu «Detect and report invalid stream earlier» i el que fa és revelador: afegeix un codi d'error que no existia, BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH, i canvia tres funcions de canvi de bloc —literal, ordre i distància— perquè retornin codis d'error en lloc d'un simple sí o no. Aquesta és la història sencera en una línia de diff: «canvi de bloc malformat» no tenia manera de dir-se, així que el descodificador deia l'única cosa que sabia dir, que era «dona'm més dades».

I ara la dada que reordena tota la resta. Aquell commit està datat el 27 de febrer del 2026. Vuit mesos abans que la fallada es fes pública, la correcció ja era a la branca principal de Brotli, a la vista de qualsevol. El que es va publicar el 21 d'octubre no va ser l'arranjament: va ser l'avís. Durant aquells vuit mesos, que tu tinguessis la fallada o no depenia exclusivament de quan havia actualitzat la seva dependència el projecte que te la serveix.

Cinc noms en quatre dies

El 21 d'octubre, quan Node va publicar la correcció, la fallada es deia Node2Shell. El 23, Aikido el va batejar primer Python2Shell i el va rebatejar HTTP2Shell aquell mateix dia, en veure que no era cosa d'un llenguatge sinó de qualsevol que descomprimís una resposta HTTP. I el 25 es van publicar dues anàlisis el mateix dia amb dos noms més: JFrog el va anomenar Brotli2Shell, subtitulat sense cap subtilesa «més enllà de Node(2Shell) i HTTP(2Shell)», i Wiz el va anomenar Broccoli2Shell, recollint la proposta de l'investigador Juliano Rizzo.

La justificació de l'últim és la frase més neta que s'ha escrit sobre tot això: la fallada «es va dir primer Node2Shell, però el defecte és al Brotli mateix i arriba força més enllà de Node.js». Cinc noms en quatre dies, i el que mou cadascun és sempre el mateix: algú descobrint que el radi d'abast era més gran que el seu model mental. Primer es va pensar en termes de llenguatge, després de producte, després de protocol. Els dos últims noms, publicats el mateix dia per dos equips diferents, anomenen per fi la biblioteca.

El teu inventari està escrit en el primer idioma. Tens una línia per a Node, una per a .NET, una per a la teva plataforma de dades. Cap per a Brotli. Nosaltres portem l'inventari de xarxa dels nostres clients a NetBox, com a font única de veritat, i convé dir què no resol això: NetBox sap quina màquina hi ha a quin armari, amb quina adreça i a quin circuit. No sap quines biblioteques van dins dels binaris que corren a sobre. Cap eina d'inventari d'infraestructura ho sap, perquè no és la pregunta per a la qual es van dissenyar.

La paraula que es repeteix a tots els avisos: «empaquetat»

La paraula apareix als quatre avisos. Node parla del «descodificador Brotli empaquetat amb Node.js». La incidència d'Apache Arrow es titula, literalment, «[C++] El Brotli 1.0.9 empaquetat està afectat per CVE-2026-111497». Al repositori de .NET, qui va obrir la pregunta es refereix al «descodificador Brotli incorporat a .NET 9.0.21 i 10.0.13». I ClickHouse va un pas més enllà: no empaqueta el Brotli de Google, empaqueta una bifurcació pròpia —amb un pedaç per a arquitectura loongarch64— i el que va fer el 25 d'octubre va ser actualitzar el seu fork.

Fixa't en el número d'Arrow: 1.0.9. No és l'última de la branca afectada —aquesta és la 1.2.0—, ni la penúltima: està diverses versions per darrere, empaquetada dins del mateix projecte, mentre la biblioteca seguia el seu camí per fora. Ningú no la va instal·lar, ningú no la va triar i ningú no la veurà amb un apt. El rastrejador de seguretat de Debian, de fet, llista per a aquest CVE exactament tres paquets font: brotli, nodejs i python-brotlicffi. El teu gestor de paquets et parlarà d'aquests tres i et dirà la veritat. També et dirà zero sobre l'exemplar que porta a dins Arrow, el que porta .NET i el fork de ClickHouse.

Per això els investigadors de JFrog —Aviv Engelberg, Eddy Tsalolikhin i Ofri Ouzan— no van publicar només una anàlisi: van publicar un escàner, i el van justificar dient que troba rutes de descompressió Brotli que els escàners basats únicament en versió no veuen. Busca crides de JavaScript (fetch, Express, body-parser), clients de Python (Requests, HTTPX, aiohttp, urllib3), referències al descodificador dins de binaris ELF i ús per línia d'ordres. És a dir: van deixar de preguntar «quina versió tens instal·lada?» i van passar a preguntar «quin codi teu descomprimeix això?». No és la primera vegada que aquesta distinció decideix el resultat; en vam escriure quan vam explicar per què el teu escàner diu zero crítiques i l'exploit dona root.

La petició la fas tu (i per això el teu perímetre no mira allà)

El reflex és pensar en el servidor: algú m'envia un cos comprimit amb br i em rebenta el procés. Passa. Envoy i Istio —els proxies que molts tenen davant de tot— apareixen a les llistes de corregits, encara que amb una particularitat que convé mirar de prop més avall. Però el camí més ample va en l'altra direcció.

Els paquets Brotli i brotlicffi de PyPI porten el descodificador vulnerable, i requests, urllib3, aiohttp i httpx fan servir qualsevol dels dos automàticament per descodificar respostes amb Content-Encoding: br. Automàticament vol dir: sense que tu escriguis una línia, sense que aparegui al teu codi, sense que ho hagis decidit. El teu treballador en segon pla crida una API d'un tercer, el tercer respon comprimit, i el descompressor vulnerable s'executa dins del teu procés, darrere del teu tallafocs.

El perímetre no et cobreix d'això perquè el perímetre està dibuixat al voltant d'una pregunta diferent: qui entra. Aquí no entra ningú. Surts tu, i tornes amb alguna cosa a les mans. És la versió moderna d'un clàssic de la resiliència. La peça que et trenca és la que donaves per descomptada. La fallada d'un descodificador de compressió és inevitable a escala de tot l'ecosistema; que això es converteixi en avaria depèn de si pots respondre, en hores, on és la teva còpia i qui l'actualitza.

«Està pedaçat?» no té una resposta: té vuit rellotges

Aquesta és la taula que importa, amb l'estat a 26 d'octubre del 2026. El que canvia de fila a fila són tres coses: la versió, què aconsegueix l'atacant i si existeix alguna cosa per instal·lar.

On Què aconsegueix la fallada allà Rellotge a 26-oct-2026
Node.js Execució remota de codi via node:zlib Corregit el 21-oct a 22.23.4, 24.22.1 i 26.12.1. Les branques 18, 20, 23 i 25 estan fora de suport: no hi haurà pedaç
Bun Mateix descodificador, mateix abast Afectat d'1.0.21 a 1.4.3; corregit a 1.4.4
Python S'activa sol, en descodificar respostes br des de requests, urllib3, aiohttp o httpx Brotli corregit a 1.2.1; brotlicffi a 1.2.1.0
Envoy / Istio Només si has activat la descompressió Brotli, que no ve per defecte: llavors descomprimeix abans que ningú Envoy: 1.36.12, 1.37.8, 1.38.6, 1.39.3 i 1.40.0. Istio: 1.29.9, 1.30.6 i 1.31.2, i només si tens activada la descompressió brotli
.NET Caiguda del procés per violació de segment en codi natiu Actualitzacions des del 25-oct: 8.0.33, 9.0.22, 10.0.14 i 11.0.0-rc.3
ClickHouse Denegació de servei en descomprimir peticions HTTP: se't talla la consulta Fusionat el 25-oct (PR #127419), amb retroports anotats a 26.10.3.6, 26.9.26.18, 26.8.35.17 i 26.3.61.13
Apache Arrow Bucle infinit de CPU en descomprimir Parquet Incidència oberta (#55166, del 23-oct). Continua empaquetant Brotli 1.0.9
FFmpeg / libjxl Corrupció de memòria en descodificar un JPEG XL preparat JFrog va obtenir una fallada de segment a 0x414141414141 en alliberar un punter derivat del payload

Les dues files dels extrems són les que importen. A Node, el 21 d'octubre hi havia binari per instal·lar; cinc dies després, a Apache Arrow continua havent-hi una incidència oberta i un Brotli 1.0.9 a dins. Si la teva plataforma de dades llegeix Parquet a través d'Arrow, «actualitza» no és un pla: encara no hi ha res per instal·lar. El que hi queda és operatiu, no de pedaç: no acceptar br a les entrades que controles i no anunciar Accept-Encoding: br a les sortides que fas, fins que existeixi el binari. És incòmode i és més lent, i és la diferència entre tenir un pla i tenir una frase.

I hi ha un novè rellotge, que no és a la taula perquè no és de ningú en concret. Les versions corregides d'Envoy van sortir entre el 6 i el 13 d'octubre; les d'Istio, el 15 i el 16. Abans que el CVE fos públic. No van reaccionar ràpid: havien actualitzat la seva dependència de Brotli en algun moment posterior al febrer i es van trobar corregits sense saber-ho. Aquesta és l'altra meitat de la resposta a «ens afecta?», i és la que no controles: la freqüència amb què els teus proveïdors actualitzen el que porten a dins.

El 9,2 i el «cap»: les dues columnes que no es resolen

Columna u. 9,2 crític assignat per Google, xarxa, sense privilegis, sense interacció. Wiz va publicar el 25 d'octubre que el 91 % dels entorns cloud que veu té recursos vulnerables i el 82 % té instàncies de Node.js exposades públicament. I Imperva va executar codi aliè dins d'aplicacions que feien servir la imatge oficial de Node.js 24.22.0 per a Linux arm64, sense modificar, amb Express 5.0.0–5.3.0 i body-parser 2.0.0–2.3.0; les mateixes entrades van ser rebutjades per la 24.22.1.

Columna dos. La fitxa SSVC de CISA del 23 d'octubre registra l'explotació com a «none» i l'automatitzable com a «no». Red Hat el puntua 8,1 en CVSS 3.1 en lloc de 9,2 i escriu que «no hi ha una prova de concepte d'execució remota de codi pública i fiable». El mateix Node.js el va classificar com a High, no com a Critical. I l'exploit d'Imperva no està publicat: ells mateixos avisen que l'execució els va costar diversos intents i que no van provar totes les compilacions.

Les dues columnes són veritat i no es resolen una contra l'altra. El que decideix el resultat és una dada que no és a cap de les dues: quant trigues tu a passar de «hi ha pedaç» a «està instal·lat». Si aquest número és de dies, la columna dos et dona marge de sobres. Si és de mesos, la columna dos és irrellevant, perquè quan aparegui el primer atac de debò continuaràs on ets ara. Ja vam explicar el cas on aquella distància estava mesurada: el pedaç de Redis feia 164 dies que estava publicat quan van començar a entrar.

El cens de cinc preguntes (es fa aquesta tarda)

Cinc preguntes amb resposta concreta. Nosaltres les fem en aquest ordre quan entrem en una plataforma que no coneixem, i si alguna deixa en blanc tothom a la sala, aquella és la que cal treballar. Comença per aquí:

# 1. Quines còpies de Brotli tens, sense preguntar-ho al gestor de paquets node -v # compara amb 22.23.4 / 24.22.1 / 26.12.1 # NO facis servir node -p "process.versions.brotli" per decidir: Node pedaça per cherry-pick # SENSE pujar aquell número. Un 24.22.1 ja pedaçat continua responent 1.2.0. pip list 2>/dev/null | grep -i brotli # la de Python: 1.2.1 / brotlicffi 1.2.1.0 dotnet --list-runtimes # compara amb 8.0.33 / 9.0.22 / 10.0.14 apt list --installed 2>/dev/null | grep -i brotli # NOMÉS la del sistema # 2. I ara les que no declara ningú strings /ruta/al/binario | grep -i "brotli" # necessita binutils; no dona versió find / -name "*brotli*" -not -path "*/proc/*" 2>/dev/null # COMPTE: find només veu fitxers. Les còpies enllaçades estàticament —Arrow, .NET, # ClickHouse: justament el cas d'aquest article— no tenen fitxer propi.
  1. Què coses teves descomprimeixen Brotli sense que ho hagis demanat? No «què tens instal·lat»: què executa el descodificador. Cada treballador que crida una API externa, cada proxy, cada procés que llegeix Parquet. Si la llista et surt curta a la primera, és que no has mirat dins dels contenidors.
  2. De cada còpia: és del sistema o va empaquetada? La distinció decideix qui l'arregla. La del sistema l'arregla la teva distribució. L'empaquetada l'arregla el projecte que la hi va posar, al seu ritme, i tu te n'assabentes llegint el seu repositori.
  3. Per a les que no tenen versió corregida: quina és la mitigació operativa, escrita? Rebutjar Content-Encoding: br a les entrades que controles, deixar d'anunciar Accept-Encoding: br a les trucades sortints. Amb data de revisió, perquè és temporal i el que és temporal sense data es queda.
  4. Qui mira això d'aquí a dues setmanes? Un avís que en cinc dies va tenir quatre noms i vuit estats de pedaç diferents no es tanca amb un correu el divendres. Necessita propietari i data de revisió. Si la resposta és «ja ho va veure algú», no hi ha resposta.
  5. Què fas amb el que està fora de suport? Node 18, 20, 23 i 25 no rebran aquest pedaç mai. Això ja no cap en un tiquet de seguretat. És una decisió d'arquitectura, i algú l'ha de prendre amb una data al davant. Cada CVE que arribi a partir d'ara et repetirà la mateixa pregunta.

Quan NO cal córrer

I la part contrària. Si res teu descomprimeix Brotli que vingui de fora, això és una actualització de rutina i no un dissabte. Una aplicació interna que només parla amb ella mateixa, en una xarxa tancada, sense trucades sortints a tercers, no és a la primera fila d'això per molt que la seva versió de Node aparegui en vermell en un informe. Posa-la al cicle normal i continua amb la teva setmana.

El risc contrari al pànic és el tracte uniforme, i fa més mal del que sembla: tractar tots els avisos del mes amb la mateixa urgència és exactament com es gasta el crèdit que necessitaràs el dia que un d'ells sí que s'estigui explotant. L'escala de gravetat no la posa el CVSS, la posa la resposta a la pregunta u del cens.

Nosaltres operem infraestructura aliena des del 1996 i l'inventari és, de lluny, la part menys vistosa i més decisiva de la feina. No es guanya amb una eina: es guanya sabent què corre dins de cada cosa, qui l'actualitza i què es fa quan el projecte de riu amunt encara no ha publicat res. Si el teu equip fa tres dies que intenta respondre a «ens afecta?» i la resposta continua sent un fil de correu, el problema no és aquest CVE: és que el cens no existeix. Això és la meitat del que fem a dades i aplicacions i a ciberseguretat. Si vols que ho mirem amb tu, en parlem.

Fonts. Tot el que segueix es pot comprovar a l'origen, i els enllaços hi són a posta. La fallada: avís de Node.js del 21 d'octubre del 2026 («Node.js Brotli decoder out-of-bounds write permits remote code execution», severitat High, corregit a 22.23.4, 24.22.1 i 26.12.1), d'on surten la descripció literal del canvi de bloc invàlid i la memòria intermèdia interna de vuit bytes, la via d'accés per node:zlib i els crèdits a OpenAI Codex Security (Khai Tran i Sylvie Mayer) i a mcollina i aduh95. Puntuació CVSS 4.0 de 9,2 assignada per Google com a entitat assignadora, vector CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N i classificació CWE-787 sobre CWE-252, de la fitxa del NVD. La correcció riu amunt: commit 0ed4268 de google/brotli, «Detect and report invalid stream earlier», datat el 27 de febrer del 2026, que afegeix el codi d'error BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH i propaga codis d'error a les tres funcions de canvi de bloc; versions 1.0.10, 1.1.1 i 1.2.1 publicades totes tres el 21 d'octubre del 2026. Cada producte, al seu propi repositori: dotnet/runtime #136422 (oberta el 24 d'octubre; les compilacions 8.0.33, 9.0.22, 10.0.14 i 11.0.0-rc.3 les dona l'equip de seguretat de Microsoft al fil el 25); apache/arrow #55166 (oberta el 23 d'octubre, encara oberta en tancar aquest article); ClickHouse #127419 (fusionada el 25 d'octubre, d'on surten el fork propi amb pedaç per a loongarch64 i els retroports anotats a 26.10.3.6, 26.9.26.18, 26.8.35.17 i 26.3.61.13); rastrejador de seguretat de Debian, amb els seus tres paquets font; i el butlletí ISTIO-SECURITY-2026-007, que acota l'impacte a qui habilita la descompressió Brotli, per exemple amb un EnvoyFilter. Les versions corregides d'Envoy (1.36.12, 1.37.8, 1.38.6, 1.39.3, 1.40.0) i d'Istio (1.29.9, 1.30.6, 1.31.2) són anteriors a la divulgació: van sortir entre el 6 i el 16 d'octubre i el CVE es va afegir al butlletí després. L'anàlisi: JFrog Security Research («Brotli2Shell», 25 d'octubre, per Aviv Engelberg, Eddy Tsalolikhin i Ofri Ouzan) i el seu escàner obert. Precisió important: JFrog documenta a FFmpeg una fallada de segment a 0x414141414141 en alliberar un punter derivat del payload, i diu haver reproduït control del punter d'instrucció en «aplicacions natives addicionals» que no anomena; atribuir a FFmpeg el control del punter seria anar més lluny que la font. Les dues columnes: el 91 % i el 82 % són telemetria pròpia de Wiz del 25 d'octubre sobre els entorns que veu, no un cens del parc mundial, i Wiz no acota la població; la frase que «no hi ha una prova de concepte d'execució remota de codi pública i fiable» i el 8,1 en CVSS 3.1 són de la fitxa de Red Hat; la demostració i els seus límits, d'Imperva, que va executar codi dins d'aplicacions sobre «la imatge oficial de Node.js 24.22.0 per a Linux arm64» i adverteix que li va costar diversos intents i que no va provar totes les compilacions; l'exploit no està publicat. La fitxa SSVC de CISA del 23 d'octubre (explotació «none», automatitzable «no»), la cronologia de noms —Python2Shell i HTTP2Shell el 23, per Aikido— i el comportament automàtic de requests, urllib3, aiohttp i httpx en descodificar Content-Encoding: br, de l'anàlisi de Phoenix Security. El cinquè nom, Broccoli2Shell, i la seva justificació («the bug is in Brotli itself and reaches well beyond Node.js»), atribuïda a la proposta de l'investigador Juliano Rizzo, de la pàgina independent broccoli2shell.com, que declara no estar afiliada a Google, al projecte Node.js ni a la Python Software Foundation. Els estats de pedaç són a 26 d'octubre del 2026 i canvien cada poques hores: abans d'actuar, ves al repositori del producte que t'afecti, que és just el que demana aquest article. No hem trobat aquesta fallada ni hem auditat el codi de Brotli, i no tenim cap client la plataforma del qual caigués per això aquesta setmana: aquest article llegeix avisos i repositoris públics i els creua amb com portem nosaltres un inventari, i ho diu així expressament.

Etiquetes: Ciberseguretat Vulnerabilitats Inventari Pedaços Dades Proveïdors

Subscriu-te al nostre butlletí

Per rebre històries del món IT, novetats d'everyWAN i ofertes exclusives per a subscriptors, dona't d'alta a la nostra llista de correu

everyWAN
everyWAN