Hoy veremos cómo podemos analizar un malware cualquiera con Radare2, ¿Por qué Radare2? Son muchas las razones, entre otras como veremos a continuación, se puede automatizar lo que se nos ocurra, podemos hacernos un script sencillo que nos permita de un vistazo rápido realizar búsquedas u obtener información que nos muestran gran variedad de páginas destinadas a analizar malware (sandboxies) y se encuentran online. Eso sí, hablaremos de análisis estático, no dinámico.
Para ello, me gustaría ir mostrando capturas de unas plataformas de tipo Sandbox e ir viendo lo que se obtiene con unas y otras y lo que se muestra con Radare2, vereis como se puede conseguir lo mismo, otra cosa es que para verlo más bonito haya que currárselo.
Cojemos una muestra cualquiera (Wannacry decryptor) : Aqui.
Se trata de uno de los ejemplos incluidos en la plataforma de análisis Intezer. Si alguno no la conoce, a grandes rasgos, “trocean” el malware de tal forma que son capaces de identificar el origen de esos trozos de código. Por lo que para detectar actores que se encuentran detrás de una muestra viene al dedillo.
Lo dicho, veamos que información hay en esta página y cómo obtener lo mismo con radare2.
Vemos información relativa al ejecutable y una serie de cadenas. Con radare2 podemos obtener esos datos con los siguientes comandos: “i/iI” (información del fichero), “iV” (version_info), “izz” (strings):
Cambiamos de plataforma y vamos a esta otra, con el mismo fichero. Tenemos un menú por donde navegar y tener la información necesaria del fichero.
Esta información ya habíamos visto como obtenerla. Pasamos a la cabecera del fichero:
Con el comando “iH” (información de la cabecera) podemos verlo.
Pasamos a las secciones:
Utilizamos los comandos: “iS md5,sha1,sha256” (información de las secciones y sus respectivos hashes) y por otro lado “iS=” (información de las secciones con barras)
Llegamos a los “imports” o funciones que se cargan y que va a utilizar el binario (pueden utilizar más cargándolas en tiempo de ejecución y no aparecer aquí).
Para ver cuales se cargan utilizamos el comando “ii” (información de los import). Vemos la DLL y el nombre de la función que se carga, además de otros datos.
Si queremos buscar alguna en concreto: ii~FUNCION .Podemos buscarlo “case sensitive” o “insensitive”.
Ficheros de Recursos:
Los buscamos con “iR”:
hybrid-analysis.com
Pasamos a otra plataforma y mismo fichero y vemos que datos aún no tenemos localizados:
Compiler/Packer: Microsoft visual C++ 5.0. Para obtener este tipo de dato podemos jugar con reglas yara.
Necesitaremos ejecutar los siguientes comandos para poder utilizar yara en radare2 .
r2pm -i yara-lib
r2pm -i yara
Después de completar los requerimientos necesarios, ya podemos utilizarlo.
Localizamos nuestra regla con los “packers” y/o “compilers”, lo añadimos y buscamos coincidencias.
Esa coincidencia corresponde con “Microsoft_Visual_Cpp_50”. El resto de datos ya los hemos visto anteriormente:
Si nos vamos a la parte de indicadores, se apoyan en plataformas como VirusTotal o reglas yara (hemos visto cómo utilizarlas desde radare2).
Nos quedamos con este dato por el momento:
A la hora de buscar API sospechosas es mejor buscarlo como cadenas, lo digo porque muchas veces no están en los imports, se cargan en tiempo de ejecución, esto es un truco muy común, LoadLibrary + GetProcAddress, que vereis muchas veces y esa función que va a utilizar está como un string o cadena. No la busquéis en los imports, se escapará.
Imaginaros que veis esto y queréis investigar un poco más en el binario:
Buscamos “Wana Decrypt0r 2.0” (izz~+Wana Decrypt0r 2.0), ¿desde donde se llaman? (axt@0x00420cd0), nos posicionamos allí (s 0x00420cd0, para el primer caso, idem siguientes), pdf para imprimir el contenido del desensamblado por pantalla, al no ser una función, le decimos que lo trate como tal (af) y volvemos a imprimirlo.
Y vemos que hace esa función a la que se le pasa como parámetro esa cadena que andábamos buscando.
Imprimimos su contenido (“pdf @ fcn.0040b620”), como veis trata de buscar esa ventana, con ese título.
En todas las plataformas de análisis online vienen referencias a Virus Total, no podemos obviar esta parte, ya que además de la detección de más de 60 antivirus, podemos observar el comportamiento del malware en diferentes sandboxes. Por lo que esa información vendría de perlas tenerla a mano desde radare2.
Podemos cargar cualquier script que tengamos desde radare2, ya lo sé, es ejecutar algo externo y no viene con la herramienta, lo mismo ocurría con las reglas yara. Lo que quiero que veais es que si algo no está integrado, siempre se puede buscar una solución.
Por ejemplo, podemos cargar un script que con el hash como parámetro nos diga el número de detecciones que hay así como el enlace de una de las sandboxes utilizadas donde se ve el comportamiento que os he mencionado antes.
Pasamos a identificar las funciones existentes, para ello tecleamos “afl”.
Imaginaros que queréis saber cuáles de ellas se utilizan más veces, esto es muy útil en el caso de funciones que descifran cadenas, por poner algún ejemplo, utilizad “afll” y saldréis de dudas.
Como podéis ver existe la función “main”. Por defecto, cuando cargamos el binario aterrizamos en el entrypoint (“entry0”).
Podemos ver varias formas de obtener un desensamblado. Con “pdc” tendríamos una sintaxis tipo C. Con “pdg” el desensamblado de Ghidra. Este último hay que instalarlo, no viene por defecto. Para ello ejecutad los comandos:
r2pm update
r2pm -ci r2ghidra
También podemos sacar gráficos al estilo IDA:
Comandos Radare2
El resumen de los comandos que hemos visto hasta ahora sería:
- “i/iI” (información del fichero)
- “iH” (cabecera PE)
- “iV” (version_info)
- “it” (hashes principales del binario)
- “iS md5,sha1,sha256” (secciones con hashes)
- “iS=” (secciones con bar)
- “ii” (imports)
- “iR” (información de los recursos)
- “izz” (strings)
- “yara add ./peid.yar” (añadimos fichero de reglas yara para la identificación de compiladores y empaquetadores)
- “yara scan” (búsqueda de coincidencias con la regla yara añadida)
- “!python VTScan.py bf293bda73c5b4c1ec66561ad20d7e2bc6692d051282d35ce8b7b7020c753467” (consulta en VT sobre malware)
- “pdc“ (desensamblado estilo C)
- “pdg“ (desensamblado estilo C de ghidra)
- “agf“ (grafico estilo IDA)
Script Analisis de Malware by Rafa
Podemos crear un simple script con Python y mostrar esta misma información, os adelanto que la salida del fichero será grande, ya sólo el comando para la obtención de las cadenas es gigantesco.
Utilizaremos para ello r2pipe (“pip install r2pipe”). Perdonadme por el script, deprisa y corriendo es lo que sale :D Es muy mejorable, parseando la salida (muchos comandos de radare2 pueden terminar en j y su salida es en formato json) y crear una página html, por ejemplo, en definitiva, lo que se os ocurra.
import r2pipe
import sys
import os
import re
def cr():
print '\n'
def use():
print "Simple Malware Analysis with Radare2 and r2pipe - by Rafa"
print "Use: \"" + sys.argv[0] + "\" \"<binary.exe>\""
sys.exit(1)
if len(sys.argv) != 2:
print ("You need an argument to filename")
use()
path = sys.argv[1]
if os.path.exists(path):
path = sys.argv[1]
r2=r2pipe.open(path)
pe = r2.cmd("e file.type").split('\n')[0]
if pe not in ('pe','elf'):
print "The file \"" + path + "\" is not a PE/ELF"
use()
arch = r2.cmd("e asm.arch").split('\n')[0]
r2.cmd("e asm.arch = " + arch)
r2.cmd("aaa")
r2.cmd("e scr.rows = 100")
r2.cmd("e scr.columns = 80")
r2.cmd("e scr.utf8 = true")
r2.cmd("e asm.emu = false")
r2.cmd("e asm.section = true")
r2.cmd("e asm.section.col = 14")
sha256sum = r2.cmd("it~sha256[1]")
print ("\nInformation File\n"+"=" * 80)
info = r2.cmd("i")
print info.encode('utf-8')
print ("\nInformation Headers\n"+"=" * 80)
infoH = r2.cmd("iH")
print infoH.encode('utf-8')
print ("\nInformation VersionInfo\n"+"=" * 80)
infoV = r2.cmd("iV")
print infoV.encode('utf-8')
print ("\nHashes\n"+"=" * 80)
hashes = r2.cmd("it")
print hashes.encode('utf-8')
iS = r2.cmd("iS").replace('[Sections]',("\nSections\n"+"=" * 80))
print iS.encode('utf-8')
print ("\nEntropy\n"+"=" * 80)
iSe = r2.cmd("iS md5,sha1,sha256")
print iSe.encode('utf-8')
iSeB = r2.cmd("iS=")
print iSeB.encode('utf-8')
print ("\nResources\n"+"=" * 80)
iR = r2.cmd("iR")
print iR.encode('utf-8')
ii = r2.cmd("ii").replace('[Imports]',("\nImports\n"+"=" * 80))
print ii.encode('utf-8')
print ("Functions\n"+("=" * 80)+"\n")
afl = r2.cmd("afl")
print afl.encode('utf-8')
izz = r2.cmd("izz").replace('[Strings]',("\nStrings\n"+"=" * 80))
print izz.encode('utf-8')
print ("Graphics\n"+("=" * 80)+"\n")
agf = r2.cmd("agf")
print agf.encode('utf-8')
print ("Packers/Compilers\n"+("=" * 80)+"\n")
packer = r2.cmd("yara add ./peid.yar")
packer = r2.cmd("yara scan")
print packer.encode('utf-8')
print ("Virus Total Information\n"+("=" * 80)+"\n")
VTinfo = r2.cmd("!python VTScan.py "+sha256sum)
print VTinfo.encode('utf-8')
urls = [
re.compile("((http|ftp|mailto|telnet|ssh)(s){0,1}\:\/\/[\w|\/|\.|\#|\?|\&|\=|\-|\%]+)+", re.IGNORECASE | re.MULTILINE)
print ("URLs\n"+("=" * 80)+"\n")
for findurl in urls:
for izzfound in findurl.findall(izz, re.IGNORECASE | re.MULTILINE):
for url in izzfound:
if len(url) > 8 and url:
print (url)
cr()
suspicious =
"RegCloseKey|GetUserNameA|RegCreateKeyW|CopyFileW|GetDriveTypeW|LoadLibraryA|GetFileAttributesA|FindFirstFileW|CopyFileA|GetFileAttributesW|GetModuleFileNameA|GetStartupInfoA|FindNextFileW|GetFileSize|CreateDirectoryA|DeleteFileA|GetProcAddress|CreateThread|GetModuleHandleA|FindFirstFileA|WriteFile|GetTempFileNameA|GetComputerNameA|FindNextFileA|TerminateProcess|CreateProcessA|Sleep|CreateFileA|GetTickCount|ShellExecuteExA|ShellExecuteA|FindWindowW|recv|socket|bind|send|WSAStartup|connect|closesocket|URLDownloadToFileA"
print ("Suspicious\n"+("=" * 80)+"\n")
for func in suspicious.split('|'):
for izzline in izz.split('\n'):
if func in izzline:
print ("[Suspicious]: " + func + " [" + izzline + "]")
cr()
antidbgs =
"CheckRemoteDebuggerPresent|FindWindow|GetWindowThreadProcessId|IsDebuggerPresen
t|OutputDebugString|Process32First|Process32Next|TerminateProcess|UnhandledExcep
tionFilter|ZwQueryInformation"
print ("Antidebugs\n"+("=" * 80)+"\n")
for func in antidbgs.split('|'):
for izzline in izz.split('\n'):
if func in izzline:
print ("[Antidebugs]: " + func + " [" + izzline + "]")
cr()
else:
print "The file \"" + path + "\" not exists!"
sys.exit(1)```
Veamos la salida del script. Lo primero que vemos es la información del fichero:
Seguido de la información de las cabeceras:
El “Version Info”:
Las secciones:
Los recursos:
Los “imports”:
Las funciones que son detectadas:
Las strings:
Podemos ver todas las extensiones que buscaba para descifrar su contenido.
O como nos informaba como los ficheros podían se descifrados si pasabas por caja, virtual, claro.
Los gráficos, por defecto el entry0:
Los compiladores/empaquetadores y la información de Virus Total:
Las URLs y cadenas sospechosas:
Trucos antidebug:
Como habéis podido ver y leer, con un sencillo script en Python hemos accedido a la herramienta radare2, ejecutado una serie de comandos para obtener la misma información que aparece en cualquier plataforma de análisis online del tipo Sandbox.
Espero haberos dado una visión de lo que se puede hacer con herramientas como radare2 a la hora de analizar un malware cualquiera. Hay mucho más que ver, debugging, ESIL, etc. Quizás en otro POST.
Hasta otra!!