Hoy vamos a ver cómo resolver el challenge 7 de la room pwn101 de TryHackMe.
Introducción:
Este reto pertenece a la room PWN101 de TryHackMe. En él, veremos como aprovechar un format string vulnerability para conseguir leakear un canario además de un leak de pie. Posteriormente, saltaremos a una función get_streak para conseguir una instancia de /bin/sh.
Análisis inicial del binario:
Para comenzar, vamos a mirar ante qué tipo de binario nos estamos enfrentando.
$ file pwn107.pwn107 pwn107.pwn107: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0579b2a29d47165653fbb791fb528c59e951a1a0, not stripped
Se trata de un ELF de 64 bits, enlazado dinamicamente y no stripped. Esto último nos ayudará en la fase de reversing.
Ahora, vamos a mirar las protecciones del mismo. Para ello, podemos utilizar la utilidad checksec.
$ checksec pwn107.pwn107 [*] '/home/diegoaltf4/pwn107.pwn107' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
Vemos que tiene canarios, NX y PIE. Esta información es muy importante para poder explotar adecuadamente el binario.
Análisis del decompilado:
Para esta parte, podemos emplear la herramienta ghidra.
Dado que el binario no está stripped, podemos tener acceso a toda la lista de símbolos.
Vamos a analizar la función main:
Bien, el programa nos pide un primer input que guardará en lo que he nombrado input1. Esta variable tiene un tamaño máximo de 32 bytes y en el read se leen como máximo 20 bytes. Seguidamente, imprime una cadena y el valor de input1. Dado que no especifica el formato de la variable que se va a imprimir, estamos delante de un Format String Vulnerability.
Posteriormente, nos pide un segundo input que almacena en la variable input2. Esta variable tiene un tamaño máximo de 24 bytes y en el read se leen como máximo 512 bytes. Aquí nos encontramos con la segunda vulnerabilidad.
Finalmente, comprueba que el canario no haya sido modificado. Si ha sido modificado, saldrá del programa al grito de: ** stack smashing detected : terminated. **
Mirando el resto de funciones, podemos encontrar una muy interesante get_streak. Si conseguimos saltar a esta función podremos conseguir una shell:
Explotación:
Lo primero que tenemos que hacer es conseguir un leak del canario usando el format string vulnerability. Para esta tarea podemos emplear la herramienta GLUFS que desarrollé hace poco. Nos va a ayudar a automatizar todo el proceso de obtención de leaks.
La instalación es muy simple:
git clone https://github.com/Diego-AltF4/GLUFS.git cd GLUFS/ pip3 install -r requirements.txt chmod +x ./glufs.py
Ahora hay que modificar un pequeño fragmento del script para adaptarlo a nuestro binario. La zona que hay que modificar está claramente indicada y en nuestro caso quedaría de la siguiente forma:
Ya podemos empezar a buscar leaks. Usaremos la opción –canary y la opción –pie
Como se puede ver, hemos encontrado bastantes cosas:
Por ejemplo, con el payload %11$p conseguimos un leak de pie y con el payload %13$p conseguimos un leak del canario. Ahora solo queda analizar a qué distancia se encuentra el leak de pie de la base del binario. Para esto vamos a iniciar gdb (yo voy a utilizar gdb-pwndbg).
Pondremos un breakpoint en b *main+164 que es cuando imprime nuestro input1, es decir, nuestro payload.
Le daremos a run, introduciremos el payload %11$p
ni para continuar con la ejecución:
Ahí podemos ver el supuesto leak de pie.
Y ahora, calculamos la diferencia entre esta dirección y la base. Dado que los offsets se mantienen, una vez tengamos el leak de pie, podremos calcular la base del binario.
Ahora podemos comprobar que el leak del canario es correcto.
Ponemos un breakpoint en main+8 (b *main+8)
Ejecutamos el binario (r)
Avanzamos a la siguiente instrucción (ni)
Como podemos ver en el assembly de ghidra, el canario queda almacenado en RAX:
Ahora inspeccionamos este valor:
Ahí tenemos el canario que genera el binario. Continuamos la ejecución e introducimos como payload el que nos saca GLUFS: %13$p
Como podemos ver, el output contiene el mismo valor que tenía RAX. Por tanto, el leak es correcto.
Teniendo todo esto listo, podemos escribir el exploit final:
La idea es conseguir el leak del canario, el leak de pie y calcular la base del binario. Seguidamente, explotaremos el BOF que se produce con el input2 (24 bytes), sobrescribiremos el valor del canario con el leakeado, sobrescribiremos RBP y pondremos en RIP la dirección de get_streak. Vamos a conseguir la dirección de get_streak gracias al leak de PIE que nos permite obtener la base del binario.
El exploit final queda así:
Es importante mencionar que tenemos que hacer un bypass del movaps. Es por ello por lo que he introducido un return gadget. Nuevamente, para calcular correctamente su dirección hay que conocer la base del binario.
Ejecución en local:
Podemos ver que obtenemos una shell.
Ejecución en remoto:
Con esto terminamos el challenge 7 de la room pwn101 de TryHackMe. Espero que os haya gustado y, lo más importante, que hayáis aprendido.
Que la Fuerza os acompañe
DiegoAltF4
Inicio
Posibles artículos de interés:
Deja una respuesta