sábado, 30 de marzo de 2013

Unas notas sobre Yara

Hace un tiempo, tuve la tarea de tratar de identificar determinado patrón dentro de un conjunto de archivos dado. Primero pensé en hacerlo con Python pero no quería hacer lo que seguramente están pensando: string.find(). Entonces, recordé que en un momento, leyendo Malware Analyst's Cookbook, había visto yara.

Qué es Yara?. Desde su web dicen esto:
YARA is a tool aimed at helping malware researchers to identify and classify malware samples. With YARA you can create descriptions of malware families based on textual or binary patterns contained on samples of those families. Each description consists of a set of strings and a Boolean expression which determines its logic.
Para ponerlo en criollo, Yara permite, mediante un conjunto de reglas, clasificar e identificar muestras de malware.

Esto lo vamos a ver mucho más claro con un ejemplo, pero primero, instalemos Yara.

Primero que nada, nos bajamos los sources y los bindings para Python:
nriva@ubuntu:~$ wget http://yara-project.googlecode.com/files/yara-1.7.tar.gz
nriva@ubuntu:~$ wget http://yara-project.googlecode.com/files/yara-python-1.7.tar.gz
Luego, descomprimimos los .tar.gz:
nriva@ubuntu:~$ tar xzf yara-1.7.tar.gz 
nriva@ubuntu:~$ tar xzf yara-python-1.7.tar.gz
Instalamos un par de paquetes que vamos a necesitar para compilar:
nriva@ubuntu:~$ sudo apt-get install python-dev libpcre3 libpcre3-dev
Ahora, nos vamos a la carpeta de los sources de Yara y ejecutamos los siguientes comandos para compilar:
nriva@ubuntu:~$ cd yara-1.7/
nriva@ubuntu:~/yara-1.7$ ./configure
nriva@ubuntu:~/yara-1.7$ make
nriva@ubuntu:~/yara-1.7$ make check
nriva@ubuntu:~/yara-1.7$ sudo make install
Listo, ya tenemos yara funcionando:
nriva@ubuntu:~/yara-1.7$ yara
usage:  yara [OPTION]... [RULEFILE]... FILE | PID
options:
  -t <tag>                  print rules tagged as <tag> and ignore the rest. Can be used more than once.
  -i <identifier>           print rules named <identifier> and ignore the rest. Can be used more than once.
  -n                        print only not satisfied rules (negate).
  -g                        print tags.
  -m                        print metadata.
  -s                        print matching strings.
  -l <number>               abort scanning after a <number> of rules matched.
  -d <identifier>=<value>   define external variable.
  -r                        recursively search directories.
  -f                        fast matching mode.
  -v                        show version information.

Report bugs to: <vmalvarez@virustotal.com>
Ahora, compilemos los bindings para Python:
nriva@ubuntu:~$ cd yara-python-1.7/
nriva@ubuntu:~/yara-python-1.7$ python setup.py build
nriva@ubuntu:~/yara-python-1.7$ sudo python setup.py install
Probemos a ver si funciona:
nriva@ubuntu:~/yara-python-1.7$ python
Python 2.7.2+ (default, Jul 20 2012, 22:15:08)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import yara
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: libyara.so.0: cannot open shared object file: No such file or directory
>>>
Tenemos un error. Buscando en Google, damos con la solución:
nriva@ubuntu:~/yara-python-1.7$ sudo su
root@ubuntu:/home/nriva/yara-python-1.7# echo "/usr/local/lib" >> /etc/ld.so.conf
root@ubuntu:/home/nriva/yara-python-1.7# ldconfig
root@ubuntu:/home/nriva/yara-python-1.7# exit
exit
nriva@ubuntu:~/yara-python-1.7$ python
Python 2.7.2+ (default, Jul 20 2012, 22:15:08)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import yara
>>> dir(yara)
['CALLBACK_ABORT', 'CALLBACK_CONTINUE', 'Error', 'SyntaxError', '__doc__', '__file__', '__name__', '__package__', 'compile']
>>> 
Ya tenemos todo funcionando. Por un lado, la versión standalone que ejecutaremos desde una terminal y por el otro los módulos para poder utilizar Yara desde Python.

Es hora de que comencemos a escribir nuestra primer regla, pero les aconsejo que miren la documentación de Yara para tener mucha más información al respecto. 

Las reglas en Yara van simplemente en un archivo .txt aunque por conveniencia se le coloca la extensión .yara.

La estructura de cada regla es la siguiente:

rule NombreDeLaRegla : keyword1 keyword2
{
     meta:
        description = "Metadata. Un simple párrafo que explica que hace la regla."
     
     strings:
        $pattern1 = "MiStringAMatchear1"
        $pattern2 = "MiStringAMatchear2"
        $regex1 = "MiRegex"

     condition:
        if $pattern1 and $pattern2 or regex1
}
Vayamos por partes.

Para comenzar, tenemos la palabra reservada "rule" para indicar que es una regla. Seguido va el nombre que queremos darle a la regla y separado por dos puntos ":" van los keywords o tags para dicha regla.

Entre llaves "{}" va a ir todo el "código" de la regla. Primero, tenemos una sección de metadata indicada con la palabra "meta". En "description" es donde colocaremos un pequeño párrafo describiendo para que sirve la regla, como funciona, etc.

Luego, tenemos dos partes importantes que son,  básicamente, el esqueleto principal de cada regla.

Primeramente, "strings" sirve para declarar los patrones a buscar y matchear. Estos patrones pueden ser bytes, strings ASCII, strings wide, tanto case sensitive como case insensitive y hasta expresiones regulares.

A continuación, tenemos "condition". En esta sección vamos a colocar todas las condiciones que deben de cumplirse para matchear. Estas son expresiones lógicas como las que pusimos en el ejemplo.

Para seguir aclarando las cosas un poco más, escribamos una regla que nos diga si un archivo esta empacado con UPX:
rule upx : checkupx
{
    meta:
        description = "Esta regla comprueba si un archivo dado esta o no empacado con UPX."
       
    strings:
        /*
        -----> UPX 3.09
        807C24 08 01  CMP BYTE PTR SS:[ESP+8],1
        0F85 E2010000 JNZ iisproxy.10091E5D
        60            PUSHAD
        BE 00800510   MOV ESI,iisproxy.10058000
        8DBE 0090FAFF LEA EDI,DWORD PTR DS:[ESI+FFFA9000]
        57            PUSH EDI
        EB 10         JMP SHORT
        */
        $entrypoint_signature = { 80 7C 24 08 01 0F 85 ?? ?? ?? ?? 60 BE ?? ?? ?? ?? 8D BE ?? ?? ?? ?? 57 EB 10 }
       
    condition:
        $entrypoint_signature at entrypoint
}
Si se fijan, en la condición estoy utilizando una palabra reservada más "entrypoint". De esta manera, le digo que solo busque el signature en el EntryPoint de un archivo PE. En caso de que el archivo no sea PE, esta regla nunca resultará en True.

Voy a probar esta regla contra un archivo empacado con UPX 3.09. Vamos a hacerlo desde una terminal:
nriva@fastix:~/Desktop$ yara -s -t checkupx checkupx.yara iisproxy.dll
upx iisproxy.dll
0x3a070:$entrypoint_signature: 80 7C 24 08 01 0F 85 E2 01 00 00 60 BE 00 80 05 10 8D BE 00 90 FA FF 57 EB 10
Como vemos, la detección fue exitosa. Al invocar a Yara, le he pasado un par de parámetros:
  • -s: con este parámetro le indico que me muestre los patrones que han matcheado.
  • -t <tag>: con este parámetro le indico que quiero que solo me matchee la regla marcada con el keyword <tag>.
Esto lo prodríamos haber hecho desde Python también pero eso queda como tarea para el lector :)

Sin dudas, es una gran herramienta que nos puede ser de mucha utilidad.

Hasta acá con Yara. Espero que les haya sido útil. Hasta la próxima.

Referencias:

No hay comentarios: