Análisis de malware (II) - Análisis estático básico: strings y metadatos
Ha llegado el día. Es hora de retomar lo que comencé hace meses y continuar con la seria sobre análisis de malware. Esta es la segunda parte de una serie de artículos. En el articulo anterior hablaba sobre los conceptos básicos, los diferentes tipos de técnicas para analizar malware, como conseguir muestras y algunos programas básicos para realizar este tipo de análisis.
Antes de nada, es necesario recordad los diferentes tipos de análisis que existen. Básicamente se dividen en 4 tipos diferentes:
- Análisis estático: consiste en analizar información sobre el malware sin analizar su código ni ejecutarlo: metadatos, firmas, formato y secciones del binario, etc.
- Análisis dinámico: consiste en observar el comportamiento del malware mientras se ejecuta: ficheros con los que interactúa, llamadas al sistema, trafico de red, cambios en el registro, etc.
- Análisis de código: consiste en observar el código y se distingue en dos tipos:
- Análisis estático de código: analizar el código sin ejecutarlo.
- Análisis dinámico de código: analizar el código mientras se ejecuta, es decir, depurarlo.
No hay una norma estricta sobre el orden en el que aplicar las técnicas o por donde empezar. Aun así, hay ciertas cosas que tienen sentido hacerlas antes que otras. Analizar código, por ejemplo, no es lo más sencillo de realizar, por lo que no suele ser lo primero que se hace. El análisis dinámico necesita capturar mucha información, por lo que si es algo que vamos a hacer nosotros en local, tiene sentido intentar tener un conocimiento básico sobre la muestra que se analiza para poder “centrar el tiro”: decidir que elementos monitorizar y priorizar que información analizar primero.
El tipo de análisis más sencillo que se puede realizar es el análisis estático. No requiere ni analizar el código ni tener que poner toda una serie de programas a monitorizar. Usando únicamente análisis estático se puede sacar mucha información sobre el tipo de archivo, metadatos con pistas interesantes, el tipo de malware que puede ser, etc.
Por ello, este tipo de técnicas suelen ser las primeras a realizar ya que son rápidas de realizar y permiten clasificar el malware. ¿En que consiste clasificar malware? Analizar malware sirve para, por un lado, ver de que tipo de malware se trata y como encaja dentro de todo el malware que existe (a que “familia” de malware pertenece) y, por otro lado, para entender como se comporta y aprender de ello. La primera de ellas es lo que se llama clasificación.
Esto es porque, por lo general, las muestras de malware suelen ser variaciones de otras. En programación, cuanto más se pueda reciclar, mejor, y en el caso del malware no va a ser diferente. Buscando patrones, firmas y características que ya se han encontrado en otras muestras permite saber de qué tipo de malware se trata mucho antes de tener que ver cómo esta programado o cómo se comporta, ahorrando mucho tiempo. Además, si se sabe que tipo de malware es, analizar su código es más sencillo (ya se sabe las características y particularidades que se quieren buscar) y ya se pueden tener pistas sobre como se va a comportar basado en como se comportan malware similar.
Requisitos⌗
Con todo esto, vamos a ver como se puede obtener información básica de una muestra. Para esto, obviamente, se necesita una muestra de malware. Yo voy a usar una que ya analicé en su momento de un ransomware Ryuk. El hash SHA-256 de la muestra es el siguiente:
7faeb64c50cd15d036ca259a047d6c62ed491fff3729433fefba0b02c059d5ed
Para esto vale cualquier muestra, solo que depende de cuál se use se va a obtener resultados diferentes. Obviamente, como las muestras cogidas de repositorios de malware como esta ya son conocidas, ya nos hemos comido el spoiler sobre que tipo de muestra es. Analizando una muestra desconocida esto no pasaría. Aun asi, lo interesante es ver hasta que punto somos capaces nosotros mismos de sacar informacion de la muestra.
Por otro lado, es necesario tener un entorno seguro donde analizar la muestra. Para ello, es necesario usar un entorno virtualizado. El el artículo anterior mencionaba varias opciones. Aunque en este caso la muestra sea para Windows, esta parte se puede hacer tanto con máquinas Linux como con Windows ya que, por el momento, no vamos a ejecutar nada. Para la parte dinámica es necesario tener una máquina virtual Windows. En mi caso, para mostrar un poco de todo, voy a usar tanto una máquina virtual Windows como una Linux, concretamente Remnux. Me gusta ir combinando las dos ya que hay herramientas que solo se encuentran en uno de los sistemas. Además, me siento más comodo con la terminal de Linux, pero a veces prefiero algunos programas para Windows. Mientras se use la misma muestra en ambos no hay problema. Podemos comprobar que tenemos la misma muestra en ambas basta con verificar la integraidad de la muestra con su hash.
Ya con esto, podemos empezar a meterle mano a la muestra.
Cálculo de hashes⌗
Lo primero es calcular los hashes de la muestra. Si se obtiene de algún repo, ya se tiene el hash y simplemente es necesario comprobarlo para comprobar su integridad; o lo que es lo mismo, garantizar que tenemos la muestra que queríamos. Se puede realizar con alguna herramienta, como 7Zip o HashCalc para Windows o usar algún comando como los que ya vienen en algunos sistemas, como la mayoría de Linux.
Si usamos varias máquinas, conviene hacer esto en todas para asegurar que se tiene la misma muestra cargada en todas las máquinas.
Vale, ya tenemos la muestra en nuestro sistema y lista para analizar. ¿Por dónde se puede empezar?
En realidad, como comentaba, no hay un orden definido para esto, pero si que es cierto que hay algunos procesos básicos que podemos realizar muy rápidamente y que suelen ser los primeros que se realizan, tanto por lo fáciles que son como por la información que pueden aportar a la hora de realizar otro tipo de análisis más complejos, como los análisis de código o de comportamiento. En esta parte vamos a realizar un par de técnicas básicas de análisis estático: analizar strings y metadatos.
Tipo de muestra⌗
Aunque en este caso se sabe que es una muestra de malware para Windows, por regla general conviene, antes que nada, determinar qué tipo de muestra tenemos ante manos.Si la muestra fuese desconocida, esto nos permitiria saber si se trata de un binario o no y, para el primer caso, ver para que arquitectura y sistema operativo. Dependiendo de esto, sera necesario unas máquinas, herramientas y técnicas u otras. La forma mas sencilla es usar algo como el comando file
de Linux.
Con esto comprobamos lo que ya sabiamos, que es un ejecutable para Windows de 32 bits. Vemos que menciona que es un ejecutable PE32. Más adelante indagaremos en el formato de los binarios PE32 y como analizar mas en detalle su estructura para obtener informacion de este tipo de muestras. De momento, ya que sabemos que es un binario, vamos a intentar extraer sus strings.
Análisis de strings⌗
Cualquiera que haya programado alguna vez ha tenido que leer código. De hecho, lo normal es pasarse más tiempo leyendo código que escribiéndolo. Cualquier programador con un mínimo de experiencia se ha tenido que sentar alguna vez delante de alguna codebase para ver como funciona algún programa; porque le han pasado ese proyecto en el trabajo, porque ha forkeado un proyecto y quiere ampliarlo o porque esta leyendo una respuesta de Stack Overflow. Sea por el motivo que sea, una de las formas más fáciles de entender lo que hace el código es fijarse en los mensajes que muestra o en los strings que contiene. Esos prints y logs que informan al usuario y al desarrollador también informan a otros programadores. Por ello, una de las cosas más básicas que se puede hacer al analizar malware es analizar strings.
Pero hay un problema. Normalmente, las muestras de malware que vamos a encontrar son muestras compiladas. No nos van a alegrar el día enseñándonos el código. asi que, ¿Cómo lo hacemos? ¿Ingeniería inversa?
Sí y no. Aunque haciendo reversing podemos obtener un código desensamblado o incluso un código a más alto nivel, todavía no es necesario. Un string no deja de ser un “churro” de bytes con una codificación concreta, como ASCII o UTF-8 por lo que con intentar pasar los bytes que tenemos a una codificación concreta para ver si conseguimos algo legible, podremos ser capaces de encontrar esos strings.
Obviamente, no hay que andar haciendo esto a mano. Hay muchas herramientas para hacer esto. Muchos editores hexadecimales tienen opciones para búsquedas de strings. En Windows, la manera más sencilla es usar strings.exe
, una SysInternal de Windows que extrae strings de un binario. Las SysInternal de Windows son herramientas muy potentes para tanto para administradores de sistemas Windows como para analistas de malware. Tenerlas en tu arsenal es un must. El equivalente de esta herramienta en Linux es el comando strings
.
Ejecutándola con el binario podemos ver los strings que detecta (he acortado la salida ya que da mucha basura y falsos positivos).
!This program cannot be run in DOS mode.
.text
.rdata
@.data
.rsrc
DllUnregisterServer
catsrv.dll
IsProcessorFeaturePresent
GlobalUnlock
GetUserDefaultUILanguage
GetCurrentProcess
QueryPerformanceCounter
GetFileAttributesW
LoadLibraryExW
CloseHandle
InitializeCriticalSectionAndSpinCount
InitializeSListHead
GetCurrentThreadId
LoadLibraryA
TerminateProcess
CreateEventW
GetModuleHandleW
GetProcAddress
SetUnhandledExceptionFilter
VirtualProtectEx
UnhandledExceptionFilter
GlobalAlloc
GlobalLock
DeleteCriticalSection
IsDebuggerPresent
GetModuleFileNameW
GetCurrentProcessId
GetLastError
OutputDebugStringW
GetStartupInfoW
kernel32.dll
DllGetClassObject
msident.dll
CoUninitialize
CoInitialize
CoCreateGuid
ole32.dll
UuidCreate
rpcrt4.dll
CloseClipboard
EnableWindow
DrawIcon
IsIconic
EmptyClipboard
OpenClipboard
GetClientRect
SendMessageW
GetSystemMenu
GetParent
GetForegroundWindow
SetClipboardData
LoadIconW
AppendMenuW
GetSystemMetrics
user32.dll
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns = 'urn:schemas-microsoft-com:asm.v1' manifestVersion = '1.0'>
<trustInfo xmlns = "urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level = 'asInvoker' uiAccess = 'false' />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
VeriSign, Inc.1+0)
"VeriSign Time Stamping Services CA0
070615000000Z
120614235959Z0\1
VeriSign, Inc.1402
+VeriSign Time Stamping Services Signer - G20
http://ocsp.verisign.com0
"http://crl.verisign.com/tss-ca.crl0
TSA1-20
Western Cape1
Durbanville1
Thawte1
Thawte Certification1
Thawte Timestamping CA0
031204000000Z
131203235959Z0S1
VeriSign, Inc.1+0)
"VeriSign Time Stamping Services CA0
http://ocsp.verisign.com0
0http://crl.verisign.com/ThawteTimestampingCA.crl0
TSA2048-1-530
VeriSign, Inc.1
VeriSign Trust Network1;09
2Terms of use at https://www.verisign.com/rpa (c)09100.
'VeriSign Class 3 Code Signing 2009-2 CA0
Moscow1
Moscow1
Kaspersky Lab1>0<
5Digital ID Class 3 - Microsoft Software Validation v21
Technical dept1
Kaspersky Lab0
3http://csc3-2009-2-crl.verisign.com/CSC3-2009-2.crl0D
https://www.verisign.com/rpa0
http://ocsp.verisign.com0?
3http://csc3-2009-2-aia.verisign.com/CSC3-2009-2.cer0
VeriSign, Inc.1705
.Class 3 Public Primary Certification Authority0
090521000000Z
190520235959Z0
VeriSign, Inc.1
VeriSign Trust Network1;09
2Terms of use at https://www.verisign.com/rpa (c)09100.
'VeriSign Class 3 Code Signing 2009-2 CA0
https://www.verisign.com/cps0*
https://www.verisign.com/rpa0
#http://logo.verisign.com/vslogo.gif0
http://ocsp.verisign.com01
http://crl.verisign.com/pca3.crl0)
Class3CA2048-1-550
xEv1
Washington1
Redmond1
Microsoft Corporation1)0'
Microsoft Code Verification Root0
060523170129Z
160523171129Z0_1
VeriSign, Inc.1705
.Class 3 Public Primary Certification Authority0
Dhttp://crl.microsoft.com/pki/crl/products/MicrosoftCodeVerifRoot.crl0
VeriSign, Inc.1
VeriSign Trust Network1;09
2Terms of use at https://www.verisign.com/rpa (c)09100.
'VeriSign Class 3 Code Signing 2009-2 CA
VeriSign, Inc.1+0)
"VeriSign Time Stamping Services CA
100907170408Z0#
Hay strings que van a aparecer prácticamente siempre, como los de las cabeceras DOS (ese "!This program cannot be run in DOS mode"
es un mecanismo de compatibilidad del formato PE que lleva arrastrando Microsoft desde hace décadas) o algunos que son simplemente falsos positivos. Aquí lo interesante es ver si alguno de esos strings da algún tipo de información sobre lo que hace el binario.
Entre todos los strings que se observan, se pueden ver dos tipos de strings:
- Referencias a librerías y a funciones. En un binario, es normal encontrar referencias a funciones de librerías externas, ya que los programas suelen necesitar librerias externas (ya sean del sistema o no) para poder realizar ciertas cosas. Estas funciones pueden dar pistas sobre las capacidades que tiene el binario, es decir, que cosas puede hacer; por ejemplo, si contiene funciones para interactuar con ficheros, es que puede interactuar con ficheros. Aun así, esto no muestra todas las capacidades que puede tener un binario, ya que hay mecanismos para ocultar esto, los cuales veremos más adelante.
- Información sobre algún tipo de certificado (menciones a VeriSign, menciones a Microsoft, etc.). Esto puede indicar que el binario se encuentra firmado. Firmar un binario es una forma de eludir sistemas antivirus. Podemos verificar si esta firmado con diferentes herramientas.
Como se puede ver en este caso, por desgracia, los strings no parecen dar demasiada información sobre qué hace la muestra. No se han detectado strings propios del programa, como mensajes de salida, variables del programa, etc. En estos casos hay dos opciones: o la muestra no contiene strings interesantes o están ofuscadas. Sea cual sea, conviene siempre probar varios métodos para la extraction de string. Usar la SysInternal es el más sencillo de todos. Si usamos varias aplicaciones, como por ejemplo PEStudio, y comparamos resultados entre ellas, puede que saquemos mas resultados.
Pero, si están ofuscadas ¿cómo se pueden detectar? Hay herramientas pensadas para la detección de este tipo de strings. Si se ha usado algún tipo de mecanismo común, como usar un empaquetador de código (como UPX) o técnicas de ofuscación sencillas (como usar XOR sobre los datos en binario) este tipo de herramientas los pueden detectar. Se pueden utilizar herramientas como FLOSS para intentar buscar este tipo de datos.
Por no llenar el articulo de strings, no he puesto la salida de FLOSS, pero la herramienta no detecta más strings de las detectadas por strings
. Por el momento, se ve que con esta muestra no hemos tenido mucha suerte. El hecho de no encontrar strings tan fácilmente puede ser un indicador de uso de técnicas de ofuscación. Ofuscar tanto los datos como el código es normal en el mundo del malware (y no solo en el mundo del malware, este tipo de técnicas también se emplean para proteger propiedad intelectual, por ejemplo). De todas maneras, en las próximas partes nos meteremos en detalle más formas para detectar ofuscaciones, como el análisis del la secciones del binario y el análisis de entropía.
Metadatos⌗
Como esto esta quedando muy largo, en esta parte solo voy a tratar otro punto básico más: el análisis de metadatos. Las muestras de malware, al igual que cualquier archivo, pueden contener metadatos interesantes. Digo “pueden” porque puede darse el caso de que hayan sido eliminados, sobrescritos por otro sistema o incluso deliberadamente modificados para dificultar el análisis. Aun así, siempre conviene analizarlos para ver si se encuentra información.
Hay mil aplicaciones para analizar metadatos. La forma más sencilla (y que todos hemos usado alguna vez) es la típica de clicar en el archivo y ver sus propiedades. También hay aplicaciones que permiten hacer esto y muestran la información de manera más detallada y organizada. A mi personalmente me gusta usar PEStudio (que entre otras muchas cosas, también muestra los metadatos) y ExifTool, que es muy conocida ya no solo para analizar binarios como para analizar cualquier archivo (como su nombre indica, es muy común para sacar metadatos de fotografías).
En los metadatos no vemos nada muy relevante, peo sí que se muestra información sobre un certificado de Microsoft, ya que el binario parece estar firmado. Podemos comprobar esto de varias maneras como, por ejemplo, en las propiedades del archivo o mediante funciones como Get-AuthenticodeSignature
.
Si se hace a traves de las propiedades del archivo, en caso de que este tuviera un certificado válido, las propiedades mostrarían una nueva pestaña con información sobre el certificado. En este caso, no parece haber certificado.
Comprobando con PowerShell se obtiene le mismo resultado. Esto no tiene porque indicar que no tiene firma, también puede deberse a que la firma se encuentre caducada o que el certificado usado no sea valido. Aunque se haya firmado con un supuesto certificado de Microsoft, si estos certificados se ven comprometidos (cosa que ya ha pasado en alguna ocasión) se revocan, de tal manera que los sistemas no los den por válidos. En este caso, por la información de los metadados, parece tratarse de eso.
Conclusión⌗
Comenzar a analizar malware es una tarea relativamente sencilla (tranquilos, que se va a complicar). Aunque de momento no hemos obtenido mucha información, el hecho de no encontrar pistas en los strings o el encontrar un certificado sospechoso ya da pistas indicando que el archivo es malicioso (en este caso ya lo sabíamos, pero al analizar muestras desconocidas son pistas interesantes).
De todas maneras, el análisis estático que se puede realizar no acaba más que comenzar. En la siguiente parte tengo intención de indagar más en este tipo de técnicas, analizando técnicamente el binario y sus secciones y entrando más en detalle sobre como detectar técnicas de ofuscación, hablando de conceptos como de entropía.
Pero por el momento, stay safe & happy hacking!