Buffer Overflows

   

Another common means of attacking a system or service is by way of a buffer overflow. Buffer overflows are caused by errors in the programming of system service and can be exploited in any number of ways, from gaining elevated privileges to executing arbitrary code, depending on the service that is to be exploited.

A buffer overflow is nothing more than a matter of loose or nonexistent bounds checking on a buffer. Say what? What does that mean? Don't worry, we're getting there. The C programming language, which is used to build almost all of Mac OS X, requires developers to keep very close tabs on variables and the amount of memory they require. A C string, for example, must be allocated to contain the number of characters in the string, plus one null terminator. For example, the string "John Ray" requires 9 bytes of storage space, as shown in Figure 9.3.

Figure 9.3. C strings require storage for one byte per ASCII character + 1 null terminator.

graphics/09fig03.gif

Although this might seem straightforward enough, the C language includes a number of functions such as strcmp() , strcpy () , and gets() that do not attempt to verify that the string on which they are operating is the appropriate length before executing. Consider the following program ( overflow.c ) which reads and prints a user 's name :

 int main() {   char name[9];   printf ("Enter your name: ");   gets(name);   printf ("Your name is %s\n", name); } 

Compile ( gcc -o overflow overflow.c ) and then execute the program:

 %  ./overflow  warning: this program uses gets(), which is unsafe. Enter your name:  John Ray  Your name is John Ray % 

For an execution with a string that fits within the 9-byte buffer, the program acts exactly as one would hope. Change the length of input and things are nearly as peachy:

 %  ./overflow  warning: this program uses gets(), which is unsafe. Enter your name:  The Noble and Powerful Ezekial Martograthy Bartholomew III  Your name is The Noble and Powerful Ezekial Martograthy Bartholomew III Segmentation fault 

Now, instead of executing cleanly, the program crashes with a segmentation fault. What has happened is that the input data, which should have been contained within nine bytes, has overwritten part of the executable in memory, causing it to crash.

Although crashing is certainly a common effect of buffer overflows, they can sometimes be exploited with far more dramatic effects. A good example of a buffer overflow exploit is demonstrated in Listing 9.1 ( buffer.c ), written by Mark E. Donaldson and provided here with a few tweaks for Mac OS X.

Listing 9.1 Source Code for buffer.c
 1:  int main() 2:  { 3:    char name[8]; 4:    char real_passwd[8]="test"; 5:    char password[8]; 6: 7:    // retrieve the user information 8:    printf ("Enter your name: "); 9:    gets(name); 10:   printf("Enter your password: "); 11:   gets(password); 12:   printf("Your name and password are %s and %s.\n",name,password); 13:   printf("The real password for %s is %s.\n",name,real_passwd); 14: 15:   // Authenticate password against real_passwd 16:   authenticate(password,real_passwd); 17:   return 0; 18: } 19: 20: void authenticate (char* string1, char* string2) { 21:   char buffer1[8]; 22:   char buffer2[8]; 23:   strcpy (buffer1,string1); 24:   strcpy (buffer2,string2); 25: 26:   if (strcmp(buffer1,buffer2)==0) printf("Access allowed!\n"); 27: } 

The program logic is simple. Lines 3-5 allocate storage for name , real_password (the password we're going to test for, hard coded as test ), and password (what the user will type in). Lines 8-13 input the name and password and print what the program thinks it has received from the user. Line 16 calls the authentication routine (lines 20-27), which checks password against real_password and prints Access allowed! if they match.

Again, compile ( gcc -o buffer buffer.c ) and execute the program, using valid (seven or fewer characters) input. First, the program is run with an invalid (wrong) but properly sized username and password:

 %  ./buffer  warning: this program uses gets(), which is unsafe. Enter your name:  jray  Enter your password:  frog  Your name and password are jray and frog. The real password for jray is test. 

The output is exactly as expected: the passwords do not match, so the access message is not displayed. Next, run the program using the correct password ( test ):

 %  ./buffer  warning: this program uses gets(), which is unsafe. Enter your name:  jray  Enter your password:  test  Your name and password are jray and test. The real password for jray is test. Access allowed! 

Again, the output is precisely what one would expect: the passwords match and access is allowed. Now look at what happens when the invalid lengths are used. This time, instead of a valid username, enter 123456789ABCDEF (you'll see why we chose these values shortly) and test for the password:

 %  ./buffer  warning: this program uses gets(), which is unsafe. Enter your name:  123456789ABCDEF  Enter your password:  test  Your name and password are 123456789ABCDEF and test. The real password for 123456789ABCDEF is 9ABCDEF. 

Did you notice that even though test was used as the password, the program didn't correctly authenticate the user? In addition (and more importantly), did you notice that the program is now claiming that the REAL password is 9ABCDEF ? This provides very useful information about the program and its flaw. First, you can tell that the input for the user's name ( name ) has obviously overwritten the real password ( real_passwd ) in memory. Because the real password starts with the 9ABCDEF input, it can be inferred that the buffer size for name is eight bytes long, and that the name and real_passwd data structures are located sequentially in memory (just as they are defined on lines 3 and 4 of the source code).

So, how can this newfound knowledge be exploited? Simple: By overflowing the name buffer, you can set the password to any value you want, and subsequently authenticate against it:

 %  ./buffer  warning: this program uses gets(), which is unsafe. Enter your name:  jrayXXXXHack  Enter your password:  Hack  Your name and password are jrayXXXXHack and Hack. The real password for jrayXXXXHack is Hack. Access allowed! 

Obviously, no real-world program should be written like this, and developers should take the time to perform bounds checking on their code if it is not handled automatically by the compiler. The goal of most attackers it to gain elevated permissions or execute arbitrary code on a server. To do this, they must understand the code they are attacking and the underlying operating system. Just because a piece of code suffers from a buffer overflow does not mean it can be exploited beyond a simple crash. A complete play-by-play of a buffer overflow is documented in Mark Donaldson's "Inside the Buffer Overflow Attack: Mechanism, Method, and Prevention," http://www.sans.org/rr/code/inside_buffer.php.

Protection

Buffer overflows are caused by errors in the software you are running, not from a lapse in your abilities as an administrator. Protecting against a buffer overflow, like DoS attacks, is more a matter of vigilance than active prevention. Keep the following points in mind:

  • If developing, use modern languages with build-in bounds checking such as Java, Ruby, and Perl. C programmers should avoid the standard C string functions or, at the very least, perform bounds checking ( sizeof() ) before and after string operations.

  • Be aware of buffer overrun errors that have been reported for your system, servers, and libraries and take measures to patch or limit access to the affected functions (that is, keep up to date!).

  • Identify SUID applications. SUID tools are the most likely target for buffer overflows, because they execute with root permissions and may provide root access in the event of an overflow.

  • Run intrusion detection software. It is very unlikely that if you suffer a buffer overflow attack it will be launched by the person who discovered or wrote the exploit. It is very likely a script- kiddie running a piece of code picked up from an IRC chat room and that has a recognizable attack signature.


   
Top


Mac OS X Maximum Security
Maximum Mac OS X Security
ISBN: 0672323818
EAN: 2147483647
Year: 2003
Pages: 158

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