15

I'm trying to compile a simple buffer overflow example on a Debian i386 VM. The executable builds ok, but EIP does not get overwritten correctly despite being supplied a sufficiently large input to overflow the buffer and overwrite the pushed EIP on the stack. Here are my commands as root:

sysctl -w kernel.randomize_va_space=0
gcc test.c -z execstack -z norelro -fno-stack-protector -D_FORTIFY_SOURCE=0 -ggdb -o test

As you can see I have:

  • Made the stack executable
  • Removed stack canaries
  • Disabled ASLR system wide
  • Removed relro protections
  • Disabled gcc's FORTIFY_SOURCE protections

Here is test.c:

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

int main(int argc, char **argv) {
  char buf[128];
  if(argc < 2) return 1;
  strcpy(buf, argv[1]);
  printf("%s\n", buf);
  return 0;
}

I have experimented with crash strings (just all As) in length from 128-256. In gdb, the pushed EIP never gets overwritten. Realistically it should just be a string of 132 bytes to overwrite EIP as there are no other local stack variables.

Is there some other protection I need to disable when compiling? Or is there something else to disable with sysctl?

B-MO
  • 313
  • 2
  • 6

3 Answers3

15

While the accepted answer is correct in suggesting moving everything to a different function does not explain why.


As you can see in this question and answer main() functions often perform a stack alignment to ensure that the stack is setup properly. The following instruction is performed:

83 e4 f0 and esp, 0xfffffff0

This will align the stack to 8 bytes. What this means is that a subtraction operation was performed on esp. So in order to overflow the return address you need 128 bytes + <variable stack alignment> + <optional saved ebp> + <new_return_address>.

I compiled and ran your program (I had to add a -m32).

Starting program: /home/user/Private/misc/overflow_test  
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFaaaabbbbcccc
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFFFFaaaabbbbcccc

Program received signal SIGSEGV, Segmentation fault.
0x63636363 in ?? ()

I used 128 As, and then 4 of each new letter.

Decompiling the program we see the following lines:

;      .//overflow_test.c:4
;-- main: 
;-- sym.main: 
0x0804842d    55             push ebp  
0x0804842e    89e5           mov ebp, esp  
0x08048430    83e4f0         and esp, 0xfffffff0 
0x08048433    81ec90000000   sub esp, 0x90 

Here we see that 0x90 (144) is subtracted from the stack. Which means that more than 128 bytes of space is allocated for local variables. This SO answer gives some reasons why this might occur. So if you account for the 128 bytes, the 16 extra bytes added for the local variables, and assume 8 bytes of stack alignment: Then you have EBP overwritten with 0x62626262 and the return address with 0x63636363.


Normal functions generally don't perform this step of aligning the stack. Which is why when you moved it into a separate function everything worked as expected. In real exploitation the chances that you're exploiting the parameters going into a main() is highly unlikely. It's more realistic to be running in a different function's scope.

RoraΖ
  • 12,317
  • 4
  • 51
  • 83
5

Your example does segfault here (or cause the program termination if the stack protector is active).

Maybe you should try to move the buffer overflow into another function. Perhaps your gcc version is converting return 0; from main into exit(0);.

Ángel
  • 17,578
  • 3
  • 25
  • 60
  • 2
    Moving the `strcpy` to a separate function worked. Any idea why it works in a separate function but not in `main`? It was not the case that gcc converted the `ret` to a call to `exit` but that is an interesting idea. – B-MO Sep 15 '15 at 09:48
  • 1
    @MikeJones It doesn't work in a separate function either. It's just that C programs are not *required* to crash if there is a problem. The could behave normally for the time being, but then later you encounter a unexplainable problem (or nothing at all) – deviantfan Sep 15 '15 at 17:58
  • Note shellcode which is often as well used afterwards(as exploit) will also depend on the endianess - Little or Big endian; intel for example - and I believe AMD - to some extent; is little endian. which means you have to put in the `shellcode` backwards. NOTE: this is not really A answer; it is more like an "to add to (..)" answer – William Martens Jul 09 '22 at 09:19
0

just in case someone gets the error

/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory
   27 | #include <bits/libc-header-start.h>

while trying with the -m32 flag,

Try installing gcc-multilib and, (if in c++ as well) g++-multilib:

sudo apt update

sudo apt install gcc-multilib

sudo apt install g++-multilib