Saturday, October 20, 2007

An Overview Of Buffer Overflows / Buffer Overruns

Buffer Overflows (Buffer Overrun):

A buffer overrun condition occurs when a process tries to copy more data into a buffer than the buffer intended to hold. Buffer overruns can occur on the stack memory or on the heap memory. In buffer overflow attacks, the extra data may contain codes designed to trigger specific actions that could, damage the user's files, corrupt or overwrite the valid data, or disclose confidential information.

Buffer overflows are not easy to discover and even when one is discovered, it is generally extremely difficult to exploit. Buffer overflows found in widely used server products are likely to become widely known and can pose a significant risk to users of these products. It is very hard to discover these flaws in custom code of the application and risk is significantly moderate as the source code and detailed error messages for the application are normally not available to the attacker to perform further exploits other than to crash the application.

Attackers use buffer overflows to corrupt the execution stack of a web application. By sending specifically crafted input to a web application, an attacker can cause the web application to execute arbitrary code of their choice. With this attack an attacker can perform the following actions but not limited to:
  • Creating an unauthorized user or administrator accounts
  • Creating unprotected entry points into a system (“back-doors”)
  • Disabling protective devices such as firewalls or antivirus solutions
  • Running arbitrary code instead of legitimate code

Background:

In a classic buffer overflow exploit, the attacker sends data to a program that having this vulnerability, which it stores in undersized stack overwriting information on the call stack, including the function's return pointer. The attacker data sets the value of the return pointer in such a way that points back to the buffer that holds arbitrary code of the attacker, so that when the function returns, it transfers control to malicious code contained in the attacker's data. There are a variety of other types of buffer overflow, including Heap buffer overflow and Off-by-one Error among others. Another very similar class of flaws is known as Format string attack. All these conditions occur due to code not checking to see if the buffer being copied into has been allocated enough space for data being copied.

The application or component uses an unmanaged language, native code or some language that is not deemed “memory safe” for implementation is vulnerable to buffer overflows when user input is blindly copied into buffer structures without being validated first for length and type, opportunities. Managed languages such as C# and Java are generally not susceptible to buffer overrun conditions. Managed code makes buffer overruns extremely difficult to encounter however not impossible. For example, managed code can call into unmanaged code and overruns can occur there.

In Detail:

A buffer is a contiguous allocated chunk of memory. In many unmanaged languages, there are no automatic bounds checking on the buffer, which means a user can write past a buffer. Like, see the following example:

int main()
{
int stackbuff[20];
stackbuff[25] = 5;
}

The above program is a valid and doesn’t produce any errors. In the above the program writes data beyond the allocated memory for the buffer, you are allocated only 20 blocks of memory to the variable “stackbuff” of type integer but writing the value of 5 at 25th block that is beyond the allocation, which might result in unexpected behavior.

How the attacker exploits buffer overflows?

A Stack is a contiguous chunk of memory and a register called the stack pointer (SP) points to the top of the stack, the bottom of the stack is at a fixed address. Its size is dynamically adjusted at runtime. Whenever a function call is made, the following are pushed on to the stack in the specified sequence. First the function parameters, then the address to be executed after the function returns, then a frame pointer (FP), followed by local variables of the function. All these variables are cleaned up from the stack as the function terminates.

For example, see the following example code:

int sumofvalues(int x, int y, int z)
{
int sum=0;
sum = x+y+z;
return sum;
}

Whenever the above function is executed the stack will be like below:



First three function parameters x, y, z will be pushed onto the stack, then return address, then frame pointer, followed by local variable sum.

Suppose, your application is having a function looks like below:

void funccpy(char *source)
{
char destination[20];
strcpy(destination, source);
}

Later, this function is get called by passing string of characters that are to be copied into another string, well works fine if the passed characters are having length less than 20 characters. But, what happens if the passed value is having more number of characters than 20? The extra bytes run past the buffer allocated for “destination” variable causing overwrites the space allocated for the FP, return address and so on. Using this vulnerability an attacker can easily execute code of his choice by overwriting the return address. For example, attacker is able to place the arbitrary code to execute in the buffer's overflowing area and then overwrite the return address in such a way it points back to the buffer and executes the intended code. Such arbitrary code can be inserted into the program by using input parameters.

Recommendations/ Counter measures:

  • For each instance where user input is copied or concatenated into a buffer, perform input validation on the input for size prior to the buffer copy. If the user input exceeds the allocated space of the destination buffer, do not perform the copy and return with an error.
  • Source code and binaries should be scanned with source code analysis and binary analysis tools respectively to detect common buffer overrun conditions.
  • The choice of programming language can have a great effect on the occurrence of buffer overflows. Many programming languages provide runtime bounds checking which might send a warning or raise an exception when it would overwrite data.
  • Try to avoid usage of unmanaged code, if necessary, consider usage of secure functions instead of unsecure functions like strcpy(), strcat(), ...
  • Keep systems with most up to date security patches.
Add to Technorati Favorites

2 comments:

Anonymous said...

Amiable post and this fill someone in on helped me alot in my college assignement. Thank you for your information.

Anonymous said...

Understandably your article helped me altogether much in my college assignment. Hats high to you enter, intention look progressive in behalf of more cognate articles soon as its anecdote of my favourite subject-matter to read.