Solución al Gipuzkoa Encounter 2010 Hack-It! (ELF II)

En esta entrada voy a mostrar otra forma de solucionar el reto del ELF de la Gipuzkoa Encounter. Si no habéis leído la entrada previa sobre el reto, es momento de hacerlo ahora, ya que consideraré muchas cosas como sabidas. De nuevo copio el warning:

AVISO: Voy a poner la solución al reto paso a paso, tratando de que sea un solucionario completo para que tanto la gente con pocos conocimientos del tema como los más expertos puedan seguirlo. No obstante, lo ideal es que trates de resolverlo por tu cuenta, y que si te atascas continues leyendo. Dicho queda.

Binary patching

Ya dije en la otra entrada que una solución podría pasar por parchear el binario para saltarnos la protección de depuración y así utilizar gdb para seguir el programa paso a paso y localizar la contraseña que nos piden. Si el objetivo fuera la ejecución de alguna función o conseguir que el programa mostrara algún mensaje concreto podríamos tratar de parchearlo para que el flujo del programa nos llevara a ese punto, sin embargo aquí lo que nos han pedido es que les demos la contraseña válida, así que allá vamos.

(gdb) disass main
Dump of assembler code for function main:
0x080482bb <main+0>:    lea ecx,[esp+0x4] 
0x080482bf <main+4>:    and    esp,0xfffffff0 
0x080482c2 <main+7>:    push   DWORD PTR [ecx-0x4] 
0x080482c5 <main+10>:    push   ebp 
0x080482c6 <main+11>:    mov    ebp,esp 
0x080482c8 <main+13>:    push   ecx 
0x080482c9 <main+14>:    sub    esp,0x14 
0x080482cc <main+17>:    mov    DWORD PTR [esp],0x80a4f08 
0x080482d3 <main+24>:    call   0x8048f70 <puts> 
0x080482d8 <main+29>:    mov    DWORD PTR [esp],0x80a4f2f 
0x080482df <main+36>:    call   0x8048d80 <printf> 
0x080482e4 <main+41>:    call   0x8048273 <check> 
0x080482e9 <main+46>:    mov    DWORD PTR [esp],0x80c3840 
0x080482f0 <main+53>:    call   0x8048db0 <gets> 
0x080482f5 <main+58>:    mov    DWORD PTR [esp],0xa 
0x080482fc <main+65>:    call   0x8049110 <putchar> 
0x08048301 <main+70>:    mov    DWORD PTR [esp],0x80c3840 
0x08048308 <main+77>:    call   0x8048210 <balioztau> 
0x0804830d <main+82>:    test   eax,eax 
0x0804830f <main+84>:    je     0x804831f <main+100> 
0x08048311 <main+86>:    mov    DWORD PTR [esp],0x80a4f3c 
0x08048318 <main+93>:    call   0x8048f70 <puts> 
0x0804831d <main+98>:    jmp    0x804832b <main+112> 
0x0804831f <main+100>:    mov    DWORD PTR [esp],0x80a4f46 
0x08048326 <main+107>:    call   0x8048f70 <puts> 
0x0804832b <main+112>:    add    esp,0x14 
0x0804832e <main+115>:    pop    ecx 
0x0804832f <main+116>:    pop    ebp 
0x08048330 <main+117>:    lea    esp,[ecx-0x4] 
0x08048333 <main+120>:    ret 
End of assembler dump. 

Ahí tenemos el desensamblado del main. Recordad que la función check es la que comprobaba si estábamos depurando el binario y en ese caso nos mostraba un mensaje de error y finalizaba la ejecución. Una forma sencilla de evitar que pase es simplemente eliminando la llamada a check del binario. Para ello tendremos que encontrar dentro del binario ese fragmento de código y sustituirlo por otro. Podemos utilizar objdump para localizar ese fragmento de código y el hexadecimal al que ensambla:

adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ objdump -M intel -d tolosa2010_unix -z | grep -i "<main>:" -A 30
080482bb <main>:
 80482bb:    8d 4c 24 04              lea    ecx,[esp+0x4]
 80482bf:    83 e4 f0                 and    esp,0xfffffff0
 80482c2:    ff 71 fc                 push   DWORD PTR [ecx-0x4]
 80482c5:    55                       push   ebp
 80482c6:    89 e5                    mov    ebp,esp
 80482c8:    51                       push   ecx
 80482c9:    83 ec 14                 sub    esp,0x14
 80482cc:    c7 04 24 08 4f 0a 08     mov    DWORD PTR [esp],0x80a4f08
 80482d3:    e8 98 0c 00 00           call   8048f70 <_IO_puts>
 80482d8:    c7 04 24 2f 4f 0a 08     mov    DWORD PTR [esp],0x80a4f2f
 80482df:    e8 9c 0a 00 00           call   8048d80 <_IO_printf>
 80482e4:    e8 8a ff ff ff           call   8048273 <check>
 80482e9:    c7 04 24 40 38 0c 08     mov    DWORD PTR [esp],0x80c3840
 80482f0:    e8 bb 0a 00 00           call   8048db0 <_IO_gets>
 80482f5:    c7 04 24 0a 00 00 00     mov    DWORD PTR [esp],0xa
 80482fc:    e8 0f 0e 00 00           call   8049110 <putchar>
 8048301:    c7 04 24 40 38 0c 08     mov    DWORD PTR [esp],0x80c3840
 8048308:    e8 03 ff ff ff           call   8048210 <balioztau>
 804830d:    85 c0                    test   eax,eax
 804830f:    74 0e                    je     804831f <main+0x64>
 8048311:    c7 04 24 3c 4f 0a 08     mov    DWORD PTR [esp],0x80a4f3c
 8048318:    e8 53 0c 00 00           call   8048f70 <_IO_puts>
 804831d:    eb 0c                    jmp    804832b <main+0x70>
 804831f:    c7 04 24 46 4f 0a 08     mov    DWORD PTR [esp],0x80a4f46
 8048326:    e8 45 0c 00 00           call   8048f70 <_IO_puts>
 804832b:    83 c4 14                 add    esp,0x14
 804832e:    59                       pop    ecx
 804832f:    5d                       pop    ebp
 8048330:    8d 61 fc                 lea    esp,[ecx-0x4]
 8048333:    c3                       ret
adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ objdump -M intel -d tolosa2010_unix -z | grep -i 80482e4
 80482e4:    e8 8a ff ff ff           call   8048273 <check>

Como se puede observar nuestro objetivo es localizar la secuencia e88affffff y sustituirla. No podemos simplemente eliminarla del binario, ya que romperíamos las instrucciones que utilizan offsets y podríamos causar problemas de alineación o derivados. Sin embargo, podemos colocar en su lugar NOPs (0x90), un total de cinco, para cubrir el espacio de esa instrucción. Para esta operación podemos utilizar nuestro editor hexadecimal favorito, ya sea hexedit, ghex u otro.

GHex

Buscamos el patrón del call

GHex sustitution

Sustituimos el patron del call por nops

Una vez sustituido el patrón de la llamada a check por NOPs guardamos el nuevo binario y deberíamos ser capaces de ejecutarlo y depurarlo con gdb sin las molestas interrupciones de la rutina antidebuging. En este punto tocaría investigar un poco la rutina balioztau (comentada en la entrada previa). Basta con localizar el lugar donde compara la contraseña con nuestro valor, aunque no se comprenda el funcionamiento de la rutina esto es evidente:

0x08048250 <balioztau+64>: cmp dl,al

Con un poco de la magia de gdb podemos poner un breakpoint en ese momento y que se nos muestre el valor de los registros cada vez que se detenga ahí. Veremos qué pasa:

(gdb) display/c $eax
(gdb) display/c $edx
(gdb) br *0x08048250
Punto de interrupción 1 at 0x8048250
(gdb) run
Starting program: /home/adrian/Escritorio/gipuzkoa encounter/tu

 [Gipuzkoa Encounter 2010 - Hack It!]

 Passw0rd: passworderroneo

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 116 't'
1: /c $eax = 112 'p'
(gdb) c
Continuando.
 #EPIC FAIL

Program exited with code 014.

El programa se ha detenido en la comparación, y ha probado si la “p” que hemos introducido es igual a “t”. Esa “t” es, por lo tanto, la primera letra de la contraseña que buscamos. Si le damos a continuar el programa finaliza, ya que son diferentes y no sigue comprobando el resto de la cadena. Podemos repetir el proceso, introduciendo un password que comience con t, y descubriremos la segunda letra de la contraseña. Si repetimos esto el número suficiente de veces tendremos la contraseña correcta. Sin embargo eso es un poco tedioso. Podemos volver a parchear el binario para que nos sea más fácil encontrar lo que buscamos. La instrucción culpable de que el programa finalice cuando encuentra la primera letra incorrecta es este salto condicional situado justo tras el cmp anterior:

adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ objdump -M intel -z -d tolosa2010_unix | grep -i 8048252
 8048252:    74 09         je     804825d <balioztau+0x4d>

¿Cómo cambiar un je (salto si igual) por un jmp (salto incondicional) y mantener el offset correcto? Conociendo un poco las instrucciones de salto, el primer byte (0x74 en este caso) indica qué instrucción es (qué tipo de salto) y el segundo byte y siguientes (0x09 en este caso) indica el offset del salto. Por lo tanto podemos buscar con ghex nuestra secuencia de salto (7409) y sustituirla por un salto incondicional con offset 0x09. En el siguiente código de prueba podemos ver que el salto incondicional ensambla al siguiente hexadecimal:

adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ cat jmp.S
BITS 32

jmp short aqui
nop
nop
nop
nop
nop
nop
nop
nop
nop
aqui:
nop
nop
adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ ndisasm jmp -u
00000000  EB09              jmp short 0xb
00000002  90                nop
00000003  90                nop
00000004  90                nop
00000005  90                nop
00000006  90                nop
00000007  90                nop
00000008  90                nop
00000009  90                nop
0000000A  90                nop
0000000B  90                nop
0000000C  90                nop

Así que volvemos a nuestro editor hexadecimal, buscamos la secuencia 7409 y nos damos cuenta de que tenemos varias apariciones. Por lo tanto lo mejor es que busquemos nuestro salto y la instrucción anterior o siguiente:

adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ objdump -M intel -z -d tolosa2010_unix | grep -i 8048252 -B 1
8048250:    38 c2                    cmp    dl,al
8048252:    74 09                    je     804825d <balioztau+0x4d>

Ahora sí, si buscamos 38C27409 encontramos una única aparición que se corresponde a nuestro salto, y puesto que el salto incondicional ensambla a EB (con offset 09), sustituiremos únicamente el 74 (je) por nuestro EB (jmp) y guardaremos. Ahora, tanto si la contraseña va coincidiendo como si no, seguirá comprobando toda la cadena (y podremos ver la contraseña completa):

adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ gdb -q tu2
Leyendo símbolos desde /home/adrian/Escritorio/gipuzkoa encounter/tolosa2010_unix...hecho.
(gdb) br *0x8048250
Punto de interrupción 1 at 0x8048250
(gdb) display/c $eax
(gdb) display/c $edx
(gdb) run
Starting program: /home/adrian/Escritorio/gipuzkoa encounter/tolosa2010_unix

 [Gipuzkoa Encounter 2010 - Hack It!]

 Passw0rd: estapasswordnoes

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 116 't'
1: /c $eax = 101 'e'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 48 '0'
1: /c $eax = 115 's'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 108 'l'
1: /c $eax = 116 't'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 48 '0'
1: /c $eax = 97 'a'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 115 's'
1: /c $eax = 112 'p'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 64 '@'
1: /c $eax = 97 'a'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 104 'h'
1: /c $eax = 115 's'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 52 '4'
1: /c $eax = 115 's'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 99 'c'
1: /c $eax = 119 'w'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 107 'k'
1: /c $eax = 111 'o'
(gdb) c
Continuando.

Breakpoint 1, 0x08048250 in balioztau ()
2: /c $edx = 0 '\000'
1: /c $eax = 114 'r'

¡Ya la tenemos! Cogemos las letras de edx, las ponemos en orden y obtenemos la password: “t0l0s@h4ck“. Comprobamos que es correcta, aunque ya lo sepamos :P.


adrian@Andromeda:~/Escritorio/gipuzkoa encounter$ ./tolosa2010_unix

[Gipuzkoa Encounter 2010 - Hack It!]

Passw0rd: t0l0s@h4ck

Success!

¡No os olvidéis de comprobarlo contra el binario original! Si lo probáis contra el segundo binario dará igual qué password introduzcais ya que siempre dirá “Success!”. Espero que esta solución haya ilustrado otra manera de afrontar el problema y que por el camino se haya aprendido algo acerca del ensamblado de los saltos y de binary patching. Seguro que hay más soluciones posibles, así que si alguien tiene alguna idea más, o alguna duda, puede dejar un comentario 🙂

Anuncios
Tagged with: ,
Publicado en Reverse, wargame

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Archive
A %d blogueros les gusta esto: