What Is Buffer Overflow?
What Is Buffer Overflow?
The software error focuses on buffers, which are sequential sections of computing
memory that hold data temporarily as it is transferred between locations. Also known as
a buffer overrun, buffer overflow occurs when the amount of data in the buffer exceeds
its storage capacity. That extra data overflows into adjacent memory locations and
corrupts or overwrites the data in those locations.
The buffer overflow exploit techniques a hacker uses depends on the architecture and
operating system being used by their target. However, the extra data they issue to a
program will likely contain malicious code that enables the attacker to trigger additional
actions and send new instructions to the application.
For example, introducing additional code into a program could send it new instructions
that give the attacker access to the organization’s IT systems. In the event that an
attacker knows a program’s memory layout, they may be able to intentionally input data
that cannot be stored by the buffer. This will enable them to overwrite memory locations
that store executable code and replace it with malicious code that allows them to take
control of the program.
Attackers use a buffer overflow to corrupt a web application’s execution stack, execute
arbitrary code, and take over a machine. Flaws in buffer overflows can exist in both
application servers and web servers, especially web applications that use libraries like
graphics libraries. Buffer overflows can also exist in custom web application codes. This
is more likely because they are given less scrutiny by security teams but are less likely
to be discovered by hackers and more difficult to exploit.
1. System crashes: A buffer overflow attack will typically lead to the system crashing. It
may also result in a lack of availability and programs being put into an infinite loop.
2. Access control loss: A buffer overflow attack will often involve the use of arbitrary code,
which is often outside the scope of programs’ security policies.
3. Further security issues: When a buffer overflow attack results in arbitrary code
execution, the attacker may use it to exploit other vulnerabilities and subvert other
security services.
1. Stack-based buffer overflows: This is the most common form of buffer overflow attack.
The stack-based approach occurs when an attacker sends data containing malicious
code to an application, which stores the data in a stack buffer. This overwrites the data
on the stack, including its return pointer, which hands control of transfers to the attacker.
2. Heap-based buffer overflows: A heap-based attack is more difficult to carry out than the
stack-based approach. It involves the attack flooding a program’s memory space beyond
the memory it uses for current runtime operations.
3. Format string attack: A format string exploit takes place when an application processes
input data as a command or does not validate input data effectively. This enables the
attacker to execute code, read data in the stack, or cause segmentation faults in the
application. This could trigger new actions that threaten the security and stability of the
system.
This programming language is not the only one vulnerable to buffer overflow attacks. A
buffer overflow program in Assembly, C, C++ or Fortran is also particularly vulnerable
and more likely to enable attackers to compromise a system. However, applications
written in JavaScript or Perl are typically less vulnerable to buffer overflow attacks.
How to Prevent Buffer Overflows
Application developers can prevent buffer overflows by building security measures into
their development code, using programming languages that include built-in protection,
and regularly testing code to detect and fix errors.
One of the most common methods for preventing buffer overflows is avoiding standard
library functions that have not been bounds-checked, which includes gets, scanf, and
strcpy. Another common method is to prevent buffer overruns by using bounds-checking
that is enforced at runtime. This automatically checks that the data written to a buffer is
within the appropriate boundaries.
Modern operating systems now deploy runtime protection that enables additional
security against buffer overflows. This includes common protection like:
1. Address space layout randomization (ASLR): Buffer overflow attacks typically need to
know where executable code is located. ASLR moves at random around locations of
data regions to randomize address spaces, which makes overflow attacks almost
impossible.
2. Data execution prevention: This method prevents an attack from being able to run
code in non-executable regions by flagging areas of memory as executable or non-
executable.
3. Structured exception handling overwrite protection (SEHOP): Attackers may look to
overwrite the structured exception handling (SEH), which is a built-in system that
manages hardware and software exceptions. They do this through a stack-based
overflow attack to overwrite the exception registration record, which is stored on the
program’s stack. SEHOP prevents attackers’ malicious code from being able to attack
the SEH and use its overwrite exploitation technique.
Implementing security measures around development code and operating systems is not
enough to protect organizations’ systems. When a buffer overflow vulnerability is
discovered, it is crucial to quickly patch the software and ensure it is made available to
all users.
Buffer Overflow Attack Examples
A common buffer overflow example is when an attacker injects their malicious code into
corrupted memory. Or they may simply take advantage of the buffer overflow and the
adjacent memory corruption.
For example, a simple buffer overflow can be caused when code that relies on external
data receives a ‘gets()’ function to read data in a stack buffer. The system cannot limit
the data that is read by the function, which makes code safety reliant on users entering
fewer than ‘BUFSIZE’ characters. This code could look like this:
“...
char buf[BUFSIZE];
gets(buf);
…”
Other buffer overflow attacks rely on user input to control behavior then add indirection
through the memory function ‘memcpy()’. This accepts the destination buffer, source
buffer, and amount of bytes to copy, fills the input buffer with the ‘read()’ command, and
specifies how many bites for ‘memcpy()’ to copy.
“...
char buf[64], in[MAX_SIZE];
printf("Enter buffer contents:\n");
read(0, in, MAX_SIZE-1);
printf("Bytes to copy:\n");
scanf("%d", &bytes);
memcpy(buf, in, bytes);
…”
Another scenario for buffer overflow is when data properties are not verified locally. The
function ‘lccopy()’ takes a string and returns a heap-allocated copy with uppercase
letters changed to lowercase. The function does not perform bounds-checking as it
expects ‘str’ to be smaller than ‘BUFSIZE’. An attacker can bypass the code or change
the assumption of the size to overflow the buffer. An example of this code is:
strcpy(buf, str);
for (p = buf; *p; p++) {
if (isupper(*p)) {
*p = tolower(*p);
}
}
return strdup(buf);
}”
Another example of buffer overflow is when code is too complex to predict its behavior.
The below example is from the libPNG image decoder, which is used by browsers like
Mozilla and Internet Explorer. The code appears safe as it checks the variable-length
size but performs a ‘png_ptr->mode’ check that makes it more complicated. This can
result in blind length checks in the ‘png_crc_read()’ call, which shows the importance of
minimizing the complexity of code in memory operations.