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 "speedrun10", y que, en mi opinión, presenta un nivel de dificultad medio (★★★☆☆).
- Enunciado:
- Solución: Se proporciona un archivo ejecutable (chall_10) y lo primero que hago es ejecutarlo; incluyo una cadena larga ('AAA…A') y el programa finaliza:
Después, compruebo los mecanismos de seguridad del binario utilizando ‘checksec’:
Se trata de un binario de 32 bits y como se ve en la figura anterior NX está habilitado, por lo que ya sé que en el caso de que sea vulnerable a un desbordamiento de ‘buffer’ (en inglés, ‘buffer overflow’) no podré inyectar ‘shellcode’ en la pila (en inglés, ‘stack’) para ejecutarlo, y PIE está deshabilitado, por lo que es fácil para un atacante el uso 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’.
Sin embargo, también veo que se realiza una llamada a la función vuln():
Esta función
utiliza gets(), por lo que
es susceptible a un ataque de desbordamiento de ‘buffer’.
El ‘buffer’ tiene un tamaño de 54 bytes y, conforme al nombre de la variable local_3e en ‘Ghidra’, la dirección de retorno de la función vuln() se encontraría desplazada 0x3e (62) bytes desde la dirección de inicio del ‘buffer’.
Asimismo, veo que hay una función win() a la que no se llama nunca:
Esta función
recibe como entrada un parámetro (
param_1)
y si el valor de éste es ‘-0x21524111’ se realiza una llamada a system() con argumento iVar1 +
0x12e, es decir,
la llamada que se realiza es: system(“/bin/sh”) (ver explicación en este
post),
lo que me permitiría obtener una ‘shell’ en el servidor.
Antes de seguir, cabe recordar que en los binarios de 32 bits, como es el caso, los parámetros se pasan a las funciones a través de la pila.
Por tanto, mi plan de ataque consiste en sobrescribir la dirección de retorno de la función vuln() con la dirección de inicio de la función win(), pasándole a ésta como argumento ‘0xdeadbeef’ (-0x21524111), con lo que obtendré una ‘shell’, podré buscar y ver la flag, y, en consecuencia, podré resolver este reto.
Quizá se vea mejor desensamblando la función win():
Como se ve en la figura anterior,
se compara el contenido de la dirección que contiene el registro EBP (’Extended Base Pointer’)
+ 0x8 con el valor‘0xdeadbeef’ y si son iguales se realiza la llamada a system(“/bin/sh”), y, en consecuencia, se abre la
‘shell’.
Tal y como he dicho anteriormente, la dirección de retorno de la función vuln() se encontraría con un desplazamiento de 0x3e bytes desde la dirección de inicio del ‘buffer’, con lo que el tamaño del relleno de la cadena que debo incluir antes de la dirección de inicio de la función win() es de 62 bytes.
Para implementar el ataque creo un pequeño ‘exploit’ mediante un ‘script’ en python para que envíe 62 bytes (0x3e) de relleno, luego la dirección de inicio de la función
win() y, finalmente, el valor ‘0xdeadbeef’, con lo que se bifurcará a esa función, pasándole ‘0xdeadbeef’ como parámetro (
param_1) y se abrirá una
‘shell’:
#!/usr/bin/env python3
from pwn import *
binary = ELF('./chall_10')
p = remote('chal.2020.sunshinectf.org', 30010)
p.sendlineafter('Don\'t waste your time, or...\n','')
payload = b'A' * 0x3e
payload += p32(binary.sym.win)
payload += p32(0)
payload += p32(0xdeadbeef)
p.sendline(payload)
p.interactive()
Lo ejecuto:
A partir de que se abra la
‘shell’,
lo único que queda por hacer es buscar la flag en el servidor. Veo que
hay un fichero llamado flag.txt que contiene la solución a este reto: sun{second-heartbeat-aeaff82332769d0f}
Comentarios
Publicar un comentario