In this buffer overflow tutorial, we will discuss the basics of the
following :
We’ll keep the explanation and examples simple enough for you to
understand the concept completely. We’ll also use C programming
language to explain the buffer overflow concept.
What is Buffer Overflow?
A buffer, in terms of a program in execution, can be thought of as
a region of computer’s main memory that has certain boundaries in
context with the program variable that references this memory.
For example :
char buff[10]
In the above example, ‘buff’ represents an array of 10 bytes
where buff[0] is the left boundary and buff[9] is the right boundary
of the buffer.
Lets take another example :
int arr[10]
In the above example, ‘arr’ represents an array of 10 integers.
Now assuming that the size of integer is 4 bytes, the total buffer
size of ‘arr’ is 10*4 = 40 bytes. Similar to the first example,
arr[0] refers to the left boundary while arr[9] refers to the right
boundary.
By now it should be clear what a buffer means. Moving on lets
understand when a buffer overflows.
A buffer is said to be overflown when the data (meant to be
written into memory buffer) gets written past the left or the right
boundary of the buffer. This way the data gets written to a portion
of memory which does not belong to the program variable that
references the buffer.
Here is an example :
char buff[10];
buff[10] = 'a';
In the above example, we declared an array of size 10 bytes. Please
note that index 0 to index 9 can used to refer these 10 bytes of
buffer. But, in the next line, we index 10 was used to store the
value ‘a’. This is the point where buffer overrun happens because
data gets written beyond the right boundary of the buffer.
It is also important for you to understand how GCC compilation
process works to create a C executable.
Why are buffer overflows harmful?
Some of us may think that though a buffer overflow is a bad
programming practice but so is an unused variable on stack, then why
there is so much hullabaloo around it? What is the harm buffer
overrun can cause to the application?
Well, if in one line we have to summarize the answer to these
questions then it would be :
Buffer overflows, if undetected, can cause your program
to crash or produce unexpected results.
Lets understand a couple of scenarios which justify the answer
mentioned above.
1. Consider a scenario where you have allocated
10 bytes on heap memory:
char *ptr = (char*) malloc(10);
Now, if you try to do something like this :
ptr[10] = 'c';
Then this may lead to crash in most of the cases. The reason being, a
pointer is not allowed to access heap memory that does not belong to
it.
2. Consider another scenario where you try to
fill a buffer (on stack) beyond it’s capacity :
char buff[10] = {0};
strcpy(buff, "This String Will Overflow the Buffer");
As you can see that the strcpy() function will write the complete
string in the array ‘buff’ but as the size of ‘buff’ is less
than the size of string so the data will get written past the right
boundary of array ‘buff’. Now, depending on the compiler you are
using, chances are high that this will get unnoticed during
compilation and would not crash during execution. The simple reason
being that stack memory belongs to program so any buffer overflow in
this memory could get unnoticed.
So in these kind of scenarios, buffer over flow quietly corrupts
the neighbouring memory and if the corrupted memory is being used by
the program then it can cause unexpected results.
You also need to understand how you can prevent stack smashing
attacks with GCC.
Buffer Overflow Attacks
Until now we discussed about what buffer overflows can do to your
programs. We learned how a program could crash or give unexpected
results due to buffer overflows. Horrifying isn’t it ? But, that it
is not the worst part.
It gets worse when an attacker comes to know about a buffer over
flow in your program and he/she exploits it. Confused? Consider this
example :
#include <stdio.h>
#include <string.h>
int main(void)
{
char buff[15];
int pass = 0;
printf("\n Enter the password : \n");
gets(buff);
if(strcmp(buff, "manish"))
{
printf ("\n Wrong Password \n");
}
else
{
printf ("\n Correct Password \n");
pass = 1;
}
if(pass)
{
/* Now Give root or admin rights to user*/
printf ("\n Root privileges given to the user \n");
}
return 0;
}
The program above simulates scenario where a program expects a
password from user and if the password is correct then it grants root
privileges to the user.
Let’s the run the program with correct password ie
‘manish’ :
$ ./bfrovrflw
Enter the password :
manish
Correct Password
Root privileges given to the user
This works as expected. The passwords match and root privileges are
given.
But do you know that there is a possibility of buffer overflow in
this program. The gets() function does not check the array bounds and
can even write string of length greater than the size of the buffer
to which the string is written. Now, can you even imagine what can an
attacker do with this kind of a loophole?
Here is an example :
$ ./bfrovrflw
Enter the password :
hhhhhhhhhhhhhhhhhhhh
Wrong Password
Root privileges given to the user
In the above example, even after entering a wrong password, the
program worked as if you gave the correct password.
There is a logic behind the output above. What attacker did was,
he/she supplied an input of length greater than what buffer can hold
and at a particular length of input the buffer overflow so took place
that it overwrote the memory of integer ‘pass’. So despite of a
wrong password, the value of ‘pass’ became non zero and hence
root privileges were granted to an attacker.
There are several other advanced techniques (like code injection
and execution) through which buffer over flow attacks can be done but
it is always important to first know about the basics of buffer, it’s
overflow and why it is harmful.
To avoid buffer overflow attacks, the general advice that is given
to programmers is to follow good programming practices. For example: