HackIt! Nivel 8: reverse engineering en Windows (solución)

Si hacemos lo que vimos en el post anterior, veremos la siguiente cadena: «Compressed by Petite (c)1999 Ian Luck»

Así que ahora que sabemos el compresor usado en el .exe, habrá que descomprimirlo. La primera pega, sacada del readme de Petite: «No existe un descompresor de Petite.» Genial… vamos a preguntar al sabio a ver si opina lo contrario. Entre los resultados, uno curioso: «r!sc’s Petite enlarger» (alargador de petite) 🙂 Tras ejecutar enlarger.exe sobre login.exe vemos que tenemos éxito 🙂

Se genera en la misma carpeta de login.exe un nuevo binario descomprimido de nombre un-packed.exe . Ése será el ejecutable que abriremos con OllyDBG, y ahora sí, estaremos en disposición de depurarlo:

Pondremos un punto de ruptura en la dirección 0x0401290 (tocamos la línea y pulsamos F2). Pulsamos el botón Play (Run Program = F9) . En la línea 0x04012D4 vemos que se intenta leer el fichero license.lic (en concreto los campos user y login). Antes de seguir depurando, por tanto, vamos a cumplimentar esos campos (el user lo dejamos tal cual está en el fichero original, «euskal15», y en el password pondremos «contraseña», por ejemplo).

Seguimos depurando paso a paso pulsando F7 (ejecutar paso a paso, metiéndose en el código de las funciones – destino de los call – ) ó F8 (ejecutar paso a paso, pero NO meterse en el código de las funciones – no meterse en el destino de los call – ) .

Cuando lleguemos a la instrucción 0x04013AA debemos fijarnos en la zona superior derecha, en el valor de los registros EAX, ECX, etc. Veremos la cadena HACKthis (podemos empezar de cero, probando a poner esa cadena en el password y ejecutar el .exe. Veremos que no es la contraseña que necesitamos) Seguimos ejecutando paso a paso, hasta que al llegar en una de las vueltas a la instrucción 0x04013CD veremos lo siguiente:


«HFODdget» … qué cadena más curiosa, ¿no? 😉 No voy a explicar exactamente qué hace cada región de código ensamblador, eso lo dejo «como ejercicio para el lector» 😉

Hack It! Nivel 8: reverse engineering en Windows

Descargamos el archivo .zip del nivel 8 y descomprimimos. Veremos dos ficheros : login.exe y login.lic

Ejecutamos login.exe y nos saludará la siguiente ventana:

O sea, «¡No! pero gracias por concursar…»

Empecemos con la artillería: abrimos el ejecutable en OllyDBG, y obtenemos otro mensaje de alerta no muy bonito:

«Entry Point Alert. Module login has entrypoint outside the code (as specified in the PE header). Maybe this file is self-extracting or self-modifying. Please keep it in mind when setting breakpoints!»

Probablemente el .exe esté comprimido. Lo más habitual es con UPX (de hecho este compresor de ejecutables ya ha aparecido en otras pruebas del HackIt! …) Sin embargo, tras probar con UPX veremos que no es el caso. ¿Qué hacer? Lo que deberíamos de haber hecho desde un principio, echarle un vistazo al binario con un editor de textos (como UltraEdit), a ver si vemos algún string interesante.

Y aquí os dejo que penséis un poco cómo seguir 😉

Hack It! Nivel 7: solución

Como ya comentamos, no tenía demasiado misterio. Descargar la captura de tramas de una conexión wifi con cifrado WEP contenida en el fichero tampered.cap y pasarle un crackeador por ataque de diccionario (en español). El diccionario utilizado es lemario-espanol-2002-10-25 encontrado en http://lemarios.olea.org/

La orden de crackeo también es sencilla:

$ aircrack-ng -w lemario-espanol-2002-10-25.txt tampered.cap

En unos pocos minutos aparecerá la clave para pasar al siguiente nivel: proclive .

El nivel 8 sí que nos costó un poco… tal vez porque había que practicar ingeniería inversa sobre un binario .exe , y estamos un poco oxidados en OllyDBG 😉

HackIt! 2007 : nivel 6: solución

Reverse Engineering. Cracking. Un poco de conocimientoso sobre ambos temas serán imprescindibles para
pasar este nivel (os he dejado muchos días para que fuerais sacándolo por vuestra cuenta, ¿qué tal os ha ido?) Vamos a por ello. Tras descargarnos el fichero .zip que indica la página, lo descomprimimos e intentamos ejecutar (txipi nos dijo que no había virus ni código maligno en los ejecutables, por eso nos atrevemos a ejecutar un binario a pelo…). La salida es bastante pobre, por decir algo:


sh-3.2$ ./program
sh-3.2$

Así que toca depurar con GDB.


$ gdb ./program
warning: no loadable sections found in added symbol-file /tmp/program
(no debugging symbols found)

Vaya, si no hay tabla de símbolos, la depuración puede ser un infierno. Así que comprobemos primero que el ejecutable
no haya sido comprimido con UPX o similares (UPX es el compresor más conocido de ficheros ELF en Linux):


$ upx -l ./program
Ultimate Packer for eXecutables
Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007
UPX 3.00 Markus Oberhumer, Laszlo Molnar & John Reiser Apr 27th 2007
.
File size Ratio Format Name
-------------------- ------ ----------- -----------
532432 -> 52404 9.84% linux/elf386 ./program

¡Tachán! Efectivamente el ejecutable está comprimido con UPX. Descompresión al canto:


$ sh-3.2$ upx -d ./program
Ultimate Packer for eXecutables
Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007
UPX 3.00 Markus Oberhumer, Laszlo Molnar & John Reiser Apr 27th 2007
.
File size Ratio Format Name
-------------------- ------ ----------- -----------
532432 <- 52404 9.84% linux/elf386 program
Unpacked 1 file.

Y ahora volvemos a pasarlo por GDB, a ver qué nos dice:


sh-3.2$ gdb ./program
GNU gdb 6.6-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb)

Mejor 🙂 Bien, vamos a poner un punto de ruptura en main():


(gdb) break main
Breakpoint 1 at 0x8048455
(gdb)

Ejecutemos hasta el punto de ruptura y pidamos un desensamblado de la zona:

(gdb) run
Starting program: /tmp/program
Breakpoint 1, 0x08048455 in main ()
(gdb) disassemble
Dump of assembler code for function main:
0x08048444 : lea 0x4(%esp),%ecx
0x08048448 : and $0xfffffff0,%esp
0x0804844b : pushl 0xfffffffc(%ecx)
0x0804844e : push %ebp
0x0804844f : mov %esp,%ebp
0x08048451 : push %edi
0x08048452 : push %esi
0x08048453 : push %ebx
0x08048454 : push %ecx
0x08048455 : sub $0x78,%esp
0x08048458 : movl $0x0,0xffffffec(%ebp)
0x0804845f : lea 0xffffffd0(%ebp),%eax
0x08048462 : mov %eax,(%esp)
0x08048465 : call 0x804837c
0x0804846a : movl $0x0,0xffffffe8(%ebp)
0x08048471 : jmp 0x8048477
0x08048473 : addl $0x1,0xffffffe8(%ebp)
0x08048477 : cmpl $0x98967e,0xffffffe8(%ebp)
0x0804847e : jle 0x8048473
0x08048480 : lea 0xffffffc0(%ebp),%eax
0x08048483 : mov %eax,(%esp)
0x08048486 : call 0x804837c

Bien, se ve la zona de paso de variables a la pila (guardar el contexto) y primera llamada call (por el nombre de la función, times, parece que hace algún tipo de comprobación) y en función del resultado, en main+45 saltamos a main+51. Ahí nueva comparación… saltamos (hacia atrás) a main+47. Parece un bucle, ciclando hasta que se cumpla cierta condición. Posteriormente, se hace una nueva llamada a «times», haciendo alguna nueva comprobación:


0x08048486 : call 0x804837c
0x0804848b : mov 0xffffffc0(%ebp),%edx
0x0804848e : mov 0xffffffd0(%ebp),%eax
0x08048491 : mov %edx,%ecx
0x08048493 : sub %eax,%ecx
0x08048495 : mov %ecx,%eax
0x08048497 : mov %eax,0xffffffe8(%ebp)
0x0804849a : cmpl $0xa,0xffffffe8(%ebp)
0x0804849e : jle 0x8048586
0x080484a4 : movl $0x80c89a8,(%esp)
0x080484ab : call

En main+90 hay alguna condición que tras comprobarla hace que el programa termine (salta a main+322). Vamos a cortocircuitar este último salto . a ver qué pasa 😉 :

(gdb) j *0x080484a4
Continuing at 0x80484a4.
username? jota
error: wrong serial number!
Program exited normally.
(gdb)
Vaya, algo hemos avanzado, porque al menos nos pide username… Pero poniéndole cualquier cosa (jota, p.ej.), vemos que no le gusta.
Mmmmmhhh… volvamos a la carga:


(gdb) run
Starting program: /tmp/program
.
Breakpoint 1, 0x08048455 in main()
(gdb) disassemble
...
0x080484ab : call 0x804835c
0x080484b0 : lea 0xffffffac(%ebp),%eax
0x080484b3 : mov %eax,(%esp)
0x080484b6 : call 0x804833c
0x080484bb : mov $0x303c48b1,%eax
0x080484c0 : sub 0xffffffec(%ebp),%eax
0x080484c3 : mov %eax,0xffffffec(%ebp)
0x080484c6 : addl $0x2faf080,0xffffffec(%ebp)
0x080484cd : cmpl $0x0,0xffffffec(%ebp)
0x080484d1 : jne 0x804857a
0x080484d7 : movb $0x9b,0xffffffe0(%ebp)
...

Entre el printf («username») y el gets (recogida del parámetro por teclado) no hay nada que destacar.
Lo siguiente termina en un jne a main+310. Volvamos a cortocircuitar (no queremos saltar en main+141… queremos ir
a la siguiente instrucción) :

(gdb) j *0x080484d7
Continuing at 0x80484d7.
correct! password = d3nb0r4
Program exited normally.
(gdb)

¡Qué bonito password! 🙂