Challenge 1 - pwn101
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
pwn101-1644307211706.pwn101: 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]=dd42eee3cfdffb116dfdaa750dbe4cc8af68cf43, 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 pwn101-1644307211706.pwn101
[*] '/home/effect/Informatic/Cybersecurity/THM/pwn101/pwn101-1644307211706.pwn101'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Stripped: No
Les protections à retenir ici sont l'absence de Canary, ce qui nous permettra d'effectuer un Buffer Overflow classique.
Analyse du Code (Ghidra / GDB)
Je me dirige vers la fonction main. Grâce au fait que le binaire n'est pas strippé, la lecture est simplifiée.
On identifie rapidement deux éléments critiques :
- 1 : Une variable buffer dont on identifie la taille de 60 caractères (0x3c) grâce à l'analyse sous Ghidra.
- 2 : Une variable de vérification (que j'ai nommée check) située juste après le buffer dans la mémoire.
[!WARNING]
La fonction gets() est connue pour être vulnérable car elle ne vérifie pas la taille de l'entrée utilisateur. Elle continuera d'écrire en mémoire tant qu'elle ne rencontre pas de retour à la ligne, ce qui permet trivialement un Buffer Overflow.
La variable check est initialisée par défaut à 1337. L'objectif est de modifier cette valeur pour obtenir un shell.
Exploit
En lançant le programme, un texte explicatif s'affiche puis nous demande un input.
Deux cas de figure pour l'exploitation :
- 1 : Input de 61 caractères
On remplit totalement les 60 octets du buffer, et le 61ème caractère va écraser le premier octet de la variable check. La valeur ne sera plus 1337, la condition est validée -> Shell.
- 2 : Input de 60 caractères
On remplit les 60 octets. Cependant, en C, les chaînes de caractères se terminent par un octet nul (\0 ou null byte).
gets va donc écrire nos 60 'A', puis ajouter un \0 juste après, c'est-à-dire pile sur la variable check.
La valeur de check devient donc altérée (elle prendra une valeur contenant ce 0), la condition est validée -> Shell.
Création du payload
Voici le script Python utilisant pwntools pour automatiser l'attaque :
from pwn import *
#context.binary = binary = "./pwn101.pwn101"
payload = b"A"*0x3c + p64(0x42) # 0x42 = "B"
p = remote("10.80.144.84", 9001)
p.sendline(payload)
p.interactive()
Une fois le script lancé, nous obtenons un accès au système.
Flag : THM{7h4t's_4n_3zy_oveRflowwwww}