GlassWorm – analiza zlonamerne kode

Začelo se je s kodo, ki je ni bilo.

Med pregledom Git repozitorijev v okviru penetracijskega testiranja smo opazili nekaj nenavadnega: datoteke, ki so v urejevalniku izgledale povsem običajne, vendar so se ob izvajanju obnašale drugače. Edini vidni namig je bilo neskladje v številu znakov.

To odstopanje nas je pripeljalo do odkritja prikrite zlonamerne kampanje, ki jo danes spremljamo pod imenom GlassWorm. Gre za večstopenjski Node.js implant, ki se skriva na očeh z uporabo nevidnih Unicode znakov ter pridobiva naslov svojega nadzornega strežnika (C2) prek verige blokov Solana in BitTorrent DHT omrežja.

Kar se je sprva zdelo kot manjša anomalija, se je izkazalo za razširjen napad na dobavno verigo, ki je prizadel več kot 100 javnih repozitorijev.

V času objave tega članka smo kodo zaznali v skupno ~100 javnih repozitorijih na platformi GitHub.

“Nevidna” koda

Zlonamerna koda se skriva z nevidnimi Unicode znaki iz obsegov U+E0000–U+E007F in U+FE00–U+FE0F. Ti znaki se v večini urejevalnikov besedila ne prikažejo, zato jih ob ročnem pregledu težko zaznamo. V Visual Studio Code je prisotnost kode razvidna zgolj iz števila označenih znakov — vsebina sama ostane nevidna.

Zlonamerna koda, vrinjena v git repozitorij
Nevidni Unicode znaki

Ko je koda enkrat naložena, program dekodira skrite znake v veljavno JavaScript kodo in jo izvede z eval(). Rezultat je generator, ki najprej počaka 500 ms, nato pa s pomočjo algoritma AES-256-CBC dešifrira in izvede naslednjo stopnjo. Ključ in inicializacijski vektor sta kodirana neposredno v kodi.

Dinamična resolucija C2

Namesto da bi bil naslov C2 strežnika kodiran v kodi, ga GlassWorm pridobi dinamično — iz metapodatkov zadnje transakcije na Solana denarnici BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC. Vrednost polja link v metapodatkih je Base64-kodiran URL, ki po dekodiranju kaže na http://217.69.11.99/axtyituCswaN5AamyO692g==.

Strežnik vrne naslednjo stopnjo zlonamernega programa, prilagojenega operacijskemu sistemu žrtve (Windows, macOS, Linux). Pred izvajanjem preveri, ali je od prve okužbe minilo vsaj 2 dni, s čimer upočasni analizo v peskovnikih (angl. sandbox). Koda je dodatno zaščitena: Base64-kodirana in AES-256-CBC šifrirana, ključ in IV pa sta posredovana v HTTP-glavah odgovora (ivbase64 / secretkey). Navidezno se izvaja izolirano z uporabo Node.js vm modula. Nadaljnja analiza pokaže, da so reference do console in process omogočene z deljenim kontekstom — sistemski ukazi ostanejo dosegljivi.

Izvajanje se samodejno preneha, če program zazna, da teče na sistemu z ruskimi jezikovnimi nastavitvami ali časovnim pasom.

Kraja podatkov

Windows

»Payload« za Windows je obsežen in v veliki meri sestavljen iz legitimnih knjižnic. Zlonamerni del izvaja naslednje aktivnosti:

  • Prenos in dešifriranje dodatnih modulov s strežnika 217.69.11.99
  • Kraja npm in git poverilnic
  • Kraja podatkov iz kripto-denarnic (namiznih in brskalniških razširitev)
  • Kraja shranjenih gesel iz upravljalnikov gesel
  • Prenos in zagon .NET binarne datoteke, če je na sistemu nameščen Ledger Live

Program z uporabo HTTP-protokola s strežnika 217.69.11.99 prenese ZIP arhiv iz poti /get_arhive_npm/1B89Udneo8JJwdQlbr1w6w==. Arhiv se shrani v %TMP%\kkXqPAFp in ekstrahira v %TMP%\rKeKMWikl. Iz njega se dešifrirata dve datoteki z AES-128-CBC:

  • %TMP%\kkXqPAFp\w.node -> dešifriran in zapisan v %TMP%\cTdTMfItnz\tlESkme
  • %TMP%\rKeKMWikl\f_ex86.node ->preverjena veljavnost SHA-256 zgoščene vrednosti, dešifriran in zapisan v %TMP%\cTdTMfItnz\WujOBGtse

Za obe dešifriranji se uporabita naslednja parametra (Base64-dekodirana pred uporabo):

Ključ: caTY0D6roK1LHa02cA80jA==
IV:     /sXfxpU5Dr0kuXoihE6JFQ==

Ukradeni podatki se pošljejo na http://208.85.20.124/wall s POST-metodo.

.NET binarna datoteka(ggsuuck.exe)

Ob zaznani namestitvi Ledger Live se iz http://217.69.11.99/led-win32 prenese .NET binarna datoteka, shranjena kot %TMP%\ggsuuck.exe. Datoteka se doda med programe ob zagonu naprave preko PowerShell ukaza v registrski ključ HKCU\Software\Microsoft\Windows\CurrentVersion\Run z vrednostjo UpdateLedger.

Aplikacija se pretvarja, da je del programske opreme Ledger Live. Od uporabnika zahteva vnos 24-besedne obnovitvene fraze, ki jo verificira s pomočjo internega slovarja (Assaac.WordBookHealth.thisRectangleWord). Vnesene vrednosti se prenesejo na 45.150.34.158:8080 preko TCP-povezave.

Validacija in prenos 24-besedne fraze

Pred prikazom okna program preveri lokacijo žrtve preko https://ipapi.co/xml. Izvajanje se prekine za sisteme, ki se nahajajo v: Armeniji, Rusiji, Kirgizistanu, Moldaviji, Tadžikistanu, Uzbekistanu ali Kazahstanu.

Pridobivanje in primerjava lokacije žrtve

macOS

Različica za macOS je po strukturi podobna različici za Windows. Zlonamerna koda se začne v vrstici 1380, pred njo je legitimna knjižnica yauzl za upravljanje z ZIP arhivi.

Funkcija FileGrabber.run() zbere:

  • Keychain datoteko
  • Piškote in gesla iz brskalnikov Safari, Firefox in Chrome
  • Uporabnikove zapiske (Notes)
  • SSH in AWS konfiguracijske datoteke (.ssh, .aws)
  • Dokumente iz mape Documents
  • Podatke iz 150+ brskalniških razširitev (MetaMask, Phantom, Exodus, Ledger …)
FileGrabber.run() funkcija

Zbrane datoteke se kopirajo v /tmp/ijewf/. Po izvajanju FileGrabber.run() se zažene AppleScript preko osascript podprocesa. Skripta poskuša pridobiti geslo Chrome preko ukaza security find-generic-password -ga “Chrome”. Gesla se shranijo v /tmp/ijewf/pwd. Če pridobivanje ni uspešno, se prikaže lažno sistemsko okno za ročni vnos.

Zbrani podatki se arhivirajo in pošljejo na http://208.76.223.59/p2p s POST metodo, z nestandardnimi HTTP-glavami:

  • uuid: 57f432c1-c5b7-4b51-8667-044847c63470
  • buildid: 2026-03-06T07:35:05.258Z
  • uuid_machine: dinamično pridobljeno z ukazom ioreg
Eksfiltracija podatkov (POST z nestandardnimi HTTP-glavami)

Za zagotavljanje obstojnosti skripta namesti izvajalno okolje Node.js v23.5.0 v ~/.config/system/.data/.nodejs/ in ustvari LaunchAgent v ~/Library/LaunchAgents/com.user.nodestart.plist, ki ob zagonu sistema izvaja Base64-kodirano JavaScript kodo.

Persistenca — LaunchAgent in Node.js namestitev

Na koncu se preko funkcij ZOZgdRcMWJ(), GgFYYlKGIY() in envExfiltration() ukradejo git poverilnice, npm ključi, SSH ključi in datoteke iz .vscode map. Zbrani podatki se arhivirajo v /tmp/h.zip in pošljejo na http://208.85.20.124/wall.

Indikator ruskega izvora: komentar v kodi za krajo GitHub SSH ključev je v ruščini.

ZOMBI — Vztrajna C2 komponenta

ZOMBI je JavaScript komponenta, ki deluje neodvisno od fiksnega C2 naslova in implementira dva mehanizma za dinamično resolucijo IP-ja napadalčevega strežnika.

Resolucija C2 naslova

Primarni mehanizem je BitTorrent DHT protokol. ZOMBI se poveže na tri javne strežnike in poizveduje po vrednosti, podpisani z vgrajenim ed25519 javnim ključem. Podpis se verificira s funkcijo crypto_sign_verify_detached, kar zagotavlja, da se naprava poveže le na napadalčev strežnik. IP-naslov se osvežuje na vsakih 50 sekund.

  • libtorrent.org:25401
  • bittorrent.com:6881
  • utorrent.com:6881
Inicializacija DHT klienta z javnim ključem

Sekundarni mehanizem izhaja iz zadnje transakcije Solana denarnice BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC. Pridobljena vrednost je vozlišče, ki se doda DHT klientu za novo poizvedbo. Za dostop do Solana omrežja se uporabi seznam javnih RPC končnih točk:

  • https://go.getblock.us/86aac42ad4484f3c813079afc201451c
  • https://solana-mainnet.gateway.tatum.io/
  • https://solana-rpc.publicnode.com
  • https://api.blockeden.xyz/solana/KeCh6p22EX5AeRHxMSmc
  • https://sol-protect.rpc.blxrbdn.com/
  • https://solana.drpc.org/
  • https://solana.leorpc.com/?api_key=FREE
  • https://solana.api.onfinality.io/public
  • https://solana.api.pocket.network/
  • https://api.mainnet-beta.solana.com
  • https://public.rpc.solanavibestation.com/
Intervalno pridobivanje C2 IP naslova

Zmogljivosti

Ko je C2 naslov razrešen, se ZOMBI poveže na WebSocket na vratih 4789 in ob vzpostavitvi posreduje vrednost _partner: “mulKRsVtolooY8S”. Napadalcem omogoča izvajanje treh vrst ukazov:

  • Izvajanje poljubne JavaScript kode z uporabo eval().
  • Prenos binarnih datotek na okuženo napravo.
  • Delovanje kot SOCKS proxy strežnik — okužena naprava postane del napadalčeve infrastrukture.
WebSocket message handlerji

Indikatorji kompromitacije (IOC)

Omrežni IOC

Tip Vrednost
IP 217.69.11.99
URL http://217.69.11.99/axtyituCswaN5AamyO692g==
URL http://217.69.11.99/get_arhive_npm/1B89Udneo8JJwdQlbr1w6w== — ZIP arhiv (Win)
URL http://217.69.11.99/led-win32
IP 208.85.20.124 — eksfiltracija /wall
IP 208.76.223.59 — eksfiltracija /p2p
IP:port 45.150.34.158:8080
WebSocket [C2]:4789 — ZOMBI C2 kanal

Blockchain / DHT

Tip Vrednost
Solana denarnica BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC
DHT dht.libtorrent.org:25401
DHT router.bittorrent.com:6881
DHT router.utorrent.com:6881

Datoteke in poti — Windows

Tip Vrednost
Arhiv %TMP%\kkXqPAFp
Imenik %TMP%\rKeKMWikl
Modul %TMP%\kkXqPAFp\w.node
Modul %TMP%\rKeKMWikl\f_ex86.node
Modul %TMP%\cTdTMfItnz\tlESkme
Modul %TMP%\cTdTMfItnz\WujOBGtse
Datoteka %TMP%\ggsuuck.exe
Datoteka %HOME%\init.json
Reg. ključ HKCU\Software\Microsoft\Windows\CurrentVersion\Run → UpdateLedger

Datoteke in poti — macOS

Tip Vrednost
Direktorij /tmp/ijewf/
Datoteka /tmp/ijewf/pwd
Datoteka /tmp/h.zip
Direktorij ~/.config/system/.data/.nodejs/
Datoteka ~/Library/LaunchAgents/com.user.nodestart.plist
Datoteka ~/init.json

Kriptografski artefakti

Tip Vrednost
AES-256-CBC key (S1) zetqHyfDfod88zloncfnOaS9gGs90ONX
AES-256-CBC IV a041fdaa0521fb5c3e26b217aaf24115 (hex)
AES-128-CBC key (S3, B64) caTY0D6roK1LHa02cA80jA==
AES-128-CBC IV /sXfxpU5Dr0kuXoihE6JFQ==
SHA-256 (f_ex86.node) a89dd9e5c813bf66d0738a70a616d97f6438917c423de347560e5ed2db3df5ff

Ostali artefakti

Tip Vrednost
HTTP glava uuid: 57f432c1-c5b7-4b51-8667-044847c63470
HTTP glava buildid: 2026-03-06T07:35:05.258Z
HTTP glava uuid_machine: dinamično (ioreg)
WebSocket token _partner: mulKRsVtolooY8S

Zaključek

GlassWorm je večstopenjska kampanja, ki izkorišča zaupanje razvijalcev v odprtokodne ekosisteme. Posebej zaskrbljujoča je kombinacija naprednih tehnik – prikrivanje z nevidnimi Unicode znaki, C2 resolucija prek blockchaina in DHT, ter modularna arhitektura, ki omogoča napadalcem daljinsko nadgradnjo zmogljivosti. Prisotnost v ~100 javnih repozitorijih kaže na sistematično kampanjo, ne na izolirani incident.

Organizacijam priporočamo pregled npm odvisnosti in git zgodovine na prisotnost nevidnih Unicode znakov, monitoring omrežnih klicev na identificirane IP-naslove ter preverjanje obstoja datoteke init.json v domačem imeniku na napravah razvijalcev.