Categorías
Uncategorized

Análisis del Examen curso de ida pro nivel 12 (curso de Ricardo Narvaja)

Este ejercicio se trata de un Stack based buffer overflow y el objetivo es  ejecutar calc.exe, sin más que decir empecemos con el análisis

Abrimos nuestro ejecutable con el depurador de ida y parados en un breakpoint podemos cerciorar con process explorer que este no cuenta con protecciones como DEP y ASLR

Ahora analicemos el mismo en IDA

Parados en la función main vemos que lo primero que hace es verificar si el numero de argumentos pasados por consola es dos o mayor a dos

En caso de que la condición no se cumpla imprimirá en consola bye y saldrá del programa y en caso de que la condición se cumpla llamara a un método constructor pasando como parámetro Destination, renombremos esta función a constructor, Como podemos apreciar Destination es una variable del stack, veamos que hace el constructor

Esta toma el this y en el offset 0xc8 coloca el puntero a la string “A ejecutar la calculadora de nuevo…”, creemos la estructura
Hasta ahora no sabemos el tamaño de la estructura, pero podemos tomar todo el tamaño del stack y si es necesario corregimos más adelante, así que lo que aremos será resaltar toda esa zona del stack y presionaremos Create Struct from selection

Ahora si abrimos view->open Subview->Structures

Podemos ver y editar nuestra estructura, así que vallamos al offset 0xc8 de nuestra estructura y nombrémoslo str_aEjecutar

Presionamos la tecla D para cambiar el tipo varias veces hasta que quede de tipo DD

Luego presionamos la N para cambiar el nombre a str_aEjecutar

Ya que hemos creado nuestra estructura podemos regresar a nuestro constructor

Con el offset de la estructura seleccionado apretamos T y seleccionamos nuestra estructura

Nos queda de esta forma, regresemos al main para continuar con nuestro análisis

Lo siguiente que hace es guardar el puntero a system en un miembro de la estructura renombremos Destination a my_estruct y var_8 a ptr_system

Luego con memset va a colocar a cero dos buffers de tamaño 0xc8 el primero en el offset cero y el segundo en el offset de var_d4, editemos nuestra estructura
Renombramos nuestros miembros a bufer1_200 y buffer2_200 y los convertimos en array de tamaño 0xc8 con clic derecho array

Array size 0xc8 y presionamos ok

Nuestra estructura nos quedó de esta forma

Luego toma un buffer y lo inicializa a ceros este tiene un tamaño también de 0xc8 o 200 decimal

Luego lo que hace es copiar el argumento pasado por consola y copiarlo al buffer1_200 y luego usa esa string como un nombre de archivo que se le pasa a fopen en modo lectura así que renombraremos buffer1_200 a filename, como usa strcpy que es una función insegura y no ha realizado ningún chequeo aquí podemos overflodear, pero sigamos analizando

Guarda el handle del archivo en my_struct.Stream y chequea si es cero, en caso de serlo imprime que no se pudo leer el archivo y sale, en caso contrario imprime la string str_aEjectar con lo que imprime “A ejecutar la calculadora de nuevo…” luego lee 0xc8 bytes del archivo en buffer que como es del tamaño correcto no hay overflow

Con strlen saca el largo del buffer lo guarda en var_4 y lo imprime, cambiemos el nombre de var_4 a len_buffer

Luego verifica el largo del buffer con 0xc8 si no es menor sale, este chequeo solo verifica el contenido del archivo hasta que encuentra un cero, en caso de que pase el chequeo llama a una función sub_401030 pasándole como argumentos el largo del buffer, el puntero al buffer, y una copia de my_struct en el stack. Veamos que hace esta función

Esta función lo que hace es tomar el filename (primer campo de la estructura) y pasarlo a fopen el cual lo abre en modo lectura binaria, luego lee 0x7d0 (2000) bytes del archivo y los almacena en buffer el cual es de 0xc8 (200), algo que se me olvidaba mencionar es que los números en Hexa los podemos convertir en decimales seleccionándolos y presionando la letra H

Aquí vemos que el buffer que es de 200 se escribirán 2000 bytes así que tenemos un overflow, luego imprimirá el buffer y retornará.

Cuando retorna de esta función retorna del main y nuestro programa terminará

Renombremos esta función a vulnerable

Ahora veamos cómo podemos explotar esta función vulnerable, para esto tenemos que tener en cuenta los chequeos que realiza el programa y cumplirlos todos para poder llegar a esa función

Primero debemos pasarle como argumento el nombre del archivo a leer

Este archivo debe tener una string de menos de 200 bytes. Creemos nuestro archivo con Python, así que abramos nuestro editor favorito y creemos un script de Python para explotar este programa

Lo primero que hacemos será importar pack y creamos un archivo en modo escritura, pero ¿Qué escribiremos?, lo que tenemos que hacer es determinar en qué offset se encuentra nuestro retorno, para que al pisarlo con determinado valor el programa salte a ejecutar la dirección que nosotros controlamos, generalmente lo que se hace es pisar el ret por un jmp esp, call esp, o push esp ret o cualquier instrucción que salte a ejecutar el stack y en este tendremos nuestra shellcode la cual se ejecutará. Bien teniendo esto claro pasemos a determinar el offset de nuestro ret

En la pestaña del stack de nuestra función main seleccionamos desde el inicio del buffer que vamos a overflodear hasta el estored ebp (s) presionamos array y este nos mostrará la cantidad de bytes necesarios para pisar el ret

En este caso 620
También podemos encontrar el offset mediante el uso de patterns esta técnica consiste en llenar el buffer con cadenas de caracteres únicos y al final con el debugger ver el valor de eip y buscar ese valor en la cadena y así determinar el offset exacto donde se pisa eip

En nuestro script lo que hicimos fue crear una variable llamada primeraComp que contiene 100 Aes y un null terminator esto con el objetivo de pasar la comparación que verifica que strlen sea menor a 200, lo que hace strlen es contar todos los caracteres y parará cuando encuentra el cero, luego lo que hicimos es crear una variable junk que contiene una formulita que desglosaremos a continuación

sta primera parte agrega a junk el contenido de primeraComp que es nuestras 100 Aes

Luego en el paréntesis se calcula (620-len(primeraComp)) esto lo hacemos para obtener la diferencia es decir queremos saber el numero de Aes faltantes para que en total tener 620 y estas Aes las concatenamos con las anteriores teniendo en total 620 bytes el cual corresponde a 100 Aes un NULL y EL resto de Aes

Luego creamos una variable llamada eip el cual contiene el valor que pisara nuestro registro eip

Por último, lo que hacemos es escribir nuestro junk+eip que corresponde a los 620 bytes junk y eip será nuestro valor controlado al cual saltaremos, depuremos

En la pestaña debugger->switch debugger seleccionamos local Windows debugger

Damos clic en debugger->Process options

Y en parameters le colocamos el parámetro que en este caso es el nombre del archivo

Luego de haber ejecutado nuestro script de Python colocamos el archivo generado en la misma carpeta que el ejecutable, ponemos un breakpoint en main y presionamos el botón de start

Pasa satisfactoriamente la primera comparación

pasa la comparación del largo del buffer

y se dirige a ejecutar nuestra función vulnerable, presionamos step into

Dentro de la función abre el archivo nuevamente

Lee los 2000 bytes, en este punto la dirección de retorno de la función main debe estar pisada por 0x41424344 veamos

Retorna de la función vulnerable y se acerca al ret de main, avancemos hasta estar parados en el ret

Parados en el ret vemos que este retornará a 0x41424344 con lo que satisfactoriamente controlamos la dirección de retorno, ahora ya que controlamos la dirección de retorno coloquemos nuestra shellcode en el stack y retornemos a un jmp esp para ejecutar nuestra shellcode

Nuestra shellcode es muy sencilla lo que hace es mover a edx un puntero a una zona escribible y en esta escribir “calc” (clac esta al revés por ser Little endian) luego mueve a eax el offset de la iat de system le pasa como parámetro nuestro puntero a calc y llama a system(“calc”).

Lo siguiente que debemos hacer es saltar a ejecutar el stack, pero en este caso no tenemos ninguna instrucción jmp esp o similares ¿Qué podemos hacer?

Si bajamos un poco en el stack podemos ver varios punteros al stack, lo que podemos hacer es pisar el ultimo byte con el offset al inicio de nuestra shellcode, el puntero esta en 19ff60 y nuestro ret está en 0019FF28 , calculemos el desplazamiento para pisar este valor:

0x19FF60-0x19FF28 = 0x38 = 56

56 bytes, pero estos no pueden ser cualquier valor ya que si intentamos retornar a una dirección invalida nos lanzara una excepción una solución es colocar direcciones a ret de tal manera que retorne y retorne sucesivamente hasta llegar al valor, hagámoslo

Hemos hecho nos cambios a nuestra shellcode, des pues de nuestro buffer colocamos nuestra shellcode, seguido de Aes hasta completar 620 y luego 13 rets y por ultimo los bytes que pisa el puntero al stack con el offset de nuestra shellcode, si ejecutamos

Podemos ver que nuestra shellcode es ejecutada, depuremos

Colocamos un breakpoint en el return del main y ejecutamos

Parados en el ret vemos en el stack que el programa retornara a 0x401077 que es una instrucción ret

En esta imagen podemos apreciar la instrucción a la cual retornará

Luego de retornar varias veces en el stack queda el puntero al stack qué le pisamos los últimos bytes para que apuntara a nuestra shellcode al presionar ret se ejecutará nuestra shellcode

Nuestra shellcode escribe en 0x4030D0 calc y luego llama a system(“calc”) ejecutando así nuestro calc

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *