Overwriting return address

Consider again the vulnerable password checking of previous class:

When we compile the program with option -fno-stack-protector the strcpy can overflow pwd_buffer and overwrite auth_flag, setting it to 1 and bypassing password check.

In the following variant, we do not have auth_flag but strcpy can still overflow pwd_buffer.

Now there is no variable to overwrite in order the change the return value and the above attack has no effect (even when compiled with -fno-stack-protector, i.e., with stack protector disabled)

What happens if we increase even more the length of the input?

Program gives a ‘segmentation fault’ with no output.

What is happening?

  • Our input string overwrites the stack all the way to the saved ebp or the return address of the function check;
  • When function check returns, either the stack is corrupted or the program simply dereferences a junk address (what we have written on the stack) which is likely to be out of any meaningful segment, from which the error.

We depict the stack state just before function check returns:

saved base pointer (ebp of the calling function) (overwritten)
return address (overwritten)

We illustrate with gdb:

We set a breakpoint right after the call to strcpy and we run the program with input AAAAAAAAAAAAAAAAAAAAAAA, i.e., the biggest string that does not crash the program.

Now we can have a look at the stack:

We can see all the 0x41 corresponding to ‘A’ and the terminating 0x00 (notice the little-endianess). Since we have picked the longest string not crashing we must be right before the stored ebp. In fact 0xbffff8a8 is right after the current stack addresses and 0x0804852b is close to the address of check function. We can check that it’s the return address by disassembling the main function:

From the previous disassemble of main function we can easily discover the address of printf("AUTHENTICATED!\n"); :

If we manage to place this address in place of the return address we can make the program jump directly on the interesting part of the code (where authentication is successful).

Let us see if this works in gdb:

We managed to change the return address from 0x0804852b to 0x08048532 and we made the program jump directly into the “authenticated” branch!

Exploiting the vulnerability

We have seen that return address, if overwritten, can make a program jump to a different place when a function returns. How can this be exploited through a buffer overflow? The idea is to simply overflow the buffer with the desired return address so to overwrite the one on the stack. Recall that little-endianess requires to store bytes in reversed order, so to write 0x08048532 we have to in fact write 0x32850408.

A way to write arbitrary bytes on the terminal is to use echo with option -e and prefixing byte by \x:

We now take the shortest sequence of ‘A’ the crash the program, since this is overwriting one byte of the ebp with the terminating 0x00. We then add four more ‘A’ letters to cover the ebp and we place the address in little endian. We use ‘command substitution’ of bash, written $(command), to pass arbitrary sequences of bytes to our program. Command substitution executes the command and put its output in place of $(command). We thus use $(echo …) to execute echo and pass the resulting output as argument to pwdcheck2:

Ah! we get “AUTHENTICATED!” so the program is jumping where we wanted it to jump!

Tip: we could use perl to simplify building the attack payload as follows. Notice that x7 repeats 7 times the preceding string and . is used to concatenate:

Similarly, we can use python to shape the payload:

In fact we can simply repeat the address enough time as in

If we enable stack protector this attack is prevented.

We discuss how this is possible below.

The canary

Stack protector rearranges variables, as we have seen. It also adds a control value just before function return address, as follows:

  1. When function call happens, after pushing return address and ebp on the stack, the program pushes a special value called ‘canary’. This value is different for each running process, and is picked at random by the kernel when a process starts. With this protection enabled, the stack appears as follows:
    saved base pointer (ebp of the calling function)
    return address
  2. When a function completes, before returning to the caller, the canary value is checked:
    • If the value is not the expected one program aborts (with the message you have observed), detecting a stack corruption. Since the canary is in between variables and return address, if we overwrite the latter through an overflow we necessarily overwrite the canary. In our specific case we have:
      ... (overwritten)
      canary (overwritten)
      saved base pointer (overwritten)
      return address (overwritten)
    • If the value matches, the program jumps to the return addess

NOTE. The origin of name ‘canary’ comes from the (sad) use of canaries to check for toxic gas in mines. A dead canary indicated a dangerous situation and the mine was evacuated.

Leave a Reply

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