
Neseniai teko analizuoti įdomią kenkėjišką grandinę: nuo „ClickFix” socialinės inžinerijos iki dar nematyto modulinio Python RAT ir naršyklės plėtinio. Įdomi dalis ta, kaip kenkėjas gauna savo C2 domenus: ne įrašytus kode, o dinamiškai gaunamus iš išmaniojo kontrakto blokų grandinėje (EtherHiding taktika). Šia analize ir norisi pasidalinti.
Viskas prasidėjo teisėtoje, bet sukompromituotoje svetainėje – lietuviškoje drabužių parduotuvėje. Joje buvo patalpintas įtikinamas, bet netikras pranešimas „patvirtinkite, kad esate žmogus” – ir gaunamas paprastas „sprendimas”: paspausti Win + R, tuomet Ctrl + V ir Enter. Įdomi detalė, kad kenksmingas pranešimas buvo užkraunamas iš promo-chatgpt.com domeno.

O apgaulė ta, kad svetainės kodas į iškarpinę jau yra įdėjęs kenkėjišką komandą. Vartotojas mano patvirtinantis savo žmogiškumą, o iš tikrųjų savo rankomis paleidžia pirmąją kenkėjo komandą. Ši technika, vadinama ClickFix ir gynybos požiūriu itin nemaloni: pradžioje dažnai nėra jokio kenkėjiško kodo atsisiuntimo; o veiksmą atlieka pats vartotojas, tad atrodo kaip teisėta žmogaus veikla. Stebėtu atveju paleista komanda tik sukūrė suplanuotą užduotį (angl. scheduled task) pavadinimu „Enter“, kuri jau turėjo atsiųsti kitą stadiją.
![]()
Suplanuotai užduočiai „Enter” pasileidus, ji parsisiuntė ir įvykdė skriptą, kuris į vartotojo laikinąjį katalogą (%TEMP%) išsaugojo du failus – paleidimo (launcher) ir obfuskuotą PowerShell skriptą – tuomet pridėjo registro „Run” raktą PersonalizedUpdates (kad pasileistų vartotojui prisijungus), ištrynė pačią suplanuotą užduotį „Enter” (ji jau atliko savo darbą, tad pėdsakas panaikinamas) ir paleido obfuskuotą skriptą.
Būtent obfuskuotas skriptas ir yra pirmasis realus valdymo komponentas – savotiškas „mini” PowerShell RAT. Pirmiausia jis surinko sistemos „piršto atspaudą” – vartotojo vardą, kompiuterio vardą ir registre saugomą unikalų sistemos identifikatorių (MachineGuid) – ir jį keliais sluoksniais užšifravo (XOR su statiniu, į kodą įrašytu raktu → eilutės apvertimas → Base64). Šis atspaudas leidžia operatoriui atpažinti kiekvieną auką.

Tada ateina įdomiausia dalis – kaip skriptas sužino, su kuriuo serveriu bendrauti. C2 domenas skripte neįrašytas. Vietoje to jis dinamiškai gaunamas iš blokų grandinės – technika, vadinama EtherHiding.

Veikimo esmė tokia: operatorius savo C2 domeną „paslepia” išmaniojoje sutartyje viešame blokų grandinės tinkle (šiuo atveju – Polygon). Kenkėjiškas kodas atlieka eth_call tipo užklausą į konkrečią sutartį (nurodydamas jos adresą ir kviečiamą funkciją), o grąžinta reikšmė atkoduojama į tikrąjį C2 adresą. Skirtingai nei domeną ar serverį, blokų grandinės įrašo „nenuimsi” – jis nekeičiamas ir necenzūruojamas. O užklausos keliauja į gerą reputaciją turinčius viešus blokų grandinės RPC mazgus, kurie organizacijose retai blokuojami. Svarbi detalė – eth_call yra tik skaitymo operacija: ji įvykdoma lokaliai blokų grandinės mazge, į tinklą netransliuojama jokia transakcija, nemokamas joks „gas” mokestis ir nelieka jokio „on-chain” pėdsako, kurį būtų galima susieti su auka. Kenkėjiškas kodas tiesiog perskaito sutarties būseną – tarsi reikšmę iš nutolusio konfigūracijos failo.
Gavęs C2 adresą (3262d48df5d75e34[.]shop), skriptas pradėjo periodinį (kas 20 sekundžių) „beacon” ryšį, o užšifruotą atspaudą siuntė Authorization antraštėje, taip pranešdamas, kad nauja auka aktyvi ir laukia komandų.
Iš šio C2 buvo gauta vienintelė komanda: per Invoke-WebRequest parsisiųsti ir paleisti kitą stadiją iš onemm[.]net domeno (IP 37[.]27[.]52[.]152). Parsisiųstas failas išsaugotas laikinajame kataloge – ir nuo jo prasideda įdomiausia grandinės dalis.
Antroje stadijoje parsisiųstas PowerShell skriptas pradeda trečiąją grandinės dalį. Pirmiausia jis patikrina, ar sistemoje nėra įdiegto Python, o neradęs – išskleidžia jame pačiame supakuotą (gzip + Base64) nešiojamą Python aplinką su kenkėjišku kodu į laikinojo katalogo poaplankį ir paleidžia pythonw.exe (python aplinka be iššokančio lango, procesas vyksta „backgrounde“), perduodamas jam kenkėjišką Python bytecode (.pyc) failą, užmaskuotą nekaltu LICENSE.txt vardu.
Čia slypi dar vienas elegantiškas triukas: pythonw.exe yra teisėtai pasirašytas „Python Software Foundation”, tad parašais paremtos apsaugos jo neblokuoja. Kenkėjiškas yra tik .pyc failas (LICENSE.txt), o visa reali veikla vyksta po legitimaus proceso vardu – klasikinis „atsinešk savo interpretatorių” (angl. bring your own interpreter) principas.
Pats LICENSE.txt – smarkiai obfuskuotas: atsitiktiniai funkcijų ir kintamųjų vardai, XOR + Base64 užšifruotos eilutės, „negyvas” kodas ir per išimtis realizuotas valdymo srautas, apsunkinantis analizę. Dar prieš imdamasis darbo, jis atlieka smėliadėžės (sandbox) patikrą: „pamiega“ 5-10min, tada nuskaito USERNAME ir COMPUTERNAME reikšmes ir palygina jas su žinomų analizės aplinkų „juoduoju sąrašu” – pavyzdžiui, tipiniais smėliadėžių vartotojų vardais (kaip „JohnDoe”) ar kompiuterių vardais (kaip „DESKTOP-NAKFFMT”). Aptikęs tokį požymį, kenkėjas nedelsiant baigia darbą ir taip išvengia automatizuotų analizės sistemų. Tik įsitikinęs, kad veikia „tikroje” mašinoje, jis tęsia darbą.
Paleistas .pyc veikia moduliškai – tai jau pilnavertis Python RAT, pagal C2 komandas atsisiunčiantis ir vykdantis atskirus modulius (prireikus jis gali persileisti ir kaip x64 versija). Pirmiausia atliekama sistemos ir tinklo žvalgyba – leidžiamos tokios komandos kaip net session, cmd /c ver, ipconfig /all, net use ir arp -a, o per SecurityCenter2 WMI klasę išvardijamos įdiegtos antivirusinės programos. Toliau botas įsitvirtina dar dviem mechanizmais (registre „Run” raktu „edgewebhelper_7565″ ir prisijungimo skriptu pridedant registro HKCU\Environment\UserInitMprLogonScript), taip prie ankstesnės pirmos stadijos persistencijos pridėdamas dvi naujas.
Tada prasideda duomenų rinkimas: botas padaro ekrano nuotrauką, nuskaito iškarpinės turinį ir imasi naršyklėje saugomų duomenų vagystės. Tam jis priverstinai uždaro naršykles ir paleidžia jas iš naujo su specifiniais parametrais, leidžiančiais „atrakinti” slaptažodžių bei slapukų duomenų bazes. Taip pavagiami naršyklės duomenys – ne tik slaptažodžiai, bet ir sesijų slapukai, kurie leidžia apeiti multifaktoriaus autentifikavimą (MFA), nes užvaldžius aktyvią sesiją atskiro prisijungimo nebereikia.
Ryšys su C2 vėl prasideda nuo EtherHiding – šįkart C2 domenas (aj3hf7uk3hh3yskr[.]com) gaunamas iš antros Polygon sutarties. Ryšys jau brandesnis nei pirmos stadijos „mini” RAT: JSON pranešimai šifruojami AES-256-GCM ir siunčiami per HTTPS rečiau (numatytas „beacon” intervalas ~250 s), o funkcionalumas suskaidytas į kelis endpointus – /b (beacon), /r (rezultatai), /l (logai), /mc/{vardas} (modulių parsiuntimas) ir /s (interaktyvus srautas). Botas palaiko turtingą komandų rinkinį: dinamiškai užkrauti, paleisti ir atnaujinti modulius, rotuoti šifravimo raktus, o gavęs „kill” komandą – nutraukti darbą ir pašalinti savo pėdsakus. Nepavykę siuntimai patenka į pakartotinio bandymo eilę (iki 100 pranešimų), tad dalis duomenų nepradingsta net trūkinėjant ryšiui.
Galiausiai botas kreipiasi dar į vieną serverį (90happymoney[.]top) ir parsisiunčia paskutinį komponentą – naršyklės plėtinį, kuris grandinę paverčia pilnaverčiu nuotoliniu valdymu pačioje naršyklėje.
Paskutinis grandinės komponentas parsisiunčiamas iš 90happymoney[.]top ir įsidiegia kaip „Chrome” plėtinys (lokacija %APPDATA%\MediaEngineOpen_6u6g8l, plėtinio ID kkecmimnokipgkinknmmpjcbpldfdhfd). Jį sudaro du skirtingus sluoksnius valdantys obfuskuoti skriptai – content.js ir LensTestManager.ps1 – bei juos jungiantis valdymo skriptas background.js.
content.js veikia kiekvieno lankomo puslapio kontekste. Jo tikslas – pagauti duomenis dar naršyklėje. Tam jis perrašo tris standartinius naršyklės mechanizmus: XMLHttpRequest.send(), window.fetch() ir window.postMessage() – nuo to momento visas iš puslapio siunčiamas POST/PUT/PATCH srautas praeina pro jo filtrą. Be srauto, content.js registruoja klaviatūros paspaudimus (keylogger funkcionalumas) ir perima formų siuntimus. Mokėjimo kortelių duomenis jis renka keliais būdais vienu metu: skenuoja DOM elementus, tikrina laukų pavadinimus ir autocomplete atributus, rastus skaičius patvirtina Luhn algoritmu ir stebi postMessage srautą – taip pagaunami net per iframe’us iš mokėjimų apdorojimo paslaugų („Stripe”, „Braintree”, „Spreedly”) perduodami duomenys. Visa surinkta informacija perduodama background.js skriptui.

Sistemos lygmens operacijos vykdomos per native messaging hostą. background.js siunčia komandas per chrome.runtime.sendNativeMessage(„com.top.index”, …), o jas įvykdo LensTestManager.ps1, kurį paleidžia TestPad.bat. LensTestManager.ps1 – apie 58 tūkst. eilučių failas, kurio didžiąją dalį sudaro niekinės, apgaulingos funkcijos. Failo antraštė suklastota taip, kad skriptas atrodytų kaip teisėtas įrankis: jame deklaruojama, esą tai „SessionManager v2.6.21 – Enterprise ResourcePool Module” (autorius nurodytas „Platform Engineering”). Tikrasis kenkėjiškas kodas – viena didelė obfuskuota „PowerShell” eilutė, paslėpta failo viduryje. Prieš imdamasi darbo, ji pirmiausia „apakina” gynybą: išjungia AMSI, ETW ir „PowerShell” skriptų blokų žurnalinimą, taip atimdama iš gynėjų dalį matomumo. Pasinaudodamas native messaging hostu, operatorius gali įvykdyti bet kokią „PowerShell” komandą.

background.js yra tarpinis mazgas tarp plėtinio ir operatoriaus. Jis surenka duomenis iš content.js ir per LensTestManager.ps1 leidžia PowerShell komandas. C2 adresas ir čia gaunamas per „EtherHiding” – jau iš trečios „Polygon” sutarties. Prisijungęs prie C2 per „WebSocket” (wss://90happymoney[.]top:3001/ws), palaiko nuolatinį interaktyvų ryšį. Pirmo paleidimo metu jis automatiškai nuskaito ir išsiunčia į C2 ekrano nuotrauką, visus naršyklės slapukus, lankymo istoriją, žymes, įdiegtų plėtinių sąrašą ir sistemoje esančios programinės įrangos sąrašą.
Šioje stadijoje grandinė pasiekia savo tikslą: content.js realiu laiku renka duomenis naršyklės lygmeniu, o LensTestManager.ps1 vykdo komandas pačioje sistemoje. Auka kompromituojama vienu metu abiejuose lygmenyse – tiek naršyklės, tiek sistemos.
Tokią grandinę pastebėti ir laiku užblokuoti yra sunku. Kenkėjiškas kodas buvo naujas, anksčiau nematytas – net rašant šį straipsnį, pagal VirusTotal duomenis dauguma saugumo vendorių šios atakos IoCs žymi kaip saugius. Kadangi parašais paremtos (angl. signature based) priemonės tokio pavyzdžio nepagauna, vienintelė viltis elgesio analize – bet ir pažangiausi EDR sprendimai šioje grandinėje sureaguoja per vėlai, kai žala jau padaryta.
Kas realiai būtų padėję? Visų pirma – vartotojų mokymai. Phishing mokymuose būtina turėti atskirą skyrių apie ClickFix tipo atakas: paaiškinti patį „patikrinkite, ar esate žmogus” ir „Win+R → Ctrl+V → Enter” scenarijų.
Antra, organizacijoms verta pagalvoti apie blockchain RPC endpointų blokavimą darbo vietose. Jei Web3 funkcionalumas įmonei nereikalingas, toks srautas gali būti drąsiai ribojamas – ir tai tiesiogiai kerta EtherHiding taktikai, atimdamas iš kenkėjo galimybę dinamiškai gauti C2 adresą ar komandas. Nuolat atnaujinamo, „oficialaus” tokių endpointų sąrašo nėra, tačiau kaip atskaitos tašką galima naudoti bendruomenės prižiūrimus sąrašus – pavyzdžiui, awesome-list-rpc-nodes-providers, kuriame surinkti viešų RPC mazgų ir tiekėjų adresai daugeliui tinklų (tarp jų – ir Polygon).
Baigiamoji mintis – efektyviausia investicija dažnai yra ne dar vienas įrankis, o žmogus, kuris atpažins ataką dar prieš paspausdamas „Enter”.
RPC endpoints used in campaign:
Smart contract addresses and functions:
Domains/IPs:
Hashes:
Other:
Projektą „SOCcare“ kartu su mūsų bendradarbiais, NRD Cyber Security ir RevelSI, bendrai finansuoja Europos Sąjunga, o jį remia Europos kibernetinio saugumo kompetencijos centras (ECCC) pagal dotacijos susitarimą Nr. 101145843.
Išsakytos nuomonės ir požiūriai yra tik autoriaus ir nebūtinai atspindi Europos Sąjungos ar Europos kibernetinio saugumo kompetencijos centro požiūrį ir nuomonę. Nei Europos Sąjunga, nei Europos kibernetinio saugumo kompetencijos centras negali būti laikomi atsakingais už jas.
