Een campagne van 148 npm-pakketten, vermomd als webproxy’s van studenten, veranderde de browsers van bezoekers in mei ongeveer twee weken lang in een gedistribueerd denial-of-service-botnet, zo blijkt uit nieuw onderzoek van JFrog.
De pakketten gingen niet achter de ontwikkelaars aan die ze zouden kunnen installeren. De operators gebruikten het register als gratis hosting voor een proxysite met boobytraps en lieten de studenten die de webfilters van scholen kwamen ontwijken, het aanvalsverkeer verzorgen.
De pakketten werden verzonden onder namen als charlie-kirk, ilovefemboys en miguelphonk, elk met een proxy-app met de naam “Lucide” en gekleed als een landingspagina voor bijlessen genaamd Riverbend Tutoring of Northstar Tutoring.
Op het eerste gezicht werkte de proxy, waardoor studenten langs inhoudfilters konden glippen om games en geblokkeerde sites te bereiken. Daaronder laadde het een codelader op afstand waarvan de operators naar believen konden wisselen, plus een WebSocket-floodgenerator die was gebouwd om het Wisp-proxyprotocol uit te spreken. Iedereen die een pagina opende, sloot zich aan bij de zwerm zonder het te weten.
Niets hiervan wordt uitgevoerd tijdens de installatie. De pakketten bevatten geen lifecycle-hooks en geen native build-scripts, en ze zijn nooit geschreven om in een project te importeren.
De zichzelf vermenigvuldigende Shai-Hulud-worm die in september 2025 meer dan 500 pakketten trof, verzamelde geheimen van ontwikkelaars en publiceerde zichzelf opnieuw met gestolen tokens. Dagen daarvoor heeft een phishing-aanval op de beheerder, bekend als qix, portemonnee-aftappende code in krijt, debug en 16 andere pakketten gestopt met daartussen miljarden wekelijkse downloads.
Deze aanvallen worden geactiveerd zodra een pakket wordt geïnstalleerd en richten zich op de mensen die software bouwen. Deze slaat de build-pijplijn over en wacht op een browsertabblad.
Een eerder advies van SafeDep catalogiseerde 141 van de pakketten in mei en las de operatie als adware en registermisbruik: popunder-advertenties, scripts voor het genereren van inkomsten van derden en Google Analytics-tracking gekoppeld aan een Scramjet-proxy gericht op studenten. Dat hield stand voor wat zichtbaar was aan de oppervlakte.
JFrog trok de draad verder. Het team heeft de toegangsbundel van de app, een enkele regel JavaScript van 5,4 MB, uitgepakt in meer dan 20.600 regels leesbare code, onleesbaar gemaakt en gearchiveerde payloads van de Wayback Machine opgehaald om de tijdlijn van de campagne te reconstrueren.
Onder de adware zaten twee modules, die beide werden geactiveerd voordat de React-interface werd weergegeven.
De eerste, die JFrog noemt G2is een externe scriptlader en haalt code zo onveilig mogelijk op. Het haalt JavaScript uit een GitHub-repository via het jsDelivr CDN, wijst naar de veranderlijke hoofdtak in plaats van een vastgezette commit, verzendt geen Subresource Integrity-controle en voert alles uit wat terugkomt met de eigen oorsprongsrechten van de proxysite: volledige toegang tot cookies, lokale opslag en eindpunten van dezelfde oorsprong.
Een beleid zonder verwijzingen zorgt ervoor dat het verzoek niet wordt geadverteerd waar het vandaan komt. Degene die het GitHub-account erachter heeft, kan de code die in de browser van elke bezoeker draait, op elk gewenst moment wijzigen.

De repository retourneerde een 404 tegen de tijd dat JFrog keek, maar een gearchiveerde kopie van 30 mei bewaarde wat het had gediend: een grove HTTP-overstroming. Elke 500 milliseconden bouwt het script een nieuwe reeks van één miljoen tekens op en vuurt deze af als een no-cors POST op cdn.caan.edu, die JFrog identificeert als het publieke domein van een verpleegschool in Matteson, Illinois.
De verzoeken wachten nooit op een reactie, dus stapelen ze zich op. JFrog klokt elke actieve bezoeker op een uploadsnelheid van ongeveer 2 MB per seconde, wat betekent dat duizend open proxy-tabbladen ongeveer 2 GB per seconde naar het doel zouden pushen. Een gerandomiseerde queryparameter verslaat caching-proxy’s, en no-cors slaat de CORS-preflight over, zodat niets de pakketten beperkt.
De tweede module, ik2is de scherpste. Het haalt een gewoon tekstbestand op, websocket.txt, met daarin een doel-WebSocket-URL en een socketaantal met een maximum tussen 1 en 1.024, en opent vervolgens zoveel verbindingen in een gespreide lus. De gearchiveerde configuratie richtte elke browser op 30 verbindingen met een Wisp-eindpunt op lunaron(.)top, zelf een live proxy die bezig was met het injecteren van malvertising.
Wisp is een Mercury Workshop-protocol met weinig overhead voor het tunnelen van veel TCP- en UDP-sockets over een enkele WebSocket, en het is gebruikelijk om in dezelfde browser-proxy-scène deze pakketten te imiteren.
Eenmaal verbonden, stelt elke browser zijn socket in op binaire modus en verzendt elke 100 milliseconden een geldig Wisp CONNECT-frame gevolgd door een CLOSE-frame, beide gericht op localhost:1. De frames zijn correcte Little-endian Wisp-pakketten, dus het doel is niet de eigen machine van de leerling. Het is de externe Wisp-server aan het uiteinde van de verbinding.

Dat maakt het een controlevlakaanval in plaats van een volumetrische aanval. Eén enkele browser die de volledige 1.024 sockets gebruikt, kan een Wisp-server ertoe aanzetten ongeveer 10.240 verbindingen per seconde toe te wijzen en af te breken, terwijl er in hetzelfde traject meer dan 20.000 logregels worden geschreven.
JFrog merkt op dat de wisp-server-node van Mercury Workshop een nieuwe socket opent voor elk CONNECT-frame zonder te controleren of de bestemming een loopback- of privé-adres is, en elke poging registreert. Dat put de bestandsdescriptors uit, overstroomt de logopslag en laat de proxy vallen. wisp-server-node is al verouderd; de beheerders wijzen gebruikers elders op precies dit soort beveiligings- en stabiliteitsproblemen.
De campagne veranderde dus een proxy-tool voor studenten in een wapen tegen de servers waarvan andere proxy-studenten afhankelijk zijn, en richtte een aparte overstroming op een school ernaast.
De infrastructuur is strak geclusterd en niet gebouwd om zich te verstoppen. JFrog traceerde de builds naar een GitHub-organisatie genaamd lucidproxy wiens accounts seconden na elkaar werden geregistreerd, gekoppeld aan een commit-e-mail op geeked(.)wtf en een Discord-handle. Negentig van de 93 gevonden hostnamen voor de implementatie zijn omgezet naar één IP-adres, 92.38.177(.)17, gehost door G-Core Labs.
Tussen de juveniele pakketnamen, een auto-publish shell-script dat in de tarballs is achtergelaten en een “TY WAVES + CHATGPT ILY”-opmerking die SafeDep in de servicemedewerker vond, lezen beide bedrijven de operator als jong. Eén account pushte 116 pakketten in minder dan 35 minuten, en npm deed niets om dit te vertragen.
De commitgeschiedenis van JFrog legt de boog uit. Het project begon in maart als eenvoudige adware, voegde medio mei in een tweedaagse uitbarsting de externe lader en de Wisp-generator toe, voerde aan het eind van de maand een live overstroming uit tegen de verpleegschool en verwijderde vervolgens de kwaadaardige modules weer op 31 mei, toen de rapportage begon.
Een tweede golf op 8 juli, onder een nieuw account, bracht het totaal op 148 pakketten en verscheepte de opgeschoonde, adware-only build. De app is nog steeds onduidelijk, laadt nog steeds scripts van derden vanuit aanvallerdomeinen en de lader wijst nog steeds naar een veranderbare branch. De DDoS-mogelijkheid is niet verdwenen, alleen uitgeschakeld. JFrog merkt op dat de operators de mogelijkheid behouden om het opnieuw in te schakelen: één commit voor die veranderlijke tak, geen pakketupdate vereist.
Veel van de campagnepakketten zijn sindsdien uit npm gehaald en vervangen door de standaard 0.0.1-beveiligingsplaceholder van het register. Uit een steekproefsgewijze controle door The Hacker News van de pakketfamilies op 14 juli 2026 bleek dat de meeste verdwenen waren, maar Charlie-Kirk bedient nog steeds de twee versies die JFrog als kwaadaardig heeft gemarkeerd, 2.0.0 en 3.0.1.
Omdat de dreiging wordt geleverd als een webapp aan de clientzijde en niet als een implantaat tijdens de installatie, volgt de oplossing van JFrog de leveringsmethode.
Beheerders van school- en bedrijfsnetwerken, waar deze proxy’s het meeste verkeer trekken, moeten de domeinen van de campagne op DNS-niveau blokkeren. De hosts voor het genereren van inkomsten en scripts die de huidige build nog steeds bereikt, waaronder woofbeginner(.)com en c.vipersfutbol(.)com, zijn degenen die als eerste worden geblokkeerd.
Iedereen die een van de proxysites heeft geladen, moet de cache van de browser en de lokale opslag wissen en de registratie van servicemedewerkers afmelden die zijn achtergelaten door een bijles- of proxydomein. Teams waarvan de bouwomgevingen de benoemde pakketten hebben opgehaald, moeten deze uit manifesten en lock-bestanden halen en schoon opnieuw opbouwen. Het artikel van JFrog bevat de volledige lijst van 148 pakketten, domeinen, IP-adressen en hashes.
The Hacker News heeft contact opgenomen met JFrog voor meer details over de omvang van het botnet en of de WebSocket-aanval tegen een levend doelwit plaatsvond, en zal dit verhaal bijwerken met eventuele reacties.
Afhankelijkheidsscanners en sandboxes tijdens de installatie zijn gebouwd om code op te vangen die op npm-installatie wordt uitgevoerd. Deze code heeft nooit om installatie gevraagd. Zolang openbare registers zich verdubbelen als gratis CDN’s, kunnen de pakketten die de moeite waard zijn om je zorgen over te maken in toenemende mate de pakketten zijn die geen enkele build-pijplijn ooit haalt.