Information Security
exploit buffer-overflow exploit-development
Updated Fri, 20 May 2022 09:15:01 GMT

Cannot overwrite EIP in basic exploitation example


I'm trying to replicate a simple buffer overflow for which I have the following code (strcpy_ex.c):

#include <string.h>
int main( int argc, char** argv ) {
        char buffer[500];
        strcpy(buffer, argv[1]);
        return 0; 
}

which I compile by using:

gcc -m32 -z execstack strcpy_ex.c -fno-stack-protector -o strcpy

The goal is to achieve a shell by exploiting the buffer overflow flaw when filling the buffer with more than 500 characters.

From the literature I've read, I expected to overwrite the EIP in order to change the execution flow and make it point to a memory address where my shellcode resides. In order to achieve this, I attempted to fill the buffer with "A's" followed by "B's" to locate where the overwriting takes place:

(gdb) r $(python -c "print 'A'*500+'B'*500")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/Examples/Buffer Overflow/strcpy $(python -c "print 'A'*500+'B'*500")
Program received signal SIGSEGV, Segmentation fault.
0x565555ec in main ()

Unfortunately, it seems I have not managed to overwrite EIP. I expected to find a value 0x42424242 in EIP, but I did not.

(gdb) i r eip esp
eip            0x565555ec   0x565555ec <main+76>
esp            0x4242423e   0x4242423e

Additional information about the ASM instructions:

(gdb) disas main
Dump of assembler code for function main:
   0x565555a0 <+0>: lea    0x4(%esp),%ecx
   0x565555a4 <+4>: and    $0xfffffff0,%esp
   0x565555a7 <+7>: pushl  -0x4(%ecx)
   0x565555aa <+10>:    push   %ebp
   0x565555ab <+11>:    mov    %esp,%ebp
   0x565555ad <+13>:    push   %ebx
   0x565555ae <+14>:    push   %ecx
   0x565555af <+15>:    sub    $0x200,%esp
   0x565555b5 <+21>:    call   0x565555ed <__x86.get_pc_thunk.ax>
   0x565555ba <+26>:    add    $0x1a46,%eax
   0x565555bf <+31>:    mov    %ecx,%edx
   0x565555c1 <+33>:    mov    0x4(%edx),%edx
   0x565555c4 <+36>:    add    $0x4,%edx
   0x565555c7 <+39>:    mov    (%edx),%edx
   0x565555c9 <+41>:    sub    $0x8,%esp
   0x565555cc <+44>:    push   %edx
   0x565555cd <+45>:    lea    -0x1fc(%ebp),%edx
   0x565555d3 <+51>:    push   %edx
   0x565555d4 <+52>:    mov    %eax,%ebx
   0x565555d6 <+54>:    call   0x56555400 <strcpy@plt>
   0x565555db <+59>:    add    $0x10,%esp
   0x565555de <+62>:    mov    $0x0,%eax
   0x565555e3 <+67>:    lea    -0x8(%ebp),%esp
   0x565555e6 <+70>:    pop    %ecx
   0x565555e7 <+71>:    pop    %ebx
   0x565555e8 <+72>:    pop    %ebp
   0x565555e9 <+73>:    lea    -0x4(%ecx),%esp
=> 0x565555ec <+76>:    ret    

Due to the simplicity of the code, shouldn't EIP be overwritten immediatly after the buffer is filled?

Even if in this situation it should not make any difference, I would like to clarify that ASLR is off.

What am I missing?


Regarding platform:

# uname -a
Linux kali 4.9.0-kali4-amd64 #1 SMP Debian 4.9.30-2kali1 (2017-06-22) x86_64 GNU/Linux



Solution

I may be missing something really here, but the easiest way for exploiting this, is that the strcpy() call is in another function. This way, you overwrite the stored return address, essentially giving you control to EIP.

Your main function doesn't really return into a space that you control.

Make the code something like:

#include <string.h>
void vuln(char *arg) {
    char buffer[500];
    strcpy(buffer, arg);
}  
int main( int argc, char** argv ) {
    vuln(argv[1]);
    return 0; 
}

The overflow will happen when vuln( ) is finishes and fetches the stored (but overwritten) return address to drop you back in main( ).

That should get you started.





Comments (5)

  • +0 – Thank you very much, I'm going to try this. Just have 1 more question: I had assumed that calling strcpy implies that arguments and the return address for the next instruction in main() are stored in the stack, as strcpy is an external function. Is my understanding wrong? Shouldn't it work in the same way as your example? — Jul 28, 2017 at 19:57  
  • +0 – It's too much to type here, I'll update the answer. — Jul 28, 2017 at 20:39  
  • +0 – Just to clarify for OP on what ndrix has stated, you can see the references clarified at the following two urls: phearless.org/istorija/razno/buffer-overflow-example.txt AND why main return0 is something you aren't controlling: stackoverflow.com/questions/35471878/… — Jul 29, 2017 at 05:51  
  • +0 – It worked for me. I was able to plant shellcode into the buffer and then overwrite eip to point to the shellcode within the buffer and execute shell. Hence, I disagree with @ndrix's answer. main() is still a function who's ebp and eip can be overflowed and thereby, control flow can be hijacked. — Mar 31, 2020 at 02:46  
  • +0 – I said easiest, not impossible. But feel free to share your code. — Mar 31, 2020 at 18:11