Toy Model of a Buffer Overflow.

Example.

So what can we do with a buffer overflow?  In Hacking The Art of Exploitation we get a nice toy model of exploiting a buffer overflow.  We set up a program to check a password entered as a command line argument.  The program is deliberately vulnerable to a buffer overflow and will allow us to bypass the authentication by using an available buffer overflow.

The Code.

//A program to demonstrate a stack oveflow causing a password bypass.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_authentication(char *password) {
    int auth_flag = 0;                          //Set initial flag 0
    char password_buffer[16];                   //Buffer to store password.     

    strcpy(password_buffer, password);          //copy the password arg to buffer.
                                                //Note that there is nothing controlling length of password.
    if (strcmp(password_buffer, "brillig") == 0)//compare the buffer and set the flag appropriately.
        auth_flag = 1;
    if (strcmp(password_buffer, "outgrabe") == 0)
        auth_flag = 1;

    return auth_flag;                           //return the int value of auth flag.
}

int main(int argc, char *argv[]) {
    if(argc < 2) {                                  //If there are no cmd line args show usage statement.
        printf("Usage: %s \n", argv[0]);
        exit(0);
    }

    if(check_authentication(argv[1])) {             //if check_authentication is true grant access.
        printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");  //In this case if the value is > 0 it's true.
        printf("      Access Granted.\n");
        printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
    } else {
        printf("\nAccess Denied.\n");
    }
}

The program is a straightforward example of comparing strings and setting a flag based on the comparison.  Again we see the use of the strcpy command in the program.  The strcpy usage is a vulnerability as we have already discussed.

Assembly.

After the first few programs we examined in assembly you may be asking if its necessary to continue the practice.  I believe it is for several reasons.  If you want to learn to write exploits and hack operating systems assembly becomes important.  We aren’t learning to write a full assembly program by reading the instructions here but we are getting used to assembly syntax and how it corresponds to the program in execution.  Every time I examine the assembly dump I’m looking at what is happening in the original program trying to match up the assembly structures to the structures in the program.  We will need assembly later on in our career as os hackers.

Lets take a look at the disassembled code in gdb for main and check_authentication.

(gdb) disass main
Dump of assembler code for function main:
   0x0804851d <+0>:     lea    ecx,[esp+0x4]
   0x08048521 <+4>:     and    esp,0xfffffff0
   0x08048524 <+7>:     push   DWORD PTR [ecx-0x4]
   0x08048527 <+10>:    push   ebp
   0x08048528 <+11>:    mov    ebp,esp
   0x0804852a <+13>:    push   ecx
   0x0804852b <+14>:    sub    esp,0x4
   0x0804852e <+17>:    mov    eax,ecx
   0x08048530 <+19>:    cmp    DWORD PTR [eax],0x1
   0x08048533 <+22>:    jg     0x8048555 <main+56>
   0x08048535 <+24>:    mov    eax,DWORD PTR [eax+0x4]
   0x08048538 <+27>:    mov    eax,DWORD PTR [eax]
   0x0804853a <+29>:    sub    esp,0x8
   0x0804853d <+32>:    push   eax
   0x0804853e <+33>:    push   0x8048661
   0x08048543 <+38>:    call   0x8048360 <printf@plt>
   0x08048548 <+43>:    add    esp,0x10
   0x0804854b <+46>:    sub    esp,0xc
   0x0804854e <+49>:    push   0x0
   0x08048550 <+51>:    call   0x80483a0 <exit@plt>
   0x08048555 <+56>:    mov    eax,DWORD PTR [eax+0x4]
   0x08048558 <+59>:    add    eax,0x4
   0x0804855b <+62>:    mov    eax,DWORD PTR [eax]
   0x0804855d <+64>:    sub    esp,0xc
   0x08048560 <+67>:    push   eax
   0x08048561 <+68>:    call   0x80484bb 
   0x08048566 <+73>:    add    esp,0x10
   0x08048569 <+76>:    test   eax,eax
---Type  to continue, or q  to quit---
   0x0804856b <+78>:    je     0x804859f <main+130>
   0x0804856d <+80>:    sub    esp,0xc
   0x08048570 <+83>:    push   0x8048677
   0x08048575 <+88>:    call   0x8048380 <puts@plt>
   0x0804857a <+93>:    add    esp,0x10
   0x0804857d <+96>:    sub    esp,0xc
   0x08048580 <+99>:    push   0x8048694
   0x08048585 <+104>:   call   0x8048380 <puts@plt>
   0x0804858a <+109>:   add    esp,0x10
   0x0804858d <+112>:   sub    esp,0xc
   0x08048590 <+115>:   push   0x80486aa
   0x08048595 <+120>:   call   0x8048380 <puts@plt>
   0x0804859a <+125>:   add    esp,0x10
   0x0804859d <+128>:   jmp    0x80485af <main+146>
   0x0804859f <+130>:   sub    esp,0xc
   0x080485a2 <+133>:   push   0x80486c6
   0x080485a7 <+138>:   call   0x8048380 <puts@plt>
   0x080485ac <+143>:   add    esp,0x10
   0x080485af <+146>:   mov    ecx,DWORD PTR [ebp-0x4]
   0x080485b2 <+149>:   leave  
   0x080485b3 <+150>:   lea    esp,[ecx-0x4]
   0x080485b6 <+153>:   ret    
End of assembler dump.

In this code we have some more assembly instructions that we haven’t seen before.  The fist instruction is the and instruction.  The and instruction is the logical bitwise and operator which compares the value of the esp register and 0xfffffff0.  The instruction rounds the esp register address down by a multiple of 16.  If you need a refresher on the logical and operator check out the wiki page here.  The next new assembly instruction is cmp which is the compare instruction.  The cmp instruction at <+19> is comparing the DWORD PTR <eax> with 0x1.  The following instruction jg tells the program to jump to the stated instruction if the comparison resulted in a greater than condition.  The test instruction at <+76> is another comparison that will execute the jump on the next line if the eax register is zero.    The last instruction that jumps out to me here is the jmp instruction which jumps to the given argument location instruction.

Next lets take a look at the check_authentication function.

(gdb) disass check_authentication
Dump of assembler code for function check_authentication:
   0x080484bb <+0>:     push   ebp
   0x080484bc <+1>:     mov    ebp,esp
   0x080484be <+3>:     sub    esp,0x28
   0x080484c1 <+6>:     mov    DWORD PTR [ebp-0xc],0x0
   0x080484c8 <+13>:    sub    esp,0x8
   0x080484cb <+16>:    push   DWORD PTR [ebp+0x8]
   0x080484ce <+19>:    lea    eax,[ebp-0x1c]
   0x080484d1 <+22>:    push   eax
   0x080484d2 <+23>:    call   0x8048370 <strcpy@plt>
   0x080484d7 <+28>:    add    esp,0x10
   0x080484da <+31>:    sub    esp,0x8
   0x080484dd <+34>:    push   0x8048650
   0x080484e2 <+39>:    lea    eax,[ebp-0x1c]
   0x080484e5 <+42>:    push   eax
   0x080484e6 <+43>:    call   0x8048350 <strcmp@plt>
   0x080484eb <+48>:    add    esp,0x10
   0x080484ee <+51>:    test   eax,eax
   0x080484f0 <+53>:    jne    0x80484f9 <check_authentication+62>
   0x080484f2 <+55>:    mov    DWORD PTR [ebp-0xc],0x1
   0x080484f9 <+62>:    sub    esp,0x8
   0x080484fc <+65>:    push   0x8048658
   0x08048501 <+70>:    lea    eax,[ebp-0x1c]
   0x08048504 <+73>:    push   eax
   0x08048505 <+74>:    call   0x8048350 <strcmp@plt>
   0x0804850a <+79>:    add    esp,0x10
   0x0804850d <+82>:    test   eax,eax
   0x0804850f <+84>:    jne    0x8048518 <check_authentication+93>
   0x08048511 <+86>:    mov    DWORD PTR [ebp-0xc],0x1
---Type  to continue, or q  to quit---
   0x08048518 <+93>:    mov    eax,DWORD PTR [ebp-0xc]
   0x0804851b <+96>:    leave  
   0x0804851c <+97>:    ret    
End of assembler dump.

In this function we have only one new assembly instruction.  The jne instruction executes if the z flag set by test is not set, which means that the eax register is not zero.

What’s Going On.

Lets think about the execution of this program for a minute.  What will cause the execution to do something unintended?  We are passing a value, the password, to a function that checks if it matches our predetermined values.

In C the strcmp function compares two strings.  It returns 0 if the two strings are equal.  In the case where they are equal we are changing the auth_flag to 1.  Otherwise we don’t do anything.  That is the only purpose of check_authentication.  The check_authentication is combined with an if statement in such a way that it is only checking to see if it is nonzero.  If check_authentication is not 0 then we get the access granted message.

If we want to exploit this program we need to figure out a way to get check_authentication to be nonzero without the proper password.  How can we do that?  We have an unchecked strcpy from the command line argument into a buffer.  To overflow we just need to figure out how far away the buffer and the auth_flag variables are.

Something also jumps out at me here.  We have declared all our variables in the check_authentication function.  That means that they will reside in the same stack frame allowing us to overflow them more easily.  That is we aren’t trying to figure out how to jump frames to overwrite something.  Which is beyond what we’ve discussed so far.

Lets take a look at where everything falls.

Breakpoint 1 at 0x80484c8: file auth_overflow.c, line 9.
(gdb) run 12345678
Starting program: /auth_overflow 12345678         
                                                                                
Breakpoint 1, check_authentication (password=0xbffff4ea "12345678")             
    at auth_overflow.c:10                                                       
10          strcpy(password_buffer, password);          //copy the password arg to buffer.                                                                      
(gdb) print &auth_flag                                                          
$1 = (int *) 0xbffff24c
(gdb) print &password_buffer
$2 = (char (*)[16]) 0xbffff23c

Here we have a break point right before we copy the password passed into the buffer.  We see as expected from our declaration that there are 16 bytes separating the password_buffer and auth_flag.  Which tells us that passing greater than 16 bytes to the password should overflow the password_buffer into auth_flag and get our nonzero value.

Lets run our program with an input to overflow the buffer.

(gdb) run "This will overflow the password_buffer"
Starting program: /auth_overflow "This will overflow the password_buffer"

Breakpoint 1, check_authentication (
    password=0xbffff4cc "This will overflow the password_buffer")
    at auth_overflow.c:10
10          strcpy(password_buffer, password);          //copy the password arg to buffer.
(gdb) cont
Continuing.

Breakpoint 2, check_authentication (
    password=0xbf007265 )
    at auth_overflow.c:17
17          return auth_flag;                           //return the int value of auth flag.
(gdb) print $password_buffer
$3 = void
(gdb) print password_buffer
$4 = "This will overfl"
(gdb) print auth_flag
$5 = 1948284783

As we can see our input overwrote the auth_flag. But we didn’t get the result we were looking for. Instead we got:

(gdb) cont
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x66667562 in ?? ()

What happened? We went too far and crashed the program rather than taking control. We needed to not overflow the auth_flag. By inputing 17 bytes as an argument we should be where we want to be.

(gdb) run 12345678901234567
Starting program: /auth_overflow 12345678901234567

Breakpoint 1, check_authentication (password=0xbffff4e1 "12345678901234567")
    at auth_overflow.c:10
10          strcpy(password_buffer, password);          //copy the password arg to buffer.
(gdb) cont
Continuing.

Breakpoint 2, check_authentication (password=0xbffff4e1 "12345678901234567")
    at auth_overflow.c:17
17          return auth_flag;                           //return the int value of auth flag.
(gdb) cont
Continuing.

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
[Inferior 1 (process 2785) exited with code 034]

Which is a nice example of why it pays to know what you are trying to overflow and where it is.

Modifying the Program.

The easiest way to prevent the buffer overflow in this program is to use strncpy instead of strcpy which will add the number of characters to be copied.  This will prevent more than 16 bytes from being copied into password_buffer.

Here’s the difference in the two:

<     strcpy(password_buffer, password);          //copy the password arg to buffer. 
--- >     strncpy(password_buffer, password,16);    //copy the password arg to buffer.

Which results in

exploit@32:$ ./auth_modified 12345678901234567

Access Denied.

So we can’t exploit the program in the same way.

Next Time:

Now that we have explored the stack a little bit we are going to move to some shellcode.  Next time I’ll explain what shellcode is and we will move towards examining an exploit using it.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s