9

I am working on io-wargames for fun right now, level3:

I do understand why there is a stack-overflow in this code (strlen(argv[1])), but what I don't understand is why it overflows the function pointer functionpointer.

functionpointer is declared before char buffer[50]; on the stack so How comes it overwrites it ???

Here is the main vulnerable code:

int main(int argc, char **argv, char **envp)
{
        void (*functionpointer)(void) = bad;
        char buffer[50];

        if(argc != 2 || strlen(argv[1]) < 4)
                return 0;

        memcpy(buffer, argv[1], strlen(argv[1]));
        memset(buffer, 0, strlen(argv[1]) - 4);

        printf("This is exciting we're going to %p\n", functionpointer);
        functionpointer();

        return 0;
}

Here is the shell exploits the stackoverflow:

level3@io:~$ /levels/level03 11111111
This is exciting we're going to 0x80484a4
I'm so sorry, you're at 0x80484a4 and you want to be at 0x8048474
level3@io:~$ /levels/level03 111111111111111111111111111111111111111111111111111111111111111111111111111111111
This is exciting we're going to 0x31313100
Segmentation fault

Here is the objump -d of the executable:

080484c8 <main>:
 80484c8:       55                      push   %ebp
 80484c9:       89 e5                   mov    %esp,%ebp
 80484cb:       83 ec 78                sub    $0x78,%esp
 80484ce:       83 e4 f0                and    $0xfffffff0,%esp
 80484d1:       b8 00 00 00 00          mov    $0x0,%eax
 80484d6:       29 c4                   sub    %eax,%esp
 80484d8:       c7 45 f4 a4 84 04 08    movl   $0x80484a4,-0xc(%ebp)
 80484df:       83 7d 08 02             cmpl   $0x2,0x8(%ebp)
 80484e3:       75 17                   jne    80484fc <main+0x34>
 80484e5:       8b 45 0c                mov    0xc(%ebp),%eax
 80484e8:       83 c0 04                add    $0x4,%eax
 80484eb:       8b 00                   mov    (%eax),%eax
 80484ed:       89 04 24                mov    %eax,(%esp)
 80484f0:       e8 a7 fe ff ff          call   804839c <strlen@plt>
 80484f5:       83 f8 03                cmp    $0x3,%eax
 80484f8:       76 02                   jbe    80484fc <main+0x34>
 80484fa:       eb 09                   jmp    8048505 <main+0x3d>
 80484fc:       c7 45 a4 00 00 00 00    movl   $0x0,-0x5c(%ebp)
 8048503:       eb 74                   jmp    8048579 <main+0xb1>
 8048505:       8b 45 0c                mov    0xc(%ebp),%eax
 8048508:       83 c0 04                add    $0x4,%eax
 804850b:       8b 00                   mov    (%eax),%eax
 804850d:       89 04 24                mov    %eax,(%esp)
 8048510:       e8 87 fe ff ff          call   804839c <strlen@plt>
 8048515:       89 44 24 08             mov    %eax,0x8(%esp)
 8048519:       8b 45 0c                mov    0xc(%ebp),%eax
 804851c:       83 c0 04                add    $0x4,%eax
 804851f:       8b 00                   mov    (%eax),%eax
 8048521:       89 44 24 04             mov    %eax,0x4(%esp)
 8048525:       8d 45 a8                lea    -0x58(%ebp),%eax
 8048528:       89 04 24                mov    %eax,(%esp)
 804852b:       e8 5c fe ff ff          call   804838c <memcpy@plt>
 8048530:       8b 45 0c                mov    0xc(%ebp),%eax
 8048533:       83 c0 04                add    $0x4,%eax
 8048536:       8b 00                   mov    (%eax),%eax
 8048538:       89 04 24                mov    %eax,(%esp)
 804853b:       e8 5c fe ff ff          call   804839c <strlen@plt>
 8048540:       83 e8 04                sub    $0x4,%eax
 8048543:       89 44 24 08             mov    %eax,0x8(%esp)
 8048547:       c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp)
 804854e:       00 
 804854f:       8d 45 a8                lea    -0x58(%ebp),%eax
 8048552:       89 04 24                mov    %eax,(%esp)
 8048555:       e8 02 fe ff ff          call   804835c <memset@plt>
 804855a:       8b 45 f4                mov    -0xc(%ebp),%eax
 804855d:       89 44 24 04             mov    %eax,0x4(%esp)
 8048561:       c7 04 24 c0 86 04 08    movl   $0x80486c0,(%esp)
 8048568:       e8 3f fe ff ff          call   80483ac <printf@plt>
 804856d:       8b 45 f4                mov    -0xc(%ebp),%eax
 8048570:       ff d0                   call   *%eax
 8048572:       c7 45 a4 00 00 00 00    movl   $0x0,-0x5c(%ebp)
 8048579:       8b 45 a4                mov    -0x5c(%ebp),%eax
 804857c:       c9                      leave  
 804857d:       c3                      ret    
 804857e:       90                      nop
 804857f:       90                      nop

I see that the complier reserved in the main's prolog function frame 0x78 bytes for the local main function variables.

migrated from stackoverflow.com Jun 8 '13 at 20:22

This question came from our site for professional and enthusiast programmers.

  • this is a major spoiler for people playing the game, could you consider removing the actual exploit and adding spoiler tags where appropriate ? – 0xea Jun 9 '13 at 20:53
  • @0xea I removed the explicit exploit. thanks – 0x90 Jun 9 '13 at 21:32
8

The compiler did put the function pointer after the buffer.

In the disassembly, check the memcpy call:

8048525:  lea    -0x58(%ebp),%eax
8048528:  mov    %eax,(%esp)
804852b:  call   804838c <memcpy@plt>

The first argument to memcpy (the buffer's address) is at [esp+0] and you can see that the value of ebp-0x58 is being put there.

Next is the function call at the end of the function:

804856d:  mov    -0xc(%ebp),%eax
8048570:  call   *%eax

You can see that the address being jumped to is loaded from [ebp-0xc] which is 0x4c (76) bytes after the beginning of the character buffer.

Here's a stack layout from IDA which will hopefully make things clearer:

-00000058 buffer          db 76 dup(?)
-0000000C functionpointer dd ?
-00000008 var_8           dd ?
-00000004 var_4           dd ?
+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)
+00000008 argc            dd ?
+0000000C argv            dd ?

Offsets in the leftmost column are ebp-relative. Memory addresses increase downwards, so it's obvious that writing too much data into buffer will overwrite the function pointer (and then the return address).

MSVC actually uses a mitigation against such attack - it reorders character buffers to be placed after all other variables:

enter image description here

  • 1
    How IDA came with the functionpointer and buffer in its output ? – 0x90 Jun 8 '13 at 21:45
  • 2
    @0x90: I renamed them, obviously :) Though it might happen automatically if DWARF info is present (since 6.4). – Igor Skochinsky Jun 8 '13 at 22:02
11

functionpointer is declared before char buffer[50]; on the stack so How comes it overwrites it ???

The order of objects in the stack is implementation defined. C does not mention any stack and the direction of the stack growing is also implementation-defined (usually it grows downwards but in some systems it grows upwards).

In your case functionpointer is probably put first and then buffer. As in your system the stack grows downards, this allow you to overwrite functionpointer when overflowing buffer.

4

Why does the function pointer get overwritten even though is declared before the vulnerable buffer?

In the vulnerable code the order of declaration is:

void (*functionpointer)(void) = bad;  
char buffer[50];

The assembly code shows us that the function pointer variable is located at ebp-0xc and the buffer at ebp-0x58.

This proves that the stack is growing downwards(to lower addresses) in this system as the buffer is placed at a lower address than the function pointer variable.

Another proof that the stack is growing downwards in this system is the below instruction which allocates the required space by substracting esp:

80484cb:       83 ec 78                sub    $0x78,%esp


Now memcpy copies num bytes starting from the byte located at ebp-0x58 and then it continues by incrementing.

Adding 1 to ebp-0x58 makes it ebp-0x57, so if num is long enough, memcpy will overwrite the function pointer located at ebp-0xc.

ebp holds an address, lets say 0x00400000, so ebp-0x58 is the address 0x003FFFA8 incrementing from that address you will eventually reach ebp-0xc(0x003FFFF4).

0x003FFFA8     buffer
...
0x003FFFF4     function pointer
0x003FFFF8     
0x003FFFFC    
0x00400000     saved ebp
0x00400004     saved return address

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.