Кампания от 148 npm пакета, маскирани като студентски уеб проксита, превърна браузърите на посетителите в ботнет за разпределена атака за отказ на услуга (DDoS) за около две седмици през май, според ново изследване на JFrog.

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

Пакетите са разпространявани под имена като charlie-kirk, ilovefemboys и miguelphonk, като всеки от тях е съдържал прокси приложение с марката "Lucide", оформено като начална страница за уроци, наречена Riverbend Tutoring или Northstar Tutoring.

На повърхността проксито е работило, позволявайки на учениците да заобикалят филтрите за съдържание, за да достигнат до игри и блокирани сайтове. Под повърхността обаче то е зареждало модул за отдалечено зареждане на код, чийто краен зловреден код операторите са можели да сменят по желание, плюс генератор на WebSocket Flood, създаден да комуникира по прокси протокола Wisp. Всеки, който е отварял страницата, се е присъединявал към ботнета, без да знае това.

Нищо от това не се изпълнява по време на инсталация. Пакетите не съдържат куки за жизнения цикъл (lifecycle hooks) и вградени скриптове за компилация, и никога не са били предназначени да бъдат импортирани в проект.

Саморепликиращият се червей Shai-Hulud, който засегна повече от 500 пакета през септември 2025 г., събираше тайни ключове на разработчици и се препубликуваше с откраднати токени. Дни преди него, фишинг атака срещу разработчика, известен като qix, внедри код за източване на крипто портфейли в chalk, debug и 16 други пакета с милиарди седмични изтегляния общо.

Тези атаки се задействат в момента на инсталиране на пакета и са насочени към хората, които създават софтуер. Тази конкретна атака пропуска процеса на разработка и изчаква в отворен таб на браузъра.

По-ранно предупреждение от SafeDep каталогизира 141 от пакетите през май и интерпретира операцията като злоупотреба с реклами и регистри: изскачащи под прозореца реклами (popunder), скриптове за монетизация от трети страни и проследяване с Google Analytics, прикачени към Scramjet прокси, насочено към студенти. Това е било вярно за видимото на повърхността.

JFrog обаче проучва случая по-дълбоко. Екипът деобфускира основния скрипт на приложението — единичен ред JavaScript с размер 5.4 MB, който след форматиране се превръща в над 20 600 реда четим код, и възстановява архивирани полезни товари от Wayback Machine, за да възстанови хронологията на кампанията.

Под рекламния софтуер са стояли два модула, като и двата се задействат преди рендирането на React интерфейса.

Първият, който JFrog нарича G2, е модул за отдалечено зареждане на скриптове и той извлича код по изключително небезопасен начин. Той изтегля JavaScript от хранилище в GitHub чрез CDN мрежата jsDelivr, като сочи към подлежащия на промени клон main вместо към конкретна фиксирана версия (commit), не изпраща проверка за интегритет на подресурсите (SRI) и изпълнява всичко получено с привилегиите на самия прокси сайт: пълен достъп до бисквитки, локално хранилище и същия произход (same-origin).

Политиката без референт (no-referrer) не позволява на заявката да разкрие откъде произлиза. Всеки, който контролира акаунта в GitHub зад нея, може да промени кода, изпълняван в браузъра на всеки посетител, по всяко време.

Хранилището вече е връщало грешка 404, когато JFrog го изследва, но архивирано копие от 30 май разкрива какво е обслужвало: проста HTTP Flood атака. На всеки 500 милисекунди скриптът генерира нов низ от един милион знака и го изпраща като no-cors POST заявка към cdn.caan.edu, който JFrog идентифицира като публичен домейн на училище за медицински сестри в Матесън, Илинойс.

Заявките никога не чакат отговор, така че се натрупват. JFrog изчислява, че всеки активен посетител изпраща около 2 MB в секунда входящ трафик към целта, което означава, че хиляда отворени таба с проксито биха насочили около 2 GB в секунда към мишената. Случаен параметър в заявката заобикаля кеширащите проксита, а режимът no-cors пропуска предварителната проверка за CORS заявки, така че нищо не ограничава пакетите.

Вторият модул, I2, е по-усъвършенстваният. Той извлича текстов файл, websocket.txt, съдържащ целеви WebSocket URL адрес и брой сокети, ограничен между 1 и 1024, след което отваря толкова връзки в разпределен цикъл. Архивираната конфигурация е насочвала всеки браузър към 30 връзки с Wisp крайна точка на lunaron[.]top, който сам по себе си е бил активен прокси сървър, инжектиращ зловредна реклама.

Wisp е протокол на Mercury Workshop с ниски изисквания към ресурсите за тунелиране на множество TCP и UDP сокети през една WebSocket връзка, и е често срещана технология в същата сфера на браузърни проксита, които тези пакети имитират.

След като се свърже, всеки браузър превключва сокета си в двоичен режим и на всеки 100 милисекунди изпраща валидна Wisp CONNECT рамка, последвана от CLOSE рамка, и двете насочени към localhost:1. Рамките са коректни Wisp пакети, така че целта не е собствената машина на ученика, а отдалеченият Wisp сървър в другия край на връзката.

Това я прави атака срещу контролния панел (control-plane attack), а не обемна (volumetric) атака. Един браузър, работещ с пълните 1024 сокета, може да принуди Wisp сървъра да заделя и затваря около 10 240 връзки в секунда, докато записва повече от 20 000 реда в лога за същия период.

JFrog отбелязва, че софтуерът wisp-server-node на Mercury Workshop отваря нов сокет за всяка CONNECT рамка, без да проверява дали дестинацията е локален (loopback) или частен адрес, и записва всеки опит в лога. Това изчерпва файловите дескриптори на сървъра.