Stack Buffer Overflows
A buffer with no bounds check plus your input equals a crash you can steer.
What you will learn
- -Trigger a crash by overflowing a stack buffer
- -Find the exact offset from your input to the saved return address
- -Confirm you control rip by watching it in gdb
Feed a program more bytes than its buffer can hold and one of two things happens. Either the program crashes with a segfault, or, if you are precise, it does exactly what you tell it to. The bug is old and simple: a function copies your input into a fixed-size buffer without checking how long your input is. gets, strcpy, sprintf, and a bare scanf("%s", ...) all do this. They keep writing until they hit a null or newline, straight past the end of the buffer.
Here is the target for this lesson. Compile it yourself so the mitigations are off and you can see the mechanics naked:
#include <stdio.h>
void vuln() {
char buf[64];
gets(buf); // no bounds check, writes as much as you send
puts(buf);
}
int main() { vuln(); }
$gcc -fno-stack-protector -z execstack -no-pie -g vuln.c -o vulnWatch it die
buf is 64 bytes. Send 64 and it fits. Send 200 and you scribble past the buffer, over the saved rbp, and into the saved return address. When vuln runs ret, it pops your garbage into rip and the CPU tries to execute an address that is not code.
$python3 -c 'print("A"*200)' | ./vuln$dmesg | tail -1Seeing 0x41414141 in the fault is the whole point. Those are your A bytes sitting in the instruction pointer. That is not a random crash. That is you, holding the wheel, just not yet steering.
Find the exact offset
Two hundred As proves you can crash it. To it you need the precise distance from the start of your input to the four (really eight) bytes that land on the return address. Guessing is for amateurs. Use a cyclic pattern, a string where every chunk is unique, so the value in rip tells you the offset directly.
$cyclic 200 > pattern.txt$gdb -q ./vuln$cyclic -l 0x6161616cgef and pwndbg ship pattern create and pattern search that do the same thing inside the . However you slice it, the answer for this binary is 72: sixty-four bytes of buffer plus eight bytes of saved rbp, and then your next eight bytes are the return address.
Confirm control
Prove the offset before you build the real . Send 72 filler bytes then eight Bs and check that rip (or the fault address) is exactly 0x4242424242424242. If it is, you own the return address to the byte, and the next lesson turns that into a jump you chose.
Overflowing a program you compiled on your own machine is practice. Sending overflow payloads at a service you do not own is an attack on a live and illegal without . Keep every payload in this module pointed at localhost or a lab you are scoped for.
After running a cyclic pattern into the vulnerable binary, gdb shows rip holds 0x6161616c. What does that value tell you?