QuickScan

Catégorie: Reverse Difficulté: medium Flag: HTB{y0u_4n4lyz3d_th3_p4tt3ns!}

Challenge

Description


QuickScan is a Medium reversing challenge. Players will be sent a series of small, randomly generated ELF files and must rapidly and automatically anlalyse them in order to extract required data.

Explications

On peut s’amuser à enregistrer 2-3 ELF que le serveur nous donne. On s’aperçoit rapidement d’un pattern : bien que la fonction entry change de position à chaque fois, elle reste toujours la même dans ses instructions et l’adresse où se trouve les données attendues est mise dans RSI grâce à l’instruction LEA

Ici les données attendues par le serveur sont à l’adresse 0x08048104

Ce qui est important c’est la valeur en vert dans l’instruction LEA, il s’agit de l’offset utilisé pour trouver l’adresse de nos données. Ici c’est 0xfffffdb9 (on est en little endian) ce qui nous donne -583 (cette valeur est signée)

Actuellement le LEA est à l’adresse 0x08048344 et la prochaine instruction est 7 bytes plus loin, soit 0x0804834b

Donc si l’on fait 0x0804834b - 583 on tombe bien sur 0x08048104, c’est à dire les données à récupérer.

En résumé, il faut :

  1. Trouver l’adresse d’entry dans notre binaire

  2. Récupérer l’offset à adresse(entry) + 7

  3. Calculer adresse(entry) + 11 + offset

  4. Lire les 24 bytes


Script de résolution

from base64 import b64decode
from io import BytesIO
from struct import unpack
from elftools.elf.elffile import ELFFile
from pwnlib.tubes.remote import remote

def get_value(elf_content: bytes):
		# On met les données sous forme de stream pour que elftools puisse le lire
    with BytesIO(elf_content) as f:
        elf = ELFFile(f)
        
        # Adresse physique à laquelle le segment est chargé
        base_address = elf.get_segment(0)['p_vaddr'] # 0**x08048000 dans notre exemple**
        
        # Adresse d'entry du script
        entry = elf.header['e_entry']                # **0x08048340 dans notre exemple**
        
        # La différence des deux permet de trouver l'adresse dans le binaire
        eip = entry - base_address                   # 0x340 dans notre exemple
        
        # On a les bytes bytes sur SUB puis les 3 du LEA
        eip += 4 + 3                                 # 0x347 dans notre exemple
        
        # A partir de ça on peut lire l'adresse qu'utilise LEA directement dans le binaire
        f.seek(eip)
        data_offset = unpack('i', f.read(4))[0]      # 0xfffffdb9 (-583) dans notre exemple
        
        # Cet adresse est un offset qui dit à où aller à partir de d'EIP lui même
        bin_addr = eip + 4 + data_offset             # 0x347 + 4 + -583 = 0x104 (260) dans notre exemple
        
        # Récupération des données
        f.seek(bin_addr)
        return f.read(0x18).hex()

def solve(host, port):
    client = remote(host, port)
    for i in range(128 + 1):   # + 1, the first question doesn't count
        client.recvuntil(b'ELF:  ')
        elf_content = b64decode(client.recvline())
        answer = get_value(elf_content)
        client.sendlineafter(b'Bytes? ', answer.encode())
    flag = client.recvall(timeout=1).decode().split('flag: ')[1]
    print(f"Flag: {flag}")

if __name__ == '__main__':
    solve('localhost', 1337)

Dernière mise à jour

Cet article vous a-t-il été utile ?