Nuevas Entradas

Walkthrough Stack1 CLS Exploits

by hdbreaker

Con la debida autorización de Ricardo Narvaja y los agradecimientos respectivos al grupo CrackLatinos se da comienzo al solucionario del ejercicio Stack1 del grupo CLS Exploits.

Enunciado:

Autor: Ricardo Narvaja
----------------------

Abrir el idb en ida y tratar de entender y mirar estaticamente el programa. Mirar la distancia exacta que se necesita para pisar la cookie y ponerla en el mail que me envian armar el script correcto y enviarmelo para que yo lo pruebe. Entre las cosas que se enviaron para bajar hay un esqueleto de script en python que puede servir cualquier duda ir al canal de telegram de exploit que alli estan en grupo trabajando en esto.

IDB: stack1.idb
Binario: stack1.exe

Lo primero que debemos hacer en este ejercicio es abrir el idb con IDA PRO, y analizar estáticamente el programa en busca de la vulnerabilidad, para luego generar un exploit que se aproveche de la misma y logre controlar el flujo de la aplicación.

La estructura principal del programa es realmente simple y luce de la siguiente forma:



En la dirección 0x004012D4 comienza la sección que nos interesa:


En esta dirección podemos apreciar cómo el offset Format que contiene el string buf: %08x cookie: %08x\n se mueve dentro del puntero [esp+0D8], esto es una forma de pushear un argumento al stack, ya que el mismo sera utilizado por la función printf en la dirección 0x004012DB la cual se encarga de mostrar un mensaje con el formato especificado en pantalla.

En la dirección 0x004012E0 podemos ver como la instrucción lea eax, [ebp+Buffer] coloca el valor al que referencia el puntero [ebp+Buffer] en eax, el cual luego es movido al puntero [esp+0B8] en la dirección 0x004012E6 el cual identificamos como un push al stack anteriormente.

Este valor es enviado como argumento de la función gets, la cual es vulnerable a overflow ya que no valida el largo del buffer donde escribe en relación con la entrada dada por el usuario.

Luego del llamado a la función gets en la dirección 0x004012EE se encuentra la siguiente comparación: 

cmp [ebp+var_C], 51525354

La misma modificara la conducta del salto condicional que se encuentra en la dirección 0x004012F5: jnz short locret_401303 (jz es equivalente a je)

Si la condición no se cumple (jmp if not equal) el salto condicional nos llevara al basic block que comienza en la dirección 0x00401303 el cual termina en un retn y concluye la aplicación.

Si la condición se cumple la aplicación sigue un flujo normal y llega al basic block que comienza en la dirección 0x004012F7 la cual coloca en el stack el mensaje "you are a winner man je\n" el cual es utilizado por la función printf para imprimir el mensaje solución del reto.

Teniendo esto en cuenta nuestro objetivo seria aprovecharnos de la función gets para enviar un valor mayor al esperado en Buffer con el fin de corromper el stack y lograr controlar el valor al que apunta el puntero [ebp+var_C]

Para lograr esto, primero debemos identificar el tamaño reservado para Buffer, IDA PRO nos permite hacer esto de forma sencilla de la siguiente forma:

1) Nos colocaremos sobre la variable Buffer y daremos doble click, esto nos situara en la vista Stack Frame View



2) Realizamos click derecho sobre Buffer y ingresamos en el submenu Array.


3) Aparecera la siguiente venta:



En esta ventana entran en juego 2 elementos Array element size y Array size, el tamaño del Buffer es equivalente a la multiplicación de estos 2 elementos, debido a que Array element size en este caso es 1 el tamaño de Buffer es igual a 140 bytes. Es importante destacar que debemos cerrar esta ventana clickeando el botón Cancelar.

Teniendo en cuenta lo anterior sabes que para desbordar Buffer debemos enviar un string mayor a 140 bytes, pero esto no es suficiente, debemos desbordar el buffer hasta el punto que logremos sobrescribir  el valor de contenido en var_C.

Para calcular cuantos bytes más debemos enviar debemos bajar en el Stack Frame View hasta localizarnos en la próxima variable adyacente a Buffer.





Con esta información podemos apreciar que este ejercicio tiene 2 posibles soluciones:

1) Sobrescribir 140 bytes + 4 bytes (var_C = 0x51525354)
2) Sobrescribir 140 bytes + 4 bytes (var_C) + 8 bytes + 4 bytes (s => ebp) + 4 bytes (r => eip)

Dicho esto procederemos a solucionar el ejercicio de ambas formas:

1) Sobrescribir 140 bytes + 4 bytes (var_C = 0x51525354)

import struct
from subprocess import Popen, PIPE
 
stdin = Popen("stack1.exe", stdin=PIPE, stdout=PIPE) 
buffer = "\x90"*140 # => Buffer length
var_c = struct.pack("<I", 0x51525354) 
payload = buffer + var_c # => jmp equal true
print "[#] Sending Payload"
stdout = stdin.communicate(payload)[0]
print stdout

Resultado:



2) Sobrescribir 140 bytes + 4 bytes (var_C) + 8 bytes + 4 bytes (s => ebp) + 4 bytes (r => eip)

Esta solución es más complicada y se puede lograr debido a que el binario no posee ASLR.

Al lograr controlar el valor del registro de retorno (EIP) somos capaces de alterar el flujo de ejecución del programa a nuestro gusto, por lo que podríamos decirle al programa que llegado el ultimo ret no finalice su ejecución, sino que siga su flujo llamando a la dirección 0x004012F7, donde comienza el basic block encargado de imprimir el mensaje "you are a winner man je\n".

Quedando nuestro exploit con la siguiente estructura:

import struct
from subprocess import Popen, PIPE
 
stdin = Popen("stack1.exe", stdin=PIPE, stdout=PIPE) 

buffer = "\x90" * 140 # Buffer length
var_c = "\x41" * 4
junk = "\x41" * 8 
s = "\x42" * 4 # JUNK
r = struct.pack("<I", 0x004012F7) # => Control over EIP

payload = buffer + var_c + junk + s + r # => jmp if not equal

print "[#] Sending Payload"
stdout = stdin.communicate(payload)[0]
print stdout

El anterior programa debería funcionar sin problemas, pero al ejecutarlo obtenemos el siguiente error:


Para comprender que sucede nos ayudaremos con el Debugger de IDA PRO, colocando con la tecla F2 un par de breakpoints, en el ultimo ret y en el jnz:


Luego de esto modificaremos nuestro exploit agregando un raw_input() antes del stdin.communicate() quedando de la siguiente forma:

import struct
from subprocess import Popen, PIPE
 
stdin = Popen("stack1.exe", stdin=PIPE, stdout=PIPE) 

buffer = "\x90" * 140 # Buffer length
var_c = "\x41" * 4
junk = "\x41" * 8 
s = "\x42" * 4 # JUNK
r = struct.pack("<I", 0x004012F7) # => Control over EIP

payload = buffer + var_c + junk + s + r # => jmp if not equal

print "[#] Sending Payload"
raw_input("Send any key to continue")
stdout = stdin.communicate(payload)[0]
print stdout

Esto lo haremos con el fin de poder attacharnos al programa antes de enviar el payload que produce el crash, para esto ejecutamos el programa:


Vamos a IDA, configuramos el Debugger a nuestro gusto, e iremos a la pestaña Debugger -> Attach to process..


Esto nos abrira una ventana en la cual debemos seleccionar el programa al cual attacharnos:


 Seleccionamos el programa y presionamos el botón "OK", esto nos llevara hacia nuestro Debugger:



Llegado este punto debemos presionar "F9" y luego dirigirnos a nuestra terminal y continuar la ejecución:



Hecho esto podremos seguir el flujo de ejecución presionando la tecla "F8", continuaremos hasta queda sobre el ultimo ret y sobre el presionamos "F9".


El error nos informa que el registro EBP se encuentra apurando a una sección de memoria que no puede ser leída, por lo que deberíamos fixear EBP con alguna dirección de memoria valida y nuestro problema debería estar resuelto, por lo que le colocaremos cualquier dirección de idata

Precionamos Ctrl+S para entrar al listado de segmentos:


Seleccionamos .idata y presionamos "OK".


Seleccionamos cualquier dirección de memoria valida, en este caso 0x004050A8 y modificamos el exploit de la siguiente forma:

import struct
from subprocess import Popen, PIPE
 
stdin = Popen("stack1.exe", stdin=PIPE, stdout=PIPE) 

buffer = "\x90" * 140 # Buffer length
var_c = "\x41" * 4
junk = "\x41" * 8 
s = struct.pack("<I", 0x004050A8)
r = struct.pack("<I", 0x004012F7) # => Control over EIP

payload = buffer + var_c + junk + s + r # => jmp if not equal

print "[#] Sending Payload"
raw_input("Send any key to continue")
stdout = stdin.communicate(payload)[0]
print stdout

Largamos el exploit, nos volvemos a attachar con IDA y nos posicionamos sobre el ultimo ret



En la imagen podemos apreciar como EBP apunta a nuestra dirección de idata y como el próximo registro a cargarse en EIP es la dirección 0x004012F7 la cual apunta al basic block donde se imprime nuestro buscado mensaje.

Resultado:



Alternativas:

Se  logran ejecutar dos acciones diferentes utilizando como registro alternativo EBP este registro nos permite mover el stack a nuestro gusto, por lo que luego de modificar el ultimo ret podríamos alterar el flujo de ejecución nuevamente alineando de forma correcta EBP y de esta forma controlar por segunda ves EIP sin necesidad de ROP.

PoC:

import struct
from subprocess import Popen, PIPE
 
stdin = Popen("stack1.exe", stdin=PIPE, stdout=PIPE) 

exit = struct.pack("<I", 0x75504416) # =>Dir KERNELBASE.EXITPROCESS
buffer = "\x90" * 136 # => Buffer length
var_c = "\x41" * 4
junk = "\x41" * 8
s = struct.pack("<I", 0x0022FEAC) # => Fix EBP jumping to exit
r = struct.pack("<I", 0x004012F7) # => Control over EIP and jmp to printf good message

payload = exit + buffer + var_c + junk + s + r # => jmp if not equal

print "[#] Sending Payload"
stdout = stdin.communicate(payload)[0]
print stdout

Se controla EIP y se apunta al basic block que genera el mensaje buscado:


El flujo es guiado hacia la zona correspondiente:


Continuando nuevamente al ret utilizamos EBP para alinear el stack a nuestra conveniencia:


Podemos apreciar como EBP posee el valor enviado en nuestro exploit, dando como resultado el desplazamiento a gusto en el stack:


Colocando en la próxima dirección a utilizar por EIP nuestra dirección a ExitProcess, en este caso esto se utiliza con el fin de generar una salida limpia del proceso pero podría utilizarse como base para otro tipo de ataque.

Resultado:




Debido a que el binario no pose DEP ni ASLR se podría ingresar una shellcode en nuestro input, localizarlo en el stack y saltar a su dirección controlando el flujo mediante EIP y de esta forma ejecutar código arbitrario, pero esto quedara para próximas entradas.



Sin más me despido dejando la primer entrada de esta serie de soluciones, espero sea de su agrado.

Saludos! 




Share this:

 
Copyright © 2014 Security Signal.
Designed by OddThemes | Distributed By Gooyaabi Templates