ROPfu PicoCTF (Hard)
Contexto de la maquina
Trayectoria ROPfu

Descripción
Este reto consiste en analizar y explotar un binario vulnerable en C que contiene un desbordamiento de buffer basado en stack. El programa utiliza la función insegura gets(), permitiendo que un atacante sobrescriba la dirección de retorno (EIP) y redirija el flujo de ejecución.
El objetivo del reto es explotar esta vulnerabilidad utilizando ROP (Return Oriented Programming) para ejecutar código arbitrario y obtener una shell en el sistema, desde la cual se podrá leer la flag.
Objetivo del reto
Explotar el desbordamiento de buffer presente en el programa para controlar el flujo de ejecución y ejecutar código que permita obtener acceso a una shell interactiva y leer el archivo que contiene la flag.
Tipo de reto
Explotación de binarios
Linux
Stack Buffer Overflow
Return Oriented Programming (ROP)
Habilidades y técnicas evaluadas
Análisis de código fuente en C
Identificación de vulnerabilidades de memoria
Explotación de stack buffer overflow
Cálculo de offsets en memoria
Uso de Pwntools
Uso de GDB para análisis de memoria
Búsqueda de gadgets con ROPgadget
Construcción de payloads con shellcode
Explotación remota mediante netcat
Análisis de vulnerabilidades

Despliegue del CTF
En la propia pagina buscaremos el CTF, dentro veremos dos archivos los cuales nos podremos descargar llamados vuln y vuln.c.
El objetivo de estos CTFs es encontrar la flag final.
Análisis del código C#
Si leemos la descripcion del reto.
La descripción nos indica básicamente que debemos explotar un programa vulnerable utilizando ROP (Return Oriented Programming) para poder obtener la flag.
Para ello nos proporcionan:
El código fuente del programa
Un servicio remoto accesible mediante netcat
Por lo tanto, lo primero que debemos hacer es analizar el código fuente para entender dónde se encuentra la vulnerabilidad y cómo podemos aprovecharla.
Código vulnerable

vuln.c
Preparando el entorno local
Antes de analizar la vulnerabilidad, vamos a crear un archivo llamado flag.txt, ya que estamos realizando el laboratorio en local.
De esta forma, cuando consigamos explotar el programa, podremos comprobar que la explotación funciona correctamente leyendo la flag desde nuestro entorno.
Más adelante, cuando tengamos el exploit funcionando, lo utilizaremos contra el servicio remoto del reto para obtener la flag real.
Explotación (ROP - Return Oriented Programming)
Identificación del Offset
Lo primero es encontrar el offset exacto necesario para sobrescribir la dirección de retorno (EIP) en el stack. Para ello utilizamos la librería Pwntools, que nos permite generar patrones cíclicos únicos y así identificar con precisión en qué posición ocurre el overflow.
El procedimiento consiste en:
Generar un patrón cíclico suficientemente grande.
Enviarlo como input al binario vulnerable.
Esperar a que el programa crashee.
Analizar el core dump para ver qué valor ha sobrescrito el registro EIP.
Usar ese valor para calcular el offset exacto.
offset.py
Lo ejecutamos de la siguiente forma:
Respuesta:
Al analizar el core dump vemos:
Ese valor pertenece al patrón cíclico generado anteriormente. Usando cyclic_find() obtenemos:
Esto significa que tras 28 bytes comenzamos a sobrescribir EIP.
¿Por qué usar offset 26 y no 28?
Aunque el offset calculado es 28, en el exploit final utilizaremos 26 bytes de relleno.
Esto se debe a cómo queremos posicionar nuestro shellcode en el stack.
Analizando el stack vemos:
Las
A(relleno) empiezan en0xffffc06cLa dirección de retorno está en
0xffffc088
La diferencia entre ambas posiciones es:
Por tanto, el offset teórico es 28.
Sin embargo, nuestro shellcode necesita colocarse inmediatamente después de la dirección de retorno y alinearse correctamente para que el flujo de ejecución continúe de forma estable.
Si usamos offset 28, el layout sería:
Esto en teoría funciona, pero dependiendo del alineamiento del stack puede provocar que el flujo de ejecución no salte exactamente donde esperamos.
En cambio, con offset 26 obtenemos:
La diferencia es pequeña, pero al desplazar ligeramente el payload conseguimos que ESP apunte exactamente a la zona donde empieza nuestro código, evitando problemas de alineación y haciendo la explotación más fiable.
Verificación con GDB
Para confirmar el offset podemos utilizar GNU Debugger y observar directamente el contenido del stack tras el crash.
Ejecutamos el binario en GDB:
Respuesta:
El valor 0x48 corresponde al carácter H, lo que indica que las H han sobrescrito EIP.
Ahora examinamos el stack:
Respuesta:
Observamos:
0x41414141→ AAAA0x42424242→ BBBB...
0x48484848→ HHHH
Esto confirma que el crash ocurre exactamente en la posición esperada.
Búsqueda de Gadgets Útiles
Tras la llamada a gets(), el registro EAX apunta al inicio del buffer donde se encuentra nuestro input.
Por tanto, si encontramos un gadget jmp eax, podremos redirigir la ejecución directamente hacia nuestro shellcode.
Para buscar gadgets utilizamos ROPgadget:
Respuesta:
Este gadget es ideal porque simplemente ejecuta:
y como EAX ya apunta a nuestro buffer, la ejecución saltará directamente a nuestro payload.
Construcción del Exploit
El flujo final del exploit será:
26 bytes de padding para alcanzar EIP
Incluir el shellcode que contiene:
jmp esp→ saltar al stackdirección de
jmp eax→ redirigir la ejecuciónshellcode que ejecuta
/bin/sh
exploitLOCAL.py
Ejecutamos el exploit:
Respuesta:
Con esto conseguimos obtener una shell interactiva y leer la flag, confirmando que el exploit funciona correctamente.
Vamos a utilizar este exploit en el reto real conectandonos de forma remota, para ello tendremos que ajustar el script que hemos creado para que se conecte al reto real y lo explote cuando lo ejecutemos:
Explotación del reto (Remoto)
A continuación se muestra el script utilizado para explotar el servicio remoto.
exploit.py
Ahora ejecutamos el script:
Respuesta:
Una vez que el payload se ejecuta correctamente, obtenemos una shell remota en el servidor del reto.
Desde esta shell simplemente leemos el archivo que contiene la flag.
flag.txt
Last updated