Protostar – Stack 0

Official description here.

This exercise demonstrates that when coded improperly, a variable set with a content longer than its defined size will be overflowed and the extra bytes will overwrite neighbor variables in the stack.

Here is the source code of the executable we will analyse, /opt/protostar/bin/stack0:

Two variables are declared:

  • a volatile integer referenced as modified (line 7)
  • an array of 64 char referenced as buffer (line 8)

The variable modified is set to 0 (line 10) and is never modified after that.

The variable buffer is used by the gets (line 11) function where the user can enter its own value.

You then have a if statement that will print “you have changed the ‘modified’ variable” in stdout if the variable modified is different than 0 and “Try again?” otherwise.

So if you followed the code, the variable modified will always be equal to 0, which means we should always have the text “Try again?” printed.

Let’s look at the assembly code with objdump:

I’m expecting the reader to know the basic of assembly, but for this first write-up I will review each instructions to make sure this is clear.

At 0x80483f4 (line 9), the program save the base pointer, then (line 10), set the new base pointer with the current stack pointer, aka the function prologue. The base pointer is usually static within a function and is used as a reference to point to a local variable while the stack pointer will always point to the top of the stack.

At 0x80383f7 (line 11), the stack pointer is “aligned” to 16 bytes.

At 0x80383fa (line 12), 0x60 bytes (96 bytes) are allocated in the stack. This is the result of declarations of variables modified (4 bytes) and buffer (64 bytes).

At 0x80483fd (line 13), the variable modified, referenced in the assembly code as [esp+0x5c], is set to 0. Let’s do a little exercise with GDB to read the stack before and after that instruction so we can identify where modified is located in memory.

For this, we will load stack0 in GDB and set a breakpoint at the instruction 0x80483fd:

Now let’s look at the registers to find where EBP and ESP are pointing:

ESP = ([EBP] AND 0xfffffff0) – 0x60 = (0xbffff7a8 AND 0xfffffff0) – 0x60 = 0xbffff7a0 – 0x60 = 0xbffff740

Let’s look now at the stack before modified is set to 0:

And after:

You can see at offset 0xbffff79c, the value change from 0xb7fd7ff4  to 0x00000000. So we have identified the variable modified in the stack.

As you can see, when declared, the value contained in the variable is not initialised. It keeps the value already in the stack previously set by operations earlier.

Let’s move to the next instructions, 0x8048405 (line 15), here we put in EAX the address ESP+0x1c, which is actually the address of the array buffer. In the next line (0x8048409), the same address is copied at the top of the stack. Here instead of pushing the value in the stack, the compiler has decided to first allocate the space in the stack, and then copy the value directly into it. So that would be the equivalent of :

The instruction at 0x8048409 is meant to prepare the stack for the function call in the following line (0x804840c). The function gets gets a line from the stdin stream and copy it in a buffer located at the address sent in argument.

To demonstrate it, I will show the stack before and after the command gets.

So here, I typed the message “Hello world, my name is Tosch —-“.

Assuming you already know the concept of endianness and the ASCII code, you will find the value “Hell” (from the string “Hello world”) in hexadecimal at 0x0xbffff75c: H = 0x48, e = 0x65, l = 0x6c and l = 0x6c.

A string in memory most of the time ends with a null character (0x00). This tell the program when to stop reading the value as a string. The null byte can be found at 0xbffff77e, so if we read the buffer, it will stop after “Tosch —-“:

We now have clearly identified the structure of the stack, with buffer in blue, modified in green, the top of the stack in orange and the base pointer in red.

stack-stack0

But what if the content entered at the gets function is longer than 64 bytes, the size of the array buffer? Will the content be truncated to fit the size? Will the variable expand to match the new size? Will it throw an error an stop the application?

Let’s figure it out. We will enter 65 characters, which in ASCII means 65 bytes. The null byte at the end is happened automatically, so it will be 66 bytes.

Here, I entered the string “AAABBBCCCDDD…” to make it easier to spot it in the stack. You can therefore see the double words 0x41414141, 0x42424242, etc. until 0x50505050, the end of the array buffer. However, we can see that the letter “Q”, 0x51 in ASCII code, overflowed the variable and thus overwrote the beginning of the variable modified, which is now 0x00000051.

If we continue, step by step, we will see that the assembly code has not changed, and the variable modified is still referenced at the address [esp+0x5c].

Indeed, at the instruction right after gets (0x8048411), the variable modified is moved into EAX. Then EAX is “tested” with itself, so basically we verify if EAX is 0 or not. If EAX is 0 it jumps to 0x8048427, if not it continues. Here in this case, since EAX is pointing to modified  and modified has been overwritten with a different value, the variable is not equal to zero anymore and therefore, the condition if(modified != 0) is met, which print the following text: “you have changed the ‘modified’ variable”.

We therefore demonstrated that a condition that seems impossible to meet can be fulfilled by overflowing a variable. In this case, it resulted with a simple text printed, but we will see later that the consequences could be more important.

Protostar write-ups: stack0stack1stack2

Leave a Reply

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