Smantellamento di KillDisk: il rovescio della componente distruttiva di BlackEnergy

[post-views]
Febbraio 29, 2016 · 12 min di lettura
Smantellamento di KillDisk: il rovescio della componente distruttiva di BlackEnergy

Saltiamo la lunga introduzione sulla minaccia BlackEnergy e passiamo direttamente a studiare il componente malware chiamato “ololo.exe”, noto al pubblico anche come KillDisk. KillDisk è un modulo del framework BlackEnergy mirato alla distruzione dei dati e alla creazione di caos/distrazione durante le operazioni APT.

https://www.virustotal.com/en/file/11b7b8a7965b52ebb213b023b6772dd2c76c66893fc96a18a9a33c8cf125af80/analysis/Gli strumenti principali utilizzati nella nostra analisi di oggi sono Process Monitor che fa parte delle utility SysInternals di Mark Russinovich e IDA Pro Disassembler. Tutte le manipolazioni saranno eseguite in un ambiente virtuale basato sul sistema operativo Windows XP. Iniziamo con una rapida configurazione iniziale della VM di test, accendiamo la macchina e creiamo uno snapshot chiamato “Prima dell’infezione”. Cominciamo!

Per tenere traccia di tutti gli eventi relativi al nostro oggetto di studio lanciamo Process Monitor e assicuriamo che rimanga visibile:BlackEnergy-killdisk-1E che tracci il processo necessario:

BlackEnergy-killdisk-2Successivamente, carichiamo il virus in IDA Pro Disassembler e vediamo la seguente immagine.Questa è la funzione WinMain, cioè la funzione principale, quindi iniziamo la sua analisi e vediamo che la prima (e unica) cosa che fa sarà chiamare la procedura denominata sub_40E070:

 BlackEnergy-killdisk-3
Quindi dirigiamoci direttamente a quella procedura. Qui possiamo trovare procedure responsabili per l’estrazione delle estensioni dei file in memoria e la prima procedura subito dopo di esse che chiama una cosa interessante chiamata sub_40E080:

BlackEnergy-killdisk-4Diamo un’occhiata più da vicino ai suoi contenuti:BlackEnergy-killdisk-5Il suo interno contiene una interessante chiamata di sistema CreateFile e un paio di procedure sub_40C390 and sub_40C400. Quindi è ora di fare uno snapshot del nostro laboratorio virtuale e continuare il nostro viaggio!

Esaminiamo la chiamata alla funzione CreateFile, impostiamo il BreakPoint su di essa ed eseguiamo il nostro campione di studio:

BlackEnergy-killdisk-6Un piccolo suggerimento riguardo questa funzione:

CreateFile la funzione crea o apre un file o dispositivo I/O. I dispositivi I/O più comunemente utilizzati sono i seguenti: file, flusso di file, directory, disco fisico, volume, buffer console (CONIN$ or CONOUT$), unità a nastro, risorsa di comunicazione, mailslot e pipe con nome. La funzione restituisce un handle che può essere utilizzato per accedere al file o al dispositivo per vari tipi di I/O a seconda del file o del dispositivo e dei flag e attributi specificati.

Come sappiamo la maggior parte dei compilatori passa gli argomenti a una funzione attraverso uno stack e dato che stiamo trattando con un campione inizialmente scritto in C, e secondo le direttive C gli argomenti sono spinti sullo stack da destra a sinistra in modo tale che il primo argomento della funzione sia spinto per ultimo nello stack e finisca per essere il primo elemento sulla sua cima. [1].

There is a special register for working with the stack – ESP (Extended Stack Pointer). The ESP, by definition, always points to the top of the stack:

BlackEnergy-killdisk-41Uno sguardo nello stack:BlackEnergy-killdisk-7L’ultimo argomento che è stato spinto nello stack (uno sulla sua cima) è il primo argomento accettato dalla funzione (secondo la sintassi della CreateFile funzione) e contiene il nome del file nel nostro caso \.PHYSICALDRIVE0, quindi il target è il primo drive fisico.BlackEnergy-killdisk-8Ora analizziamo altri argomenti inviati alla funzione prendendo i loro valori dallo stack:BlackEnergy-killdisk-9Alcune conclusioni:

  • lpFileName – puntatore a una stringa ASCIIZ contenente il nome (percorso) del file aperto o creato (come abbiamo già scoperto);
  • dwDesiredAccess – tipo di accesso al file: nel nostro caso il valore è 0C0000000h che significa GENERIC_READ+GENERIC_WRITE or accesso in lettura-scrittura;
  • DwShareMode – una modalità che permette la condivisione dei file con diversi processi e questo parametro può avere diversi valori nel nostro caso il valore di 00000003b si traduce inFILE_SHARE_READ+FILE_SHARE_WRITE oaltri processi possono aprire il file per lettura/scrittura;
  • IpSecurityAttributes – puntatore alla struttura SecurityAttributes (file winbase.h) che definisce impostazioni di sicurezza relative all’oggetto file del kernel, se la sicurezza non è impostata viene usato un valore NULL ;
  • dwCreationDistribution – è responsabile per decidere le azioni quando il file esiste o non esiste, nel nostro caso un valore di 3 significa OPEN_EXISTING or apri il file se esiste, e restituisci un errore se non esiste;
  • DwFlagsAndAttributes – flag e attributi; questo parametro è utilizzato per impostare le caratteristiche del file creato ed è ignorato in modalità di lettura 0.
  • hTemplateFile – parametro utilizzato solo quando viene creato un nuovo file 0.
  • Al successo la funzione restituisce il nuovo handle del file a ЕАХ E se la funzione fallisce un NULL viene scritto nel ЕАХ registro.

Bene quindi dopo aver chiamato CreateFile otteniamo un valore non zero per EAX il che significa che contiene l’handle del file richiesto e.g. \.PHYSICALDRIVE0:

BlackEnergy-killdisk-26Abbiamo ottenuto il nostro handle quindi procediamo alla chiamata successiva, sub_40C390:

BlackEnergy-killdisk-10Come prima diamo un’occhiata dentro la procedura e vediamo un’altra chiamata interessante:BlackEnergy-killdisk-11Suggerimento della funzione:

DeviceIoControl la funzione invia un codice di controllo direttamente a un driver del dispositivo specificato, facendo sì che il dispositivo corrispondente esegua l’operazione corrispondente.

Uno sguardo nello stack proprio prima di chiamare questa funzione:BlackEnergy-killdisk-11Conclusioni:

  • hDevice – descrittore del dispositivo (riconosciamo un vecchio amico qui \.PHYSICALDRIVE0);
  • dwIoControlCode – codice dell’operazione. Nel nostro caso il valore è 0x70000h che significa IOCTL_DISK_GET_DRIVE_GEOMETRY o recupero delle informazioni riguardanti la geometria del disco (quantità di cilindri, tipo di media, tracce per cilindro, settori per traccia, byte per settore);
  • lpInBuffer – buffer con dati di input. Non abbiamo bisogno di alcun dato di input quindi NULL lo è;
  • nInBufferSize – dimensione del buffer di input in byte pari a 0 nel nostro caso;
  • lpOutBuffer – puntatore al buffer di output. Il suo tipo è impostato dal parametro dwIoControlCode 0x0011FCA8h;
  • nOutBufferSize – dimensione del buffer di output in byte 0x18h (24);
  • lpBytesReturned – un puntatore a una variabile che contiene la quantità di byte che sono scritti nell’output 0x0011FCA4h;
  • lpOverlapped – puntatore a una struttura OVERLAPPED;
  • Mentre la chiamata viene elaborata guardiamo nel buffer (all’indirizzo che è stato inviato come argomento a lpOutBuffer precisamente 0x0011FCA8h):

BlackEnergy-killdisk-12Interpretiamo la risposta:

  • All’indirizzo di 0x0011FCA4h abbiamo un ritorno della quantità di byte scritti nel buffer di output come previsto dalla funzione (contrassegnato in verde) ne abbiamo ricevuti quanti ne abbiamo chiesti 24 simboli.
  • All’indirizzo di 0x0011FCA8h abbiamo ottenuto informazioni sulla geometria del primo disco fisico (\.PHYSICALDRIVE0):
    • quantità di cilindri – 0x519h (1305)
    • tipo di media – 0x0Ch (12) che significa disco rigido fisso.
    • tracce per cilindro – 0x0FFh (255)
    • settori per traccia – 0x3Fh (63)
    • byte per settore – 0x200 (512)

Ora ci riferiamo alla procedura tre, sub_40C400:

BlackEnergy-killdisk-13All’interno della procedura due chiamate hanno immediatamente attirato la nostra attenzione, specificamenteSetFilePointerExeWriteFile:

BlackEnergy-killdisk-14Un suggerimento sulla funzione:

SetFilePointerEx la funzione sposta il puntatore del file del file aperto specificato.

Come da tradizione consolidata guardiamo nello stack della chiamata alla funzione:BlackEnergy-killdisk-15

  • hfile – il nostro familiare descrittore del dispositivo \.PHYSICALDRIVE0;
  • liDustanceToMove – posizione iniziale per l’inizio di una scrittura 0;
  • lpNewFilePointer – puntatore a una variabile che recupera un nuovo puntatore di posizione dal file 0x0011FCA8h. In un caso in cui il parametro è VUOTO (NULL) un nuovo puntatore non viene restituito nel file;
  • dwMoveMethod – dimensione del buffer di input in byte. Nel nostro caso 0x200h (512) – byte nel settore;

Ora il nostro “mostro” usa la WriteFile funzione e per riempire il file di zeri a cui ha già stabilito accesso diretto e affidabile.

Un suggerimento sulla funzione:

Una WriteFile funzione Scrive dati nel file o dispositivo di input/output (I/O) specificato a partire dalla posizione specificata nel file. Questa funzione è progettata per operazioni sia sincrone che asincrone.

Elenco degli argomenti della funzione:BlackEnergy-killdisk-16Bene, per assicurarci di non perdere nulla di interessante diamo un’occhiata al cruscotto e tra tutte le cose prestiamo attenzione allo stack:BlackEnergy-killdisk-17Tutto pronto per l’azione:

  • 1 – restando sulla chiamata alla funzione WriteFile
  • 2 – Process Monitor attende ansiosamente il minimo movimento dei “muscoli” del nostro “mostro”
  • 3 – argomenti spinti dai registri nello stack
  • 4 – e bene, lo stack stesso
  • Un quadrato dedicato nella finestra Hex View-EBP abbiamo contrassegnato un’area che viene indirizzata dal secondo argomento della funzione (data buffer) e puoi credere sulla parola che questa – contiene esattamente 0x200h (512) di zeri.

Eseguiamo il comando premendo il tasto F8 e osserviamo il cambiamento:BlackEnergy-killdisk-18Come ci aspettavamo un acuto occhio di Process Monitor è puntato e fissa la scrittura di 512 byte a partire dalla posizione 0.

Poi la procedura esce e viene chiamata di nuovo ma ora con valori diversi:

Ora prendiamo i successivi 512 byte che è particolarmente visibile a 0x200 (a differenza degli argomenti dello stack precedenti che abbiamo contrassegnato con un riquadro blu)BlackEnergy-killdisk-19E subiscono lo stesso destino dei precedenti:BlackEnergy-killdisk-20Questo carosello continua fino a quando il controllo di 0x40C865h indirizzo restituisce il valore EBX registro uguale a 0x100h (255 volte ):

BlackEnergy-killdisk-21Per essere certi che ciò accada mettiamo un punto di arresto sull’istruzione che segue il nostro ciclo vizioso di “distruzione”:

BlackEnergy-killdisk-22Come totale riceviamo un 256 di operazioni di riscrittura.BlackEnergy-killdisk-23Questo è fondamentalmente tutto, il nostro “mostro” ha fatto il suo lavoro sporco e chiude l’handle del file chiamando la CloseHandle funzione.

Suggerimento della funzione:

Una CloseHandle funzione chiude un handle di un oggetto aperto.

Argomenti della funzione:

BlackEnergy-killdisk-24La nostra tradizionale visione della preparazione dello stack:

BlackEnergy-killdisk-25E cosa abbiamo nel registro ESI?BlackEnergy-killdisk-26Come previsto il nostro familiare descrittore 0x44h viene passato allo stack come argomento della funzione.

Vediamo i valori restituiti dalla funzione nel registro EAX: BlackEnergy-killdisk-27Se la funzione termina con successo il valore restituito è non nullo.

Se la funzione esiste con un errore il valore restituito è nullo.

Questa azione non è sfuggita all’occhio vigile dei nostri Process Monitor:

BlackEnergy-killdisk-28Tutto è andato come al solito (se si può definire normale la distruzione della parte più importante del disco di sistema) e il file è chiuso.

Andiamo avanti:BlackEnergy-killdisk-29Per una percezione più confortevole ho rinominato la funzione in Eraser. Dopo che ha distrutto con successo i dati su PhysicalDrive0, il contatore situato nel ESI registro è aumentato di 1, controllato con il valore di 0x0Ah (10) e lancia operazioni di riscrittura con un nuovo valore:BlackEnergy-killdisk-30e così via fino a raggiungere 10.

In questo modo il “mostro” continua a camminare su tutti i dischi fisici e cancellare il primo 512 * 255 = 128kb (questo dipende dalla quantità di byte in un settore che il “mostro” ha appreso conoscendo la geometria del disco).

Questa volta tuttavia, il risultato della funzione CreateFileW sarà un valore seguente: BlackEnergy-killdisk-31Il che significa che nel nostro «laboratorio improvvisato» non esiste un secondo disco fisico (o bene non esiste nessun tipo di disco con un numero di sequenza maggiore di 0) e il ciclo non ha nulla da riscrivere.part1_op_enOra che siamo armati di una conoscenza appena acquisita proviamo a disabilitare la funzione distruttiva che abbiamo appena appreso modificando il codice in modo tale che la nostra modifica possa permettere di bypassare la riscrittura dei dati:

BlackEnergy-killdisk-33Ricordiamo la procedura sub_40E080 che contiene la chiamata alla funzione CreateFileW. Dato che sappiamo che il valore di 0xFFFFFFFFh sarebbe comparso dopo il completamento della funzione CreateFileW solo nel caso in cui il drive fisico non è presente e in tale scenario la funzione di “distruzione dati” esegue un’uscita, modifichiamo un’istruzione di branch condizionale situata all’indirizzo 0x0040C7E4h a una incondizionata:

 BlackEnergy-killdisk-34
IDA Pro è uno strumento interessante, quindi dobbiamo solo scrivere un comando.

L’unica cosa che dobbiamo considerare è la grandezza del comando prima e dopo, quindi verifichiamo quanti byte vengono utilizzati dalla sequenza condizionale:

BlackEnergy-killdisk-35Come possiamo vedere, un’istruzione di branch condizionale equivale a 6 byte mentre la sequenza incondizionata occuperà solo 5 e quindi aggiungeremo un altro comando – NOP, per mantenere le cose uguali:BlackEnergy-killdisk-36NOP è il prossimo comando indipendentemente da ciò che conteneva prima (anche se vediamo uno 0 lì):

BlackEnergy-killdisk-37Facciamo un doppio controllo per verificare che gli indirizzi dei comandi siano ora equalizzati basandoci sull’indirizzo del comando successivo:

BlackEnergy-killdisk-38e premiamo annullare. Fatto.

Dopo queste operazioni, la procedura sembrerà semplice: entra ed esci, creando un ciclo vuoto (poiché non sappiamo quali altre parti del programma possano connettersi ad esso, lo lasciamo in questo stato).BlackEnergy-killdisk-39La prova finale individuata da Process Monitor indica che nessun disco fisico è stato danneggiato a seguito dell’esecuzione analizzata di KillDisk:

BlackEnergy-killdisk-40Il malware ha letto le informazioni sul disco, le ha esaminate e ha continuato con la sua attività che analizzeremo in dettaglio nei nostri prossimi articoli.

Fonti:

[1] – Hacker Disassembling Uncovered. Kris Kaspersky, 2009.

Tradotto dall’originale di Andrii Bezverkhyi | CEO SOC Prime

Questo articolo è stato utile?

Metti mi piace e condividilo con i tuoi colleghi.
Unisciti alla piattaforma Detection as Code di SOC Prime per migliorare la visibilità sulle minacce più rilevanti per il tuo business. Per aiutarti a iniziare e ottenere valore immediato, prenota ora un incontro con gli esperti di SOC Prime.