In programming, a buffer is an area in the computer’s memory that usually holds data temporarily. On many systems, these areas are stored one after another in the memory.
A buffer overflow is an anomaly where a program overruns the boundaries of such a buffer with fixed length while writing to it. When that happens, adjacent memory locations can get overwritten which can lead to undefined and potentially dangerous behavior.
The Consequences of Buffer Overflow
When a buffer with fixed length overflows, the data, stored in adjacent memory blocks, gets overwritten. The consequences of this range from a simple segmentation fault, which will cause the program to stop, to more severe problems, like a hijacked system where an attacker can gain full access to the computer.
Buffer Overflow Attack Example
The following example demonstrates what a vulnerable program looks like.
Overwriting Other Variables
The following example demonstrates what a vulnerable program looks like. In the application, two buffers, a and b, are created. Both have a fixed size of five characters. Because these buffers represent strings, the last character is a null-byte and the buffer’s effective size is, therefore, four bytes:
The program then prompts the user to enter a string. When the user hits the enter key, the entered string gets written to a and the contents of both buffers get printed to the console:
As you can see, when a user enters four characters (1, 2, 3, 4 in this case), the program behaves as it should. However, if more than five characters get entered, this is what happens:
Buffer a now seems to be six characters long while b seems to contain only one character. However, what they seem to contain is not what they actually contain. Both buffers are still four characters long, but the value of b — or at least the first two bytes — got overwritten:
As you can see, the string-terminator is missing in a. Instead, it got stored in b after the first character. More importantly, buffer b was overwritten because buffer a overflowed. Furthermore, the application now behaves unpredictably.
Overwriting Other Values
While the first example is already pretty dangerous, it gets even worse when other values of a program’s stack (for example, the return address) get overwritten. This will allow an attacker to hijack the program and call other functions of the program.
If an attacker can inject their executable code and make the return address point to it, they may even be able to hijack the entire system and gain administrator privileges.
How to Prevent Buffer Overflows
While programming languages like C and C++ don’t offer built-in protection for this vulnerability, many compilers and modern operating systems do. Usually, this built-in protection prevents an attacker from overwriting variable values and addresses on the stack.
Those mechanisms, however, won’t protect your code from crashing when a user enters a malformed input string. An effective and simple countermeasure is to always check input values. Furthermore, avoid using functions like strcpy. Instead, use their length-limited version (for example strncpy). The vulnerability described in the example above can be fixed by using fgets instead of scanf:
Pay close attention when checking for boundaries and make sure you stop at the correct length. As shown above, strings need a terminator and you can, therefore, not utilize the entire buffer.
Buffer Overflows are Dangerous but Avoidable
I hope this introduction gave you an idea of what buffer overflows are and why they are dangerous. Numerous types of attacks specifically target this vulnerability, as do the countermeasures that try to prevent them. As with most cybersecurity vulnerabilities, awareness and vigilance are your best tools!