function overwrite PicoCTF (Hard)
Contexto de la maquina
Trayectoria function overwrite

Descripción
Este reto consiste en analizar un binario vulnerable escrito en C que utiliza punteros a funciones. El objetivo es identificar una vulnerabilidad de memoria que permita modificar el flujo de ejecución del programa y provocar que se ejecute una función distinta a la prevista originalmente.
El programa permite introducir datos desde la entrada estándar y posteriormente realiza operaciones sobre un array de enteros. Debido a una validación incorrecta de índices, es posible acceder a posiciones de memoria fuera de los límites del array, lo que permite modificar un puntero a función global.
Objetivo del reto
Explotar la vulnerabilidad del programa para redirigir la ejecución hacia una función alternativa que permita revelar la flag.
Tipo de reto
Binario
Linux
Explotación de memoria
Punteros a funciones
Habilidades y técnicas evaluadas
Análisis de código fuente en C
Comprensión de punteros a funciones
Explotación de out-of-bounds array access
Manipulación de memoria
Uso de herramientas de reversing (
objdump,gdb)Comprensión del layout de memoria de variables globales
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.
Analisis del binario C#
Si leemos la descripcion del reto...
La descripción básicamente nos indica que debemos analizar un programa que utiliza function pointers en C, lo cual ya nos da una pista importante, ya que los punteros a funciones suelen ser un objetivo común en vulnerabilidades de memoria.
También nos indican que podemos descargar:
El binario ya compilado
El código fuente del programa
Antes de comenzar con el análisis vamos a crear una flag de prueba local para poder probar el exploit en nuestro entorno.
De esta forma, si conseguimos explotar el programa correctamente, podremos comprobar que se lee el contenido del archivo flag.txt.
Análisis del código

Ahora vamos a analizar el código proporcionado.
vuln.c
Vemos en el codigo que tiene una pequeña vulnerabilidad en esta parte de aqui:
Es un desbordamiento de array que permite modificar el puntero de función check. La validación es débil porque solo verifica que num1 sea menor que 10, pero no verifica que sea mayor o igual a 0.
Si ponemos num1 negativo, podemos acceder a posiciones de memoria antes del array fun. Esto nos permite modificar el puntero de función check que está antes en memoria.
Diseño de memoria
En memoria, asumiendo que fun y check están cerca en la sección de datos:
Si hacemos num1 = -1, estaríamos modificando fun[-1], que sería justo donde está almacenado el puntero check. Sin embargo, necesitamos verificar el offset exacto.
Obteniendo direcciones
Primero obtenemos las direcciones de las funciones con objdump:
Respuesta:
Calculamos la diferencia:
Queremos que check (que apunta a hard_checker) pase a apuntar a easy_checker. Necesitamos restarle 314 a la dirección actual.
Encontrando el offset correcto con GDB
Usamos GDB para encontrar la posición exacta de check relativa a fun:
La diferencia es de 64 bytes. Como es un array de enteros de 32 bits (4 bytes cada uno), el offset en índices es:
Por lo tanto, check está en fun[-16], no en fun[-1].
Análisis del assembly
Desensamblamos vuln para confirmar:
Respuesta:
Observamos líneas clave:
0x080495f8 <+136>: mov 0x80(%ebx,%eax,4),%ecx→ acceso afunenebx+0x800x08049614 <+164>: mov 0x40(%ebx),%esi→ carga decheckenebx+0x40
Confirmamos la diferencia de 64 bytes (0x80 - 0x40 = 0x40 = 64).
Calculando el payload
Necesitamos:
num1 = -16(offset correcto para llegar acheck)num2= diferencia entre direcciones = -314
Cuando se ejecute fun[-16] += -314, estaremos restando 314 a la dirección de hard_checker, obteniendo así la dirección de easy_checker.
Verificando la modificación
Ponemos un breakpoint justo después de la modificación y antes de la llamada:
Respuesta:
Ahora check apunta a easy_checker (0x080492fc)
Obteniendo la flag (local)
Ahora necesitamos que easy_checker nos dé la flag. Esta función requiere que la suma de los caracteres del story sea exactamente 1337.
Calculamos un string que sume 1337:
Carácter 'z' = ASCII 122
10 'z' = 1220
Necesitamos 117 más → carácter 'u' (ASCII 117)
String final: "zzzzzzzzzzu" (10 z's + 1 u)
Ejecutamos el exploit:
Respuesta:
Aquí ocurren dos cosas importantes:
-16permite escribir fuera de los límites del arrayfun, accediendo a memoria anterior al array.Esa escritura modifica el puntero a función
check, haciendo que ahora apunte aeasy_checker.
El segundo valor (-314) es el valor que se suma en la operación:
lo que termina ajustando el contenido de memoria hasta que el puntero a función coincide con la dirección de easy_checker.
De esta forma conseguimos redirigir el flujo de ejecución del programa hacia la función que nos interesa.
Explotación en el reto remoto
Una vez comprobado que el exploit funciona correctamente en local, podemos utilizar exactamente los mismos valores contra el servicio remoto que proporciona el reto.
Nos conectamos con nc:
Respuesta:
Con esto veremos que hemos obtenido correctamente la flag del servidor remoto, explotando la vulnerabilidad del programa.
En resumen, el exploit consiste en:
Aprovechar la falta de validación de índices negativos en el array
fun.Utilizar ese acceso fuera de límites para sobrescribir el puntero a función
check.Redirigir la ejecución hacia
easy_checker.Enviar un
storycuya suma ASCII sea 1337 para que la función imprima la flag.
flag.txt
Last updated