cperlstackbuffer-overflowbuffer-overrun

Experimenting with buffer overflow


I recently took a security class in which we briefly touched on buffer overflow. I wasn't satisfied with what we covered, so I looked for a few examples to follow along with and try myself and found Buffer Overflow Attack

I like this example as it is easy to follow and understand why everything works. I tried to follow along, but in a Debian virtual machine instead of Windows.

This is the C code from the site:

#pragma check_stack(off)

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

void foo(const char* input)
{
    char buf[10];

    printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n% p\n\n");

    strcpy(buf, input);
    printf("%s\n", buf);

    printf("Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n\n");
}

void bar(void)
{
    printf("Augh! I've been hacked!\n");
}

int main(int argc, char* argv[])
{
    //Blatant cheating to make life easier on myself
    printf("Address of foo = %p\n", foo);
    printf("Address of bar = %p\n", bar);

    if (argc != 2) 
    {
        printf("Please supply a string as an argument!\n");
        return -1;
    } 

    foo(argv[1]);
    return 0;
}

The code "cheats" by giving the addresses of the two functions foo and bar. The ultimate goal is to get bar to run using only buffer overflow. To do this, they gave a short Perl script:

$arg = "ABCDEFGHIJKLMNOP"."\x50\x10\x40";
$cmd = "StackOverrun ".$arg;
system($cmd);

Since I'm using Linux instead of Windows, and since the address of my bar function was slightly different, I made a couple of simple fixes:

$arg = "ABCDEFGHIJKLMNOP"."\xf7\x05\x40";
$cmd = "./prog ".$arg;
system($cmd);

I would think that it should work the same way as it did in their example; the Perl script is run and it gives the filler text to the program, followed by the new return address to run bar. But it doesn't work for me.

This is the output from running my Perl script:

Address of foo: 0x400596
Address of bar: 0x4005f7
The current stack:
0x7fffe6b4abd8
0x7faba670c7a0
0x1d
0x6
0x7faba63b099a
0x7fffe6b4ad00

ABCDEFGHIJKLMNOPP�
Stack after input:
0x7ffc31998568
0x7f9a7c6ed7a0
0x7f9a7c421e50
0xf70550504f4e4d4c
0x7f9a7c39199a
0x7ffc31998690

In my output, the only address that appears to hold any of the filler text is the third from the last address, the one immediately before the return address.

I suspect the issue comes from using gcc to compile my program, but I'm not sure what exactly is causing it. The issue may also be Debian. Here's how I compiled the program:

gcc -z execstack -fno-stack-protector prog.c -o prog

My hope was that compiling without the stack protector would allow me to follow the example without issues.

Any help would be great, I'm completely stuck. Realistically I could simply switch to Windows, but at this point I really want to know why it won't work and how to fix it.


Solution

  • Alright so I'm going to answer my own question here, just in case anyone who views it in the future is curious.

    Essentially the problem stemmed from not printing enough memory addresses to get a clear picture of the stack. If you followed along with the link in the question you'd see that printing 6 memory addresses of the stack was enough for their system, but it wasn't enough for ours. My friend proposed changing the source code from this:

    printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n% p\n\n");
    
    strcpy(buf, input);
    printf("%s\n", buf);
    
    printf("Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n\n");
    

    to this:

    printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n\n");
    
    strcpy(buf, input);
    
    printf("Buffer: %s\n", buf);
    printf("Address of Buffer: %p\n\n", buf);
    printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n\n");
    

    This change does two things for us. First, it increases the amount of addresses printed to 13. Second, it prints the address that the buffer starts at. The second part is important as it gives us a relative value to look for in the stack addresses given. Example:

    overflow@OVERFLOW:~/Overflow$ ./prog ZZZZZZ
    Address of foo = 0x400596
    Address of bar = 0x400601
    Current Stack:
    0x7fffffe6
    0x7f30b7f8a7a0
    0x19
    0x6
    0x7f30b7c2e99a
    0x4007c8
    0x7ffddab72653
    0x7f30b7f9cde0
    0x7f30b81b01a8
    0x7ffddab71250
    0x400672
    0x7ffddab71338
    0x200000000
    
    Buffer: ZZZZZZ
    Address of Buffer: 0x7ffddab71220
    
    Stack after Input:
    0x7fffffde
    0x7f30b7f8a7a0
    0x21
    0xc
    0x7f30b7c2e99a
    0x4007c8
    0x7ffddab72653
    0x5a5a5a5a5a5a
    0x7f30b81b01a8
    0x7ffddab71250
    0x400672
    0x7ffddab71338
    0x200000000
    

    In this example we can see Address of Buffer: 0x7ffddab71220. If we look through the stack addresses below it, we find one very similar: 0x7ffddab72653. We can think of this as a starting point for the buffer, so that the following few addresses will be the storage containers of the buffer. In fact, in this example I printed "ZZZZZZ" to the buffer, and you can see the address immediately following our starting point has changed to 0x5a5a5a5a5a5a which, you may have guessed, is "ZZZZZZ" in hex.

    Great, so now we know where the buffer actually starts, but we don't know which is the return address. If we look at the addresses of the functions:

    Address of foo = 0x400596 and Address of bar = 0x400601 We can find a similar value somewhere below the starting point of our buffer, in this case: 0x400672.

    At this point we know all we need to: which memory addresses store the buffer, the address of the function we want to call, and most importantly the return address we want to overwrite. At that point it is a matter of experimenting with the perl script, adding characters to the buffer until we get the desired result.