CISA добави уязвимостта Node2Shell, проследявана като CVE-2026-111497, в каталога си с известни експлоатирани уязвимости (Known Exploited Vulnerabilities). Атакуващи вече изпращат реален експлойт трафик към сървъри с Node.js, затова екипите трябва незабавно да инсталират корекции за сигурност.

CISA добави дефекта в каталога на 27 октомври 2026 г. Федералните агенции трябва да го отстранят до 10 ноември 2026 г. CISA го описва като грешка с непроверена върната стойност в Google Brotli. Според агенцията отдалечен атакуващ без автентикация може да използва специално изработен Brotli поток, за да повреди паметта, да изтече данни, да срине приложението или евентуално да изпълни код. CISA отбелязва, че използването му от ransomware групи е неизвестно. Агенцията посочва и че съгласно BOD 26-04 не се изисква форензичен триаж.

Уязвимостта се среща под няколко имена. Изследователите я наричат Node2Shell, HTTP2Shell, Broccoli2Shell и Brotli2Shell. CNA на Google ѝ присвоява оценка 9.2 (критична) по CVSS 4.0. Дефектът е запис извън границите на буфера (out-of-bounds write) в Brotli декодера, а техническият анализ на Wiz описва откриването и смекчаването за облачните екипи.

Нашият предишен материал за proof of concept на Node2Shell разглеждаше първата публична демонстрация на изпълнение на код. Оттогава събитията се развиха бързо. За няколко дни историята премина от лабораторна демонстрация към реални атаки.

Уязвимостта Node2Shell

Brotli е формат за компресия, който уеб сървърите и клиентите използват, за да намалят трафика. Brotli потокът е разделен на блокове, а специални команди за превключване указват на декодера какъв тип блок следва. Проблемът възниква, когато за даден вид блок е деклариран само един тип.

В такъв случай превключването е невалидно и декодерът трябва да го отхвърли. Анализът на ELLIO обяснява, че помощната функция връщала обикновено false за този случай. Тя връщала false и когато декодерът просто останел без входни данни. Два различни проблема получавали един и същ отговор.

Когато извикващият код разбере, че е нужен още вход, той изпраща още данни. Декодерът държи останалите байтове във вътрешен осембайтов буфер. При реален недостиг на данни този буфер се изпразва със следващата порция. При невалидно превключване той никога не се изпразва. Байтовете продължават да се трупат и след осмия байт записите попадат извън буфера. Тъй като записът използва знаково 32-битово отместване, атакуващият може да избере място в рамките на около 2 GB и да копира там 16 байта собствени данни.

Корекцията е upstream комитът 0ed4268a. Тя кара помощната функция да връща истински код за грешка, BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH със стойност -17, така че невалидните потоци да бъдат спирани веднага. Дефектът присъства в Brotli от версия 1.0.3 през март 2018 г. Според ELLIO корекцията стои в публичния master клон от февруари 2026 г., без издание или бюлетин в продължение на около осем месеца.

Кой е засегнат

Засегнати са Node.js 22.x, 24.x и 26.x, както и версиите с изтекла поддръжка 20.x, 23.x и 25.x. Старите линии няма да получат корекция. Коригираните версии на Node.js са 22.23.4, 24.22.1 и 26.12.1. Google коригира Brotli във версии 1.0.10, 1.1.1 и 1.2.1.

Голият Node.js сървър не разархивира Brotli тяло само защото то е пристигнало. Нещо трябва да подаде байтовете към декодера. ELLIO описва три често срещани пътя. Първият е входяща заявка със Content-Encoding, зададен на br. Express 5 с express.json() или express.raw(), body-parser 2 и Koa с @koa/bodyparser я разархивират още преди да се изпълни кодът на маршрута.

Вторият е изходяща fetch заявка. Вграденият fetch(), axios, got и SuperAgent декодират отговора на враждебен сървър, така че прегледите на връзки, уебхуковете и краулърите са изложени на риск. Третият е всяко директно извикване на node:zlib върху данни от клиент.

Проблемът излиза далеч извън Node.js. Засегнат е Bun преди 1.4.4, както и Python пакетите Brotli и brotlicffi, които се нуждаят от 1.2.1 и 1.2.1.0. Envoy, Ruby gem-ът brotli и много Linux дистрибуции също са в списъка. Бюлетинът на CIS посочва, че дефектът е в Google Brotli версии от 1.0.3 до 1.2.0 на всички платформи. Често задаваните въпроси на Tenable съдържат списък на засегнатите продукти на едно място.

Има един капан, който е добре да се знае. Node.js е пренесъл корекцията (cherry-pick), без да променя низа с версията на включената библиотека. Затова process.versions.brotli показва 1.2.0 както при коригираните, така и при некоригираните компилации. Проверявайте номера на изданието на Node.js.

Red Team на Imperva показа първия работещ експлойт на 22 октомври. Те изпълниха код върху официалния образ на Node.js 24.22.0 за Linux arm64, използвайки както fetch(), така и тела на заявки в Express 5. В техния анализ Node2Shell: From Brotli Data to Remote Code Execution се посочва, че версия 24.22.1 отхвърля същия вход.

След това ELLIO повтори атаката на x86-64. Лабораторията получи shell на четири цели със същата компилация: обикновен node:http сървър, официалния образ node:24.22.0-slim, работещ като root, Express 5.3.0 и Koa 3. Всеки shell се изпълняваше с акаунта на самата услуга.

Лабораторията установи, че официалните компилации на Node.js са non-PIE, така че двоичният файл се зарежда на един и същ адрес всеки път. Рандомизацията на адресното пространство (ASLR) все пак разбърква хийпа и стековете, така че атакуващият трябва да налучква. При включен ASLR shell се появи около 5-ия опит. При изключен ASLR 60 опита не дадоха резултат. Изключването му не помага.

Успехът зависи от две стойности, специфични за целта. Едната е номерът на приетия сокет, който shell-ът използва за вход и изход. Другата е отместване в паметта, обвързано с една точна компилация на Node.js. Отместването за 24.22.0 не даде нищо срещу 22.23.3 или 26.12.0 при 10 000 опита. Това ограничава атакуващите до вълни, насочени към една популярна компилация наведнъж.

Първо започна сканирането. Инструмент, представящ се като node2shell-check/1.0, засегна сензорите на ELLIO от AS44620. Той изпрати 20 Brotli тела на два пъти, за да намери хостове, които изобщо декомпресират Brotli. Нито едно не съдържаше истинския тригер. Това изглеждаше като съставяне на списък преди атака.

Експлойт трафикът последва на 26 октомври в 15:08 UTC. ELLIO наблюдава една заявка от 23 069 983 байта със SHA-256 7fcbdaaecd4419e2930927844f3b772668204737a4e7602a2290c991c93c5c4b. Тя идваше от 94.154.43.7 – адрес, който ELLIO оценява като злонамерен с висока степен на достоверност.

Адресът е забелязан за първи път през декември 2025 г. и е бил активен в 47 от последните 90 дни. Свързван е с експлоатация на PHP2Shell, със сондиране на Palo Alto GlobalProtect и FortiOS SSL VPN и със сканиране на Kubernetes. Заявката е насочена само към Node.js v24.22.0 на x86-64.

Слоят за доставка копира публичен експлойт код. ROP веригата, която поема контрола над процеса, е оригинална. Тя поставя /bin/sh в собствените си данни и оттам извиква execvp на Node. Не използва низа, който вече е в Node, както правят и двете публични вериги.

ELLIO я тества срещу чиста уязвима цел и получи нула shell-а от 50 опита. Веригата срина процеса. ELLIO смята, че това е бил първи опит, изстрелян, преди операторът да тества последната стъпка. Предупреждава, че операторът изглежда фокусиран върху масова експлоатация. Екипите, които помнят React2Shell, ще разпознаят същата схема: бърз публичен код, следван от бързо сканиране. Microsoft документира тази по-ранна уязвимост в своето ръководство за защита от React2Shell.

Неуспешните опити не са безвредни. ELLIO забеляза, че повечето от 400-те му опита срещу Express са сринали работния процес, вместо да дадат shell. Сривът може да запише core dump, а той може да съдържа сесийни бисквитки, токени и ключове на други посетители.

На стандартен Ubuntu хост systemd-coredump записва тези файлове в /var/lib/systemd/coredump. Контейнерите обикновено не записват нищо, така че там вредата е само прекъсване на услугата. Някои други декодери просто увисват безшумно. Python Brotli 1.2.0, brotlicffi 1.2.0.0, потоковото API на PHP и Ruby gem-ът не връщат нищо и никога не завършват, а в случая с PHP процесорът е натоварен на 100%.

Първо инсталирайте корекциите за сигурност. Преминете към Node.js 22.23.4, 24.22.1 или 26.12.1 и обновете Bun, Python пакетите, Envoy и всяка Brotli библиотека на дистрибуцията. Потребителите на Debian трябва да потърсят DLA-4855-1 или DSA-6582-1. Потребителите на Alpine се нуждаят от brotli 1.2.1-r0, или 1.1.1-r0 на 3.21. Напуснете старите линии 20.x, 23.x и 25.x, тъй като те никога няма да бъдат коригирани.

Обновяването на Node.js коригира само включените копия в Node. Инструменти със собствено копие, като изпълними файлове от bun build –compile, DuckDB и PHP разширенията, се нуждаят от отделни обновявания. Прекомпилирайте образите, обновете lock файловете и рестартирайте процесите.

Dockerfile, който фиксира 24.22.0, продължава да доставя уязвимата компилация, докато някой не редактира този ред. Проверете и staging средите, CI, serverless и лаптопите на разработчиците.

Докато корекциите не са инсталирани, блокирайте входящия Content-Encoding: br, където е възможно. Следете и логовете за големи Brotli POST тела, повтарящи се сривове на работни процеси, както и за посочените по-горе IP адрес и хеш. Ограничете кой може да чете core dump файловете и съкратете срока им на съхранение. Федералните екипи имат срок до 10 ноември, но всички останали трябва да приемат тази дата за горна граница, а не за цел.