Buffer Overflows: Difference between revisions

From Elvis Wiki
mNo edit summary
(Improve spelling)
 
(79 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== Definition ==
[[File:Buffer-overflow.png|500px|thumb|right|Buffer Overflow examle <ref name="BOB">What is a Buffer Overflow Attack, [Online]. Available: https://www.wallarm.com/what/buffer-overflow-attack-definition-types-use-by-hackers-part-1. Accessed: Dec. 11, 2024</ref>]]
 
== Introduction ==
Buffer overflow is a security vulnerability that should not be underestimated, as it has been the most common type of vulnerability in the last decade. This type of attack is an essential part of all security attacks, as buffer overflow vulnerabilities are widespread and easy to exploit. Especially in the field of cyber attacks, they are exploited by users to gain access to vulnerable servers and control them.
Buffer overflow is a security vulnerability that should not be underestimated, as it has been the most common type of vulnerability in the last decade. This type of attack is an essential part of all security attacks, as buffer overflow vulnerabilities are widespread and easy to exploit. Especially in the field of cyber attacks, they are exploited by users to gain access to vulnerable servers and control them.


== Functionality ==
== Definitions ==
A buffer overflow is essentially about how the system reacts to errors when executing software. If a program expects information from the user to be processed and a buffer of static size is created and the data that was entered is not checked for length, it would result in a buffer overflow. An example is shown below.
 
=== Buffer ===
 
A buffer is defined as a '''limited''', contiguously allocated set of memory. The most common buffer in C is an array. <ref name=”RE1”>C. Anley, The Shellcoder’s Handbook: Discovering and Exploiting Security Holes, 2nd ed., Indianapolis: Wiley, 2007</ref>
 
=== Buffer Overflow ===
 
Buffer Overflows are possible because in the C and C++ languages, there exists no inherent bounds-checking to ensure that data being copied into a buffer will not be larger than what the buffer was initialized to hold. Consequently, if the person writing the program has not explicitly coded the program to check for oversize input, it is possible for data to fill a buffer, and if that data is large enough, to continue to write past the end of the buffer. <ref name=”RE1”/>
 
== Common Weakness Enumeration (CWE) ==
 
The importance of addressing buffer overflow vulnerabilities can be seen by examining Mitre’s respective parent category: <ref name=”MIT23a”> MITRE, "CVE Search Results for 'buffer overflow'," [Online]. Available: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=buffer+overflow. Accessed: Oct. 16, 2024. </ref>
 
* CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer
 
There is a large variety of direct and indirect child categories that further help create a taxonomy of the issues at hand. Some of those are listed here:
<ref name="MIT23b">MITRE, "CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer," [Online]. Available: https://cwe.mitre.org/data/definitions/119.html. Accessed: Oct. 16, 2024.</ref>
<ref name="MIT23c">MITRE, "CWE-788: Access of Memory Location After End of Buffer," [Online]. Available: https://cwe.mitre.org/data/definitions/788.html. Accessed: Oct. 16, 2024.</ref>
 
* CWE-788: Access of Memory Location After End of Buffer
* CWE-787: Out-of-bounds Write
* CWE-786: Access of Memory Location Before Start of Buffer
* CWE-125: Out-of-bounds Read
* CWE-120: Buffer Copy Without Checking Size of Input (’Classic Buffer Over- flow’)
* CWE-121: Stack-based Buffer Overflow
* CWE-122: Heap-based Buffer Overflow
* CWE-126: Buffer Over-read
 
== History ==
In the past, there were often a series of events executed with the help of buffer overflows. In 1980, the term Internet worm was a very well-known and sensitive topic because a worm was malicious software that reproduced itself and spread through network connections. It was called a Morris worm because on November 2, 1988, an event occurred that changed the way people thought about networks and about the Internet. On that day, tens of thousands of computers quickly and simultaneously became infected with a self-replicating computer program. Back then, computer science student Robert T. Morris created a computer worm that exploited an unsafe function, which at the time was very commonly used and distributed. Through the practical application of a buffer overflow, the computer worm spread itself around at an alarming rate and, going back, nearly shut down the entire internet. This situation resulted in Morris being the first person convicted under the Computer Fraud and Abuse Act, demonstrating further how dangerous buffer overflows can be. To this day, this attack is perhaps one of the most significant events in the history of computing. With buffer overflows, it was also possible to bypass various security measures. For example, buffer overflows could be used to remove software restrictions from firmware or to bypass copy protection. This was the case with the Android and iOS operating systems, where it was possible to remove various locks and modify the smartphone according to one's own wishes. For example, it was possible to install apps that were not available in the store elsewhere, change the boot animation, access hidden system files, remove manufacturer-specific apps, remove network locks, and much more. One keyword is "jailbreaking" for Apple devices and "rooting" for Android devices. On Nintendo's game console, a game called Pokemon Yellow could be changed from the inside by manipulating the program using shellcode.
 
While many of these events already reside in the past, buffer overflows do not. The exploit reoccurs frequently, so much so that in 2023 they still secured themselves a spot on the Common Weakness Enumeration/SANS list of the Top 25 Most Dangerous Software Errors. Whether through the adaptation of the exploit's mechanism or simply by focusing on a new set of targets, buffer overflows stay relevant. That's why, when dealing with cybersecurity of any sort, there is no way past them.
 
== Technical Background and Context ==
 
=== Memory Layout of a Process ===
 
Informally, a process is a '''program in execution'''. The status of the current activity of a process is represented by the value of the '''program counter/instruction pointer''' and the content of the processor’s '''registers'''. Different parts of the program are stored in different memory segments, as shown in the figure below.
 
[[File:Memory structure.png|center]][https://www.cs.ucf.edu/~czou/CDA6938-06/Buffer%20Overflows.pdf#page=8]
 
* Stack section: temporary data storage when invoking functions (such as function parameters, return addresses, and local variables)
* Heap section: memory that is dynamically allocated during program run time
* Data section: global variables (initialized and uninitialized)
* Code/Text section: the executable, machine-readable bytecode
 
=== Readable, Writeable, Executable Memory ===
 
Memory regions have different rights with respect to the process they belong to. For instance, the text section will usually be marked read-only, and any attempt to write to it will result in a '''Segmentation Fault'''.
<ref name="One1996">A. One, "Smashing the Stack for Fun and Profit," Phrack, vol. 7, no. 49, Nov. 1996.</ref>
All other sections have to be writable in order for the process to work properly. <ref name=”RE1”/> Whether a section is '''executable''' will largely depend on the applied settings during the compilation of the program and the platform on which it is executed.
 
=== The Stack and Important Control Structures ===
 
The stack’s primary purpose is to implement and help with the use of functions. A function call alters the flow of execution through a program. However, when its task is completed, a '''function returns control''' to the statement or instruction following the function call. Furthermore, the stack is used to allocate memory for the local variables used in the functions and to return values from the function. <ref name="One1996"/>
 
During a function call, arguments are pushed onto the stack in reverse order, and the '''return address''' (the instruction pointer EIP) is saved to enable returning to the caller after function execution. Key stack-related registers are:
 
* EIP (Instruction Pointer): Stores the next instruction's address.
* ESP (Stack Pointer): Points to the top of the stack.
* EBP (Base Pointer): Marks the base of the stack frame for the current function.
 
When a function is called, the stack is manipulated using '''PUSH''' and '''POP''' instructions. A '''return address''' is pushed onto the stack, allowing the program to return to the calling function. Buffer overflows can occur if a function does not properly limit the size of data written to buffers, potentially '''overwriting the return address'''. An attacker may '''overwrite the return pointer to redirect the flow of execution'''.


[[File:Example.jpg|center]][https://www.hackingarticles.in/a-beginners-guide-to-buffer-overflow/]
== Causes ==


In a buffer, data is stored temporarily while it is transferred from one location to another. If it is assumed that 8 bytes of data are allocated and the user enters data that would be more than 8 bytes, it would result in a buffer overflow because 2 bytes more than allowed were entered. The corresponding source code example is shown below.
=== C/C++: Vulnerable Functions ===
C is the most affected programming language when it comes to creating buffer overflows, closely followed by C++.
#include <string.h>
<ref name="Howard2009">M. Howard, D. LeBlanc, and J. Viega, *24 Deadly Sins of Software Security: Programming Flaws and How to Fix Them*, 1st ed. USA: McGraw-Hill, Inc., 2009.</ref>
When writing C code, the programmer is responsible for '''data integrity'''. If this responsibility were shifted over to the compiler, the resulting binaries would be significantly slower and less efficient. Furthermore, C’s simplicity increases the programmer’s control. However, this can result in programs that are vulnerable to buffer overflows and memory leaks if the programmer isn’t careful.
using namesprace std;
<ref name="Erickson2008">J. Erickson, *Hacking: The Art of Exploitation*, 2nd ed. San Francisco: No Starch Press, 2008.</ref>
 
int main()  
Some '''vulnerable functions''' from the C/C++ standard are listed here, each with their respective, saver counterpart:
{
{|class="wikitable"
    char buffer [8];
! Vulnerable || Safer
    cout << "Input data: ";
|-
    cin >> buffer;
| strcpy() || strncpy()
|-
    return 0;
| gets() || fgets()
}
|-
| strcat() || strncat()
|-
| memcpy() || memmove()
|-
| scanf() || sscanf()
|-
| memset() || -
|}


A variable of the data type ''char'' is used, which creates an array named ''buffer'' that can store 8 bytes of data.
=== IoT/Embedded Devices ===
"The constrained resources of embedded systems leads to the predominant use of the C language. This, along with the tight processing requirements, leave associated devices open to BOF based attacks." <ref name=””> G. Mullen and L. Meany, "Assessment of Buffer Overflow Based Attacks On an IoT Operating System," 2019 Global IoT Summit (GIoTS), Aarhus, Denmark, 2019, pp. 1-6, doi: 10.1109/GIOTS.2019.8766434.
</ref>


== Practical Guide ==
== Dangers ==


In the following example, a piece of code is shown, which lets the user manipulate the output of the program enabled through a buffer vulnerability. Make sure, to have the GCC compiler installed on your Windows machine.  
Arguably, the most significant consequence of a buffer overflow is when the attacker is able to execute their own malicious shellcode. Which is also referred to as arbitrary code execution. This way it is completely up to the attacker itself what they will do with the same privileges as the program on the level of the current user. Create a reverse shell to execute remote code, stealing data or even manipulating all settings of the system. By getting elevated privileges, the attacker would also be able to run code as an administrator or root. Another potential consequence is a Denial of Service (DoS) attack. Instead of executing code, the goal is to cause unpredictable behavior or crash the system by overwriting as much data as possible until the program is unable to handle it. The downtime of a program can be costly for large organizations. Data corruption, system instability, and information disclosure, are all potential consequences that can be caused by a buffer overflow. Consequently, it is up to the creativity of the attacker once a vulnerable code is identified.


#include <stdio.h>
Summed up, these four are the main dangers:


int main(int argc, char **argv)
* Data Corruption
{
* Program Crashes
char jayce[4]="Oum";
* Exploitation: Control Flow Alteration
char herc[8]="Gillian";
* Exploitation (via Shellcode Injection): Arbitrary Code Execution
strcpy(herc, "BrookFlora");


printf("%s\n", jayce);
== Types of Buffer Overflow Vulnerabilities ==


return 0;
The following section will introduce descriptions and simple examples related to the most relevant categories of buffer overflow vulnerabilities. The distinction between Buffer Over-Read and Buffer Over-Write provides a basic categorization of the issues at hand. The respective sections will include simple, illustrative examples. The sections on Stack Buffer Overflow and Heap Buffer Overflow will present more complex concepts, introducing the mechanics and control structures of each memory region and how they could potentially be exploited. Note that Over-Read and Over-Write vulnerabilities can occur in both sections of process memory.
}


To run the program, open the command prompt in the directory of the source file. Compile the file with the following command:
=== Buffer Over-Read ===


$gcc example.c
The following is a simple example of a Out-of-bounds read operation, that can be performed by a C program:


Subsequently, execute the compiled file to display its output:
<pre>


$./example.out
#include <stdio.h>
ra
#include <string.h>
$


The intention of the program is supposed to be the output of the string "BrookFlora". As the following illustration shows, two buffers are stored back to back in the stack. Since ten characters are copied into the buffer which is only eight bytes long, the operation causes an overflow into the first buffer.
int main(){


|\0| m| u| O|    |\0|\0| a| r|
int array[5] = {1 ,2 ,3 ,4 ,5};
|\0|\0| a| i|    | o| l| F| k|
printf("%d\n", array[5]);
| l| l| i| G|    | o| o| r| B|


The first diagram shows the initial stack organization. The second diagram shows the overflowed memory layout.
}
</pre>


== History ==
This program illustrates a common error that new programmers might run into when learning about arrays: Referencing array[5] will eventually attempt to access its sixth element, although the array was only initialized to hold five elements. When this program is run, it will read beyond the bounds of the array and might produce unexpected results.
In the past, there were often a series of events executed with the help of buffer overflows. In 1980, the term Internet worm was a very well-known and sensitive topic because a worm was malicious software that reproduced itself and spread through network connections. It was called a Morris worm because on November 2, 1988, an event occurred that changed the way people thought about networks and about the Internet. On that day, tens of thousands of computers quickly and simultaneously became infected with a self-replicating computer program. Back then, computer science student Robert T. Morris created a computer worm that exploited an unsafe function, which at the time was very commonly used and distributed. Through the practical application of a buffer overflow, the computer worm spread itself around at an alarming rate and, going back, nearly shut down the entire internet. This situation resulted in Morris being the first person convicted under the Computer Fraud and Abuse Act, demonstrating further how dangerous buffer overflows can be. To this day, this attack is perhaps one of the most significant events in the history of computing. With buffer overflows, it was also possible to bypass various security measures. For example, buffer overflows could be used to remove software restrictions from firmware or to bypass copy protection. This was the case with the Android and iOS operating systems, where it was possible to remove various locks and modify the smartphone according to one's own wishes. For example, it was possible to install apps that were not available in the store elsewhere, change the boot animation, access hidden system files, remove manufacturer-specific apps, remove network locks, and much more. One keyword is "jailbreaking" for Apple devices and "rooting" for Android devices. On Nintendo's game console, a game called Pokemon Yellow could be changed from the inside by manipulating the program using shellcode.


While many of these events already reside in the past, buffer overflows do not. The exploit reoccurs frequently, so much so that in 2023 they still secured themselves a spot on the Common Weakness Enumeration/SANS list of
=== Buffer Over-Write ===
the Top 25 Most Dangerous Software Errors. Whether through the adaptation of the exploit's mechanism or simply by focusing on a new set of targets, buffer overflows stay relevant. That's why, when dealing with cybersecurity of any sort, there is no way past them.
In this section, a simple Out-of-bounds write vulnerability will be examined. Although this particular example showcases a stack-based buffer overflow, we will use it to introduce the general concept of a over-write vulnerability and delve into specifically exploiting the stack's mechanics and control structures in the later section "Stack-based Buffer Overflow".  


== Affected Programming Languages ==
The following program deliberately uses the insecure strcpy() function to copy a large string into a buffer, which is actually too small for the string. The following listing displays the complete source code.
Buffer overflows most commonly, if not exclusively, appear in the programming languages C and C++. The reason for that is that they are closely hardware-related programming languages, meaning that out-of-the-box, they do possess very little
in terms of safety mechanisms. Given said information, it would be even easier to exploit buffer overflows when using an assembler, as it is as hardware-related as it gets for programming languages. Whereas more abstract and ”heavyweight”
Programming languages such as C# and Java already come with lots of safeguards.


== Memory structure ==
<pre>
In order to understand the principle of a buffer overflow-based attack, it is of great importance to look at the memory structure in detail. Binary data refers to files that can be executed and have different file formats. One of them is ''ELF'', which stands for ''Executable and Linking Format'' and is supported by UNIX. When such an executable binary file is loaded via the linker and the program is executed, the corresponding program code is loaded into the main memory and executed by the CPU. Different parts of the program, such as the program code, constants, and data, are stored in different memory segments. The memory structure is shown below.
#include <stdio.h>
#include <string.h>


[[File:Memory structure.png|center]][https://www.cs.ucf.edu/~czou/CDA6938-06/Buffer%20Overflows.pdf#page=8]
int main() {


=== Code ===
    char B[3] = "03"; // gets higher address on stack
The code segment contains byte patterns that can be read and understood by the CPU. This memory segment is read-only, so it can be executed and used by users at the same time. Therefore, this segment is not the target of a buffer overflow, since attempting to write to it would result in a ''Memory Access Violation Error'', which would mean that the program will close.
    char A[8] = "0000000"; // gets lower address on stack


=== Data and BSS ===
    printf("Before overflow: A = %s, B = %s\n", A, B);
The Data and BSS segments store global variables that can be accessed by any function. Since variables should not contain executable code, these memory segments are not executable and should be pointed to with a command pointer, which would mean that the program will close.


=== Stack ===
    strcpy(A, "excessive"); // dangerous
The stack segment stores local variables that are used in a function and can be accessed via the function's instructions. The variables are located in a memory segment, which is the stack and corresponds to a last-in-first-out (LIFO) method. This means that only access to the top element is granted, or to move the element to another location to be able to advance to the next element. Each function call creates a new stack frame for its data, which is removed once the function returns, with automatic cleanup after function completion. The stack is at the end of the memory accessible to the program and grows downward, so the top element is actually the element with the lowest address but is still called the top element. The CPU has a special register that keeps track of only the top of the stack, that is, whether the stack is growing or shrinking, and this register is called the stack pointer (SP).
    printf("After overflow: A = %s, B = %s\n", A, B);


=== Heap ===
    return 0;
The heap segment is a memory area where it is possible to request and release memory during the execution of a program. If a programmer would need 1000 bytes of memory, that would be allocated immediately with the function ''malloc()'' and returned to the system with the function ''free()''. This can be useful in situations where it can't predict how much memory it will end up using, since it depends on what is put into the program. However, this requires manual allocation and deallocation by the programmer. Compared to the stack, the heap starts from the lowest address and grows upwards.
}
</pre>


== Buffer overflow variations ==
Console output running the program (bash).
Three different types of buffer overflows are distinguished: stack overflow, heap overflow, and integer overflow. The stack overflow and heap overflow, also referred to as stack-based buffer overflow and heap-based buffer overflow, differ in where the buffer overflow exploit takes place, namely the stack or the heap in a system's memory.
<pre>
Before overflow: A = 0000000, B = 03
After overflow: A = excessive, B = e
</pre>


=== Stack Overflow ===
Inspection of memory before the overflow (gdb).  
A stack overflow is a type of program error that can cause a computer's buffer to overflow with too much data. This means that if a program wants to write something to an address outside its data structure, it will not end up in the buffer but will overwrite the memory location, which would then lead to an overflow. Such an attack exploits the fact that when a function is called, the CPU stores the address of the next instruction on the stack, and the compiler uses the frame pointer, which is also stored on the stack and is used to remember the beginning of the current stack area. Each called function then creates its own block of memory in the stack area, gradually filling the stack with the highest address. If a buffer is allocated to the stack and the input is copied to the stack by the user without verification, the attacker can expand the size of the input to overwrite the data behind it. This then leads to the possibility of the frame pointer pointing back to a memory address, more often than not into the buffer itself, containing malicious code.
The memory addresses on the left are the locations of the two buffers.
<pre>
0xffffcc85:  0x30  0x30  0x30  0x30  0x30  0x30  0x30  0x00
0xffffcc8d:  0x30  0x33  0x00
</pre>


=== Heap Overflow ===
Inspection of memory after the overflow (gdb).
Heap overflows work on a similar principle as stack overflows and differ in that they maintain persistence between function calls, among other things. This would mean that, as long as the memory area is not used later, the overflow is also not noticed. This is possible with the function ''malloc()'', since the memory can be allocated and remains allocated until one executes the function ''free()''.
<pre>
0xffffcc85:  0x65  0x78  0x63  0x65  0x73  0x73  0x69  0x76
0xffffcc8d:  0x65  0x00  0x00
</pre>


=== Integer Overflow ===
=== Integer Overflow ===
Integer overflows occur when an arithmetic operation attempts to generate a value that lies outside a range that can be represented with a specified number of bits. The most common result of an overflow is that the least significant representable bits of the result are stored. An overflow condition can lead to results that are equivalent to unintended behavior. In particular, if the possibility is not expected, an overflow can affect the reliability and safety of a program. A code example is shown below.
Integer overflows occur when an arithmetic operation attempts to generate a value that lies outside a range that can be represented with a specified number of bits. The most common result of an overflow is that the least significant representable bits of the result are stored. An overflow condition can lead to results that are equivalent to unintended behavior. In particular, if the possibility is not expected, an overflow can affect the reliability and safety of a program. A code example is shown below.
   
   
Line 112: Line 190:
The front one, the ninth bit, is no longer contained in the 8 bits of the data type ''unsigned char''. If only the last 8 bits were considered, the result would be 1 and not 257.
The front one, the ninth bit, is no longer contained in the 8 bits of the data type ''unsigned char''. If only the last 8 bits were considered, the result would be 1 and not 257.


=== 32-bit vs. 64-bit systems ===
The Ariane 5 disaster of 1996 illustrates what can happen when integer overflows are not handled properly in the real world. The rocket was destroyed 37 seconds after launch because of a severe software error, which was caused by an integer overflow. This shows how unresolved integer overflows can have disastrous consequences, particularly in safety-critical systems like aerospace control software. The incident could have been prevented if there had been more robust input validation and error handling procedures, particularly during the conversion process from floating-point to integer data types. <ref name=”AR1”>Innovative Bytes, Ariane-5 disaster - (integer overflow - space requirements), Medium, October 2022</ref>
Additionally, buffer overflows vary based on the system used. Usually, this vulnerability is more common in 32-bit systems compared to 64-bit systems. This is because 64-bit systems can address more memory, making them less vulnerable to buffer overflows.


== Use After Free Bug ==  
=== Stack-based Buffer Overflow ===
The Use After Free bug is a vulnerability where memory should not be used in this way while the program is running. If a program clears memory, but the pointer to that memory is not yet cleared, an attacker can use this bug to gain access and control.


== Countermeasures ==
If more data is written to a buffer than the size assigned to it, there is a chance it could overwrite the adjacent memory. This is called a stack overflow. Since the memory overwritten contains valuable information like the return address, this exploitation is a prime target. As an example, consider an input string that gets copied to the stack from a vulnerable function, that does not validate the size. Whatever was written to this string, as long as it is longer than the size of the buffer, will overwrite memory next to it. If an attacker creates a string that reaches the return address with a new address on purpose, the program will jump to this exact location after its execution. This type of attack is called "stack smashing". It is frequently used and is well-known in the context of buffer overflows. A common problem of stack smashing is how to guess the starting address of the own shellcode. Just by guessing or brute-forcing the exact return value, it would take the attacker a long time and an enormous number of attempts. This can be minimized by inserting a series of NOP instructions in front of the shellcode. A NOP instruction is a special operation which will push the execution one by one until it hits the malicious code. Adding as many NOP instructions as possible increases the chances of reaching the desired code. This means that as long as the return address points to any NOP instruction, the application will execute the shellcode and we do not have to directly hit the address where the code starts.


=== Canaries ===
Stack overflows which alter return addresses, are one of the most dangerous methods of gaining unauthorized access to a system. They allow an attacker to circumvent standard program execution and potentially gain complete control. <ref name="One1996"/>
Canaries can be employed in order to recognize whether a buffer overflow has occurred. Therefore, in the simplest cases, canaries are specific values in memory that are located right after the thereby protected buffer. If their values are changed at any point, it is safe to assume that a buffer overflow occurred and countermeasures (e.g., termination) can be taken. Typical types of canaries, which are supported by security hardening technologies like ProPolice or Stackguard (GCC), are terminator canaries, random canaries, and random XOR canaries.


=== Data Execution Prevention (DEP) ===
=== Heap-based Buffer Overflow ===
Data Execution Prevention (DEP) is a security feature originally developed by MicrosoftR© for Windows XP SP2. There are two basic variants: hardware-based DEP and software-based DEP. If supported by the CPU as well as the process, hardware DEP will be employed; otherwise, DEP has to be carried out in software, which is part of the Windows operating system. The basic functionality of DEP is to prevent applications from executing code in a non-executable area of memory.
A program may not know at compile time how much memory it will need. Such segments can be allocated dynamically, during runtime, and will be placed on the heap.
Special system calls brk() and mmap() can be used by Linux programs to achieve that.
The functions malloc(), calloc(), realloc() and free() provide convenient wrapper functions around these system calls and help manage those memory segments. <ref name=”RE1”/>
 
To be efficient, any malloc() implementation stores a
lot of meta-data about the location of the chunks, the size of the chunks, and perhaps
some special areas for small chunks. It also organizes this information. In dlmalloc, it is
organized into buckets, and in many other malloc implementations it is organized into
a balanced tree structure. This information is stored in two places: in global variables
used by the malloc() implementation itself, and in the memory block before and/or
after the allocated user space. Thus, the heap contains important information about
the state of memory stored directly after any user-allocated buffer. <ref name=”RE1”/>
 
=== Use-After-Free Vulnerability ===
Use-after-free (UAF) vulnerabilities are a specialized form of a memory management bug occurring when a program tries to use a memory object even though it is already deallocated. This vulnerability can happen, because the pointer to the corresponding memory location may remain accessible, despite the memory itself being marked as accessible for other allocations. If a program tries to access this now-freed memory, it could cause some unexpected behavior or even a security breach. Since an attacker could exploit this "dangling pointer" to gain unauthorized access to the system or leak information meant to be kept confidential. <ref name="UAF">Byoungyoung Lee, Chengyu Song, Yeongjin Jang, Tielei Wang, Taesoo Kim, Long Lu, and Wenke Lee. Preventing use-after-free with dangling pointers nullification. In Network and Distributed System Security Symposium, 2015</ref>


=== Address Space Layout Randomization (ASLR) ===
== Mitigation Techniques and How to Disable Them ==
Since code reuse attacks (e.g., ROP attacks) require the memory addresses of gadgets to be known to an attacker, techniques to randomize their entry points have become increasingly popular. ASLR randomizes the location of data, and code region layout randomization offers a plausible defensive strategy since code region layout randomization hinders code reuse in exploits and data randomization impedes the redirection of control flow by making it difficult to guess the location of injected code (partly paraphrased from the 2013 paper by Snow et al.).
This section will briefly introduce some mitigation techniques that can potentially prevent buffer overflow attacks. First, it will explain the basic concepts in a simple way, and second, it will provide brief guides on how to disable certain mitigation techniques for research purposes.


=== Standard Template Library (STL) ===
=== Adress Space Layout Randomisation (ASLR) ===
Using the Standard Template Library (STL) can significantly lower risks, as STL provides safer alternatives for string handling and vectors.
ASLR randomizes the memory address space layout of processes, making it harder to predict the location of specific functions or buffers.  <ref name=”RE1”/> Since code reuse attacks (e.g., ROP attacks) require the memory addresses of gadgets to be known to an attacker, techniques to randomize their entry points have become increasingly popular.


=== C++ Compiler ===
'''ASLR can be temporarily disabled''' at the system level (Linux):
Compiling C code with a C++ compiler can enhance safety due to the stricter functionality of C++ compilers.
<pre>
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
</pre>
Re-enable after testing:
<pre>
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
</pre>


=== High-level Programming Languages ===
=== Non-executable Stack (nx-stack) ===
Using higher-level languages such as Java and C#. They come with built-in features like bounds-checked arrays and native string types, which offer more security.
A non-executable stack, or nx-stack, prevents execution of code in the stack, when designated as non-executable, mitigating buffer overflow attacks that inject shellcode into that region. <ref name=”RE1”/>


=== Fuzz Testing ===
To '''disable nx-stack''' on a program, use the -z execstack option during compilation. This allows execution of code in the stack memory:
Fuzz testing involves testing applications with random and unexpected inputs to find potential vulnerabilities, including buffer overflows.
<pre>
gcc -z execstack -o vulnerable_program source.c
</pre>


=== Unsafe Functions ===
=== Data Execution Prevention (DEP) ===
Replacing unsafe writing instructions and functions, such as the most commonly used "strcpy," "strcat," and "sprintf,” with their safer counterparts can drastically decrease the likelihood of exploits.


== Summary ==
Data Execution Prevention (DEP) is a security feature originally developed by MicrosoftR© for Windows XP SP2. There are two basic variants: hardware-based DEP and software-based DEP. If supported by the CPU as well as the process, hardware DEP will be employed; otherwise, DEP has to be carried out in software, which is part of the Windows operating system. The basic functionality of DEP is to prevent applications from executing code in a non-executable area of memory.
Since the rise of C in the early 1970s, buffer overflows have become a serious security vulnerability. Even though high-level programming languages are typically not affected, the number of vulnerable systems is actually rising. At the same time, a wide array of countermeasures are also increasingly adopted and applied. Features like executable space protection (e.g., data execution prevention under Windows) have already been deployed since the mid-2000s, and on the compiler side, technologies like Stackguard support several detection and prevention mechanisms (e.g., different types of canaries). Furthermore, almost every widely used operation system supports Address Space Layout Randomization in order to minimize the attack surface for buffer overflow attacks. For example, at the beginning of 2020, most of the bigger operating systems (Linux, Windows, macOS, iOS, Android, Solaris, OpenBSD, etc.) will offer support for ASLR.


Another key point is the expansion of the Internet of Things (IoT). These widely distributed networks of hardware endpoints have deemed themselves the perfect target for buffer overflow attacks. This stems from the fact that IoT applications mostly utilize low-level, closely hardware-related languages such as C and C++, both of which are almost exclusively for buffer overflows.
=== Stack Canaries ===


== Courses ==
Stack canaries are small random values placed on the stack to detect and prevent buffer overflow attacks. If an overflow occurs and modifies the stack, the canary value will change, triggering a security alert or crash. <ref name=”RE1”/>
Typical types of canaries, which are supported by security hardening technologies like ProPolice or Stackguard (GCC), are terminator canaries, random canaries, and random XOR canaries.


* [https://moodle.fh-campuswien.ac.at/course/view.php?id=19428 Ausgewählte Kapitel der IT-Security ILV (2021, 2022)]
To '''disable stack canaries''' when compiling a program, use the -fno-stack-protector option with gcc:
<pre>
gcc -fno-stack-protector -o vulnerable_program source.c
</pre>


* [https://moodle.fh-campuswien.ac.at/course/view.php?id=29360 Ausgewählte Kapitel der IT-Security ILV (2023, 2024)]
== Conclusion ==
Since the rise of C in the early 1970s, buffer overflows have become a serious security vulnerability. Even though high-level programming languages are typically not affected, the number of vulnerable systems is actually rising. At the same time, a wide array of countermeasures are also increasingly adopted and applied. Features like executable space protection (e.g., data execution prevention under Windows) have already been deployed since the mid-2000s, and on the compiler side, technologies like Stackguard support several detection and prevention mechanisms (e.g., different types of canaries). Furthermore, almost every widely used operating system supports Address Space Layout Randomization in order to minimize the attack surface for buffer overflow attacks. For example, at the beginning of 2020, most of the bigger operating systems (Linux, Windows, macOS, iOS, Android, Solaris, OpenBSD, etc.) will offer support for ASLR.
 
Another key point is the expansion of the Internet of Things (IoT). These widely distributed networks of hardware endpoints have deemed themselves the perfect target for buffer overflow attacks. This stems from the fact that IoT applications mostly utilize low-level, closely hardware-related languages such as C and C++, both of which are almost exclusively for buffer overflows.


== References ==
== References ==


* [http://phrack.org/issues/49/14.html Smashing the stack for fun and profit (1996, Aleph One)]
<references />
 
=== Further Reading ===
 
* [http://phrack.org/issues/57/8.html Vudo malloc tricks (2001, MaXX)]
* [http://phrack.org/issues/60/10.html#article Basic Integer Overflows (2002, Blexim)]
* [http://phrack.org/issues/60/10.html#article Basic Integer Overflows (2002, Blexim)]
* [https://doi.org/10.1109/SP.2013.45 Just-In-Time Code Reuse: On the Effectiveness of Fine-Grained Address Space Layout Randomization (2013, Snow et al.)]
* [https://doi.org/10.1109/SP.2013.45 Just-In-Time Code Reuse: On the Effectiveness of Fine-Grained Address Space Layout Randomization (2013, Snow et al.)]
* [http://www.cs.ucf.edu/~czou/CDA6938-06/Buffer%20Overflows.pdf Buffer Overflow for Dummies (2002, Josef Nelißen)]
* [http://www.cs.ucf.edu/~czou/CDA6938-06/Buffer%20Overflows.pdf Buffer Overflow for Dummies (2002, Josef Nelißen)]
* [https://www.hackingarticles.in/a-beginners-guide-to-buffer-overflow/ A Beginner’s Guide to Buffer Overflow (2021, Raj Chandel)]
* [https://www.hackingarticles.in/a-beginners-guide-to-buffer-overflow/ A Beginner’s Guide to Buffer Overflow (2021, Raj Chandel)]
* [https://repository.unikom.ac.id/56387/1/24_Deadly_Sin.pdf 24 Deadly Sins of Software Security (2009, Michael Howard, David LeBlanc, and John Viega)]
* [https://scholar.google.com/scholar_case?case=551386241451639668 United States v. Morris (1991, U.S. Dept. of Justice)]
* [https://scholar.google.com/scholar_case?case=551386241451639668 United States v. Morris (1991, U.S. Dept. of Justice)]
* [https://cwe.mitre.org/top25/archive/2023/2023_top25_list.html 2023 CWE Top 25 Most Dangerous Software Weaknesses (2023, CWE)]
* [https://cwe.mitre.org/top25/archive/2023/2023_top25_list.html 2023 CWE Top 25 Most Dangerous Software Weaknesses (2023, CWE)]
* [https://computersciencewiki.org/index.php?title=Stack_memory, Stack memory (2023, Bill MacKenty)]
* [https://computersciencewiki.org/index.php?title=Stack_memory Stack memory (2023, Bill MacKenty)]
* [https://computersciencewiki.org/index.php?title=Heap_memory, Heap memory (2023, Bill MacKenty)]
* [https://computersciencewiki.org/index.php?title=Heap_memory Heap memory (2023, Bill MacKenty)]


[[Category:Basic]]
[[Category:Basic]]

Latest revision as of 18:18, 11 December 2024

Buffer Overflow examle [1]

Introduction

Buffer overflow is a security vulnerability that should not be underestimated, as it has been the most common type of vulnerability in the last decade. This type of attack is an essential part of all security attacks, as buffer overflow vulnerabilities are widespread and easy to exploit. Especially in the field of cyber attacks, they are exploited by users to gain access to vulnerable servers and control them.

Definitions

Buffer

A buffer is defined as a limited, contiguously allocated set of memory. The most common buffer in C is an array. [2]

Buffer Overflow

Buffer Overflows are possible because in the C and C++ languages, there exists no inherent bounds-checking to ensure that data being copied into a buffer will not be larger than what the buffer was initialized to hold. Consequently, if the person writing the program has not explicitly coded the program to check for oversize input, it is possible for data to fill a buffer, and if that data is large enough, to continue to write past the end of the buffer. [2]

Common Weakness Enumeration (CWE)

The importance of addressing buffer overflow vulnerabilities can be seen by examining Mitre’s respective parent category: [3]

  • CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer

There is a large variety of direct and indirect child categories that further help create a taxonomy of the issues at hand. Some of those are listed here: [4] [5]

  • CWE-788: Access of Memory Location After End of Buffer
  • CWE-787: Out-of-bounds Write
  • CWE-786: Access of Memory Location Before Start of Buffer
  • CWE-125: Out-of-bounds Read
  • CWE-120: Buffer Copy Without Checking Size of Input (’Classic Buffer Over- flow’)
  • CWE-121: Stack-based Buffer Overflow
  • CWE-122: Heap-based Buffer Overflow
  • CWE-126: Buffer Over-read

History

In the past, there were often a series of events executed with the help of buffer overflows. In 1980, the term Internet worm was a very well-known and sensitive topic because a worm was malicious software that reproduced itself and spread through network connections. It was called a Morris worm because on November 2, 1988, an event occurred that changed the way people thought about networks and about the Internet. On that day, tens of thousands of computers quickly and simultaneously became infected with a self-replicating computer program. Back then, computer science student Robert T. Morris created a computer worm that exploited an unsafe function, which at the time was very commonly used and distributed. Through the practical application of a buffer overflow, the computer worm spread itself around at an alarming rate and, going back, nearly shut down the entire internet. This situation resulted in Morris being the first person convicted under the Computer Fraud and Abuse Act, demonstrating further how dangerous buffer overflows can be. To this day, this attack is perhaps one of the most significant events in the history of computing. With buffer overflows, it was also possible to bypass various security measures. For example, buffer overflows could be used to remove software restrictions from firmware or to bypass copy protection. This was the case with the Android and iOS operating systems, where it was possible to remove various locks and modify the smartphone according to one's own wishes. For example, it was possible to install apps that were not available in the store elsewhere, change the boot animation, access hidden system files, remove manufacturer-specific apps, remove network locks, and much more. One keyword is "jailbreaking" for Apple devices and "rooting" for Android devices. On Nintendo's game console, a game called Pokemon Yellow could be changed from the inside by manipulating the program using shellcode.

While many of these events already reside in the past, buffer overflows do not. The exploit reoccurs frequently, so much so that in 2023 they still secured themselves a spot on the Common Weakness Enumeration/SANS list of the Top 25 Most Dangerous Software Errors. Whether through the adaptation of the exploit's mechanism or simply by focusing on a new set of targets, buffer overflows stay relevant. That's why, when dealing with cybersecurity of any sort, there is no way past them.

Technical Background and Context

Memory Layout of a Process

Informally, a process is a program in execution. The status of the current activity of a process is represented by the value of the program counter/instruction pointer and the content of the processor’s registers. Different parts of the program are stored in different memory segments, as shown in the figure below.

[1]

  • Stack section: temporary data storage when invoking functions (such as function parameters, return addresses, and local variables)
  • Heap section: memory that is dynamically allocated during program run time
  • Data section: global variables (initialized and uninitialized)
  • Code/Text section: the executable, machine-readable bytecode

Readable, Writeable, Executable Memory

Memory regions have different rights with respect to the process they belong to. For instance, the text section will usually be marked read-only, and any attempt to write to it will result in a Segmentation Fault. [6] All other sections have to be writable in order for the process to work properly. [2] Whether a section is executable will largely depend on the applied settings during the compilation of the program and the platform on which it is executed.

The Stack and Important Control Structures

The stack’s primary purpose is to implement and help with the use of functions. A function call alters the flow of execution through a program. However, when its task is completed, a function returns control to the statement or instruction following the function call. Furthermore, the stack is used to allocate memory for the local variables used in the functions and to return values from the function. [6]

During a function call, arguments are pushed onto the stack in reverse order, and the return address (the instruction pointer EIP) is saved to enable returning to the caller after function execution. Key stack-related registers are:

  • EIP (Instruction Pointer): Stores the next instruction's address.
  • ESP (Stack Pointer): Points to the top of the stack.
  • EBP (Base Pointer): Marks the base of the stack frame for the current function.

When a function is called, the stack is manipulated using PUSH and POP instructions. A return address is pushed onto the stack, allowing the program to return to the calling function. Buffer overflows can occur if a function does not properly limit the size of data written to buffers, potentially overwriting the return address. An attacker may overwrite the return pointer to redirect the flow of execution.

Causes

C/C++: Vulnerable Functions

C is the most affected programming language when it comes to creating buffer overflows, closely followed by C++. [7] When writing C code, the programmer is responsible for data integrity. If this responsibility were shifted over to the compiler, the resulting binaries would be significantly slower and less efficient. Furthermore, C’s simplicity increases the programmer’s control. However, this can result in programs that are vulnerable to buffer overflows and memory leaks if the programmer isn’t careful. [8]

Some vulnerable functions from the C/C++ standard are listed here, each with their respective, saver counterpart:

Vulnerable Safer
strcpy() strncpy()
gets() fgets()
strcat() strncat()
memcpy() memmove()
scanf() sscanf()
memset() -

IoT/Embedded Devices

"The constrained resources of embedded systems leads to the predominant use of the C language. This, along with the tight processing requirements, leave associated devices open to BOF based attacks." [9]

Dangers

Arguably, the most significant consequence of a buffer overflow is when the attacker is able to execute their own malicious shellcode. Which is also referred to as arbitrary code execution. This way it is completely up to the attacker itself what they will do with the same privileges as the program on the level of the current user. Create a reverse shell to execute remote code, stealing data or even manipulating all settings of the system. By getting elevated privileges, the attacker would also be able to run code as an administrator or root. Another potential consequence is a Denial of Service (DoS) attack. Instead of executing code, the goal is to cause unpredictable behavior or crash the system by overwriting as much data as possible until the program is unable to handle it. The downtime of a program can be costly for large organizations. Data corruption, system instability, and information disclosure, are all potential consequences that can be caused by a buffer overflow. Consequently, it is up to the creativity of the attacker once a vulnerable code is identified.

Summed up, these four are the main dangers:

  • Data Corruption
  • Program Crashes
  • Exploitation: Control Flow Alteration
  • Exploitation (via Shellcode Injection): Arbitrary Code Execution

Types of Buffer Overflow Vulnerabilities

The following section will introduce descriptions and simple examples related to the most relevant categories of buffer overflow vulnerabilities. The distinction between Buffer Over-Read and Buffer Over-Write provides a basic categorization of the issues at hand. The respective sections will include simple, illustrative examples. The sections on Stack Buffer Overflow and Heap Buffer Overflow will present more complex concepts, introducing the mechanics and control structures of each memory region and how they could potentially be exploited. Note that Over-Read and Over-Write vulnerabilities can occur in both sections of process memory.

Buffer Over-Read

The following is a simple example of a Out-of-bounds read operation, that can be performed by a C program:


#include <stdio.h> 
#include <string.h> 

int main(){

int array[5] = {1 ,2 ,3 ,4 ,5}; 
printf("%d\n", array[5]);

}

This program illustrates a common error that new programmers might run into when learning about arrays: Referencing array[5] will eventually attempt to access its sixth element, although the array was only initialized to hold five elements. When this program is run, it will read beyond the bounds of the array and might produce unexpected results.

Buffer Over-Write

In this section, a simple Out-of-bounds write vulnerability will be examined. Although this particular example showcases a stack-based buffer overflow, we will use it to introduce the general concept of a over-write vulnerability and delve into specifically exploiting the stack's mechanics and control structures in the later section "Stack-based Buffer Overflow".

The following program deliberately uses the insecure strcpy() function to copy a large string into a buffer, which is actually too small for the string. The following listing displays the complete source code.

#include <stdio.h> 
#include <string.h>

int main() {

    char B[3] = "03"; // gets higher address on stack
    char A[8] = "0000000"; // gets lower address on stack

    printf("Before overflow: A = %s, B = %s\n", A, B);

    strcpy(A, "excessive"); // dangerous 
    printf("After overflow: A = %s, B = %s\n", A, B);

    return 0;
}

Console output running the program (bash).

Before overflow: A = 0000000, B = 03
After overflow: A = excessive, B = e

Inspection of memory before the overflow (gdb). The memory addresses on the left are the locations of the two buffers.

0xffffcc85:  0x30  0x30  0x30  0x30  0x30  0x30  0x30  0x00
0xffffcc8d:  0x30  0x33  0x00

Inspection of memory after the overflow (gdb).

0xffffcc85:  0x65  0x78  0x63  0x65  0x73  0x73  0x69  0x76
0xffffcc8d:  0x65  0x00  0x00

Integer Overflow

Integer overflows occur when an arithmetic operation attempts to generate a value that lies outside a range that can be represented with a specified number of bits. The most common result of an overflow is that the least significant representable bits of the result are stored. An overflow condition can lead to results that are equivalent to unintended behavior. In particular, if the possibility is not expected, an overflow can affect the reliability and safety of a program. A code example is shown below.

unsigned char a = 255;
unsigned char b = 2;
unsigned char Result = a + b;

The data type unsigned char is used, which comprises 8 bits, and the value range is from 0 to 255. For the variable "a," the value 255 is assigned, and for the variable "b," the value 2 is assigned. If an arithmetic operation is performed, we would get a result that requires more bits than are present to represent. The corresponding dual calculation is shown below.

  11111111 (a)
+ 00000010 (b)
----------
 100000001 (Result)

The front one, the ninth bit, is no longer contained in the 8 bits of the data type unsigned char. If only the last 8 bits were considered, the result would be 1 and not 257.

The Ariane 5 disaster of 1996 illustrates what can happen when integer overflows are not handled properly in the real world. The rocket was destroyed 37 seconds after launch because of a severe software error, which was caused by an integer overflow. This shows how unresolved integer overflows can have disastrous consequences, particularly in safety-critical systems like aerospace control software. The incident could have been prevented if there had been more robust input validation and error handling procedures, particularly during the conversion process from floating-point to integer data types. [10]

Stack-based Buffer Overflow

If more data is written to a buffer than the size assigned to it, there is a chance it could overwrite the adjacent memory. This is called a stack overflow. Since the memory overwritten contains valuable information like the return address, this exploitation is a prime target. As an example, consider an input string that gets copied to the stack from a vulnerable function, that does not validate the size. Whatever was written to this string, as long as it is longer than the size of the buffer, will overwrite memory next to it. If an attacker creates a string that reaches the return address with a new address on purpose, the program will jump to this exact location after its execution. This type of attack is called "stack smashing". It is frequently used and is well-known in the context of buffer overflows. A common problem of stack smashing is how to guess the starting address of the own shellcode. Just by guessing or brute-forcing the exact return value, it would take the attacker a long time and an enormous number of attempts. This can be minimized by inserting a series of NOP instructions in front of the shellcode. A NOP instruction is a special operation which will push the execution one by one until it hits the malicious code. Adding as many NOP instructions as possible increases the chances of reaching the desired code. This means that as long as the return address points to any NOP instruction, the application will execute the shellcode and we do not have to directly hit the address where the code starts.

Stack overflows which alter return addresses, are one of the most dangerous methods of gaining unauthorized access to a system. They allow an attacker to circumvent standard program execution and potentially gain complete control. [6]

Heap-based Buffer Overflow

A program may not know at compile time how much memory it will need. Such segments can be allocated dynamically, during runtime, and will be placed on the heap. Special system calls brk() and mmap() can be used by Linux programs to achieve that. The functions malloc(), calloc(), realloc() and free() provide convenient wrapper functions around these system calls and help manage those memory segments. [2]

To be efficient, any malloc() implementation stores a lot of meta-data about the location of the chunks, the size of the chunks, and perhaps some special areas for small chunks. It also organizes this information. In dlmalloc, it is organized into buckets, and in many other malloc implementations it is organized into a balanced tree structure. This information is stored in two places: in global variables used by the malloc() implementation itself, and in the memory block before and/or after the allocated user space. Thus, the heap contains important information about the state of memory stored directly after any user-allocated buffer. [2]

Use-After-Free Vulnerability

Use-after-free (UAF) vulnerabilities are a specialized form of a memory management bug occurring when a program tries to use a memory object even though it is already deallocated. This vulnerability can happen, because the pointer to the corresponding memory location may remain accessible, despite the memory itself being marked as accessible for other allocations. If a program tries to access this now-freed memory, it could cause some unexpected behavior or even a security breach. Since an attacker could exploit this "dangling pointer" to gain unauthorized access to the system or leak information meant to be kept confidential. [11]

Mitigation Techniques and How to Disable Them

This section will briefly introduce some mitigation techniques that can potentially prevent buffer overflow attacks. First, it will explain the basic concepts in a simple way, and second, it will provide brief guides on how to disable certain mitigation techniques for research purposes.

Adress Space Layout Randomisation (ASLR)

ASLR randomizes the memory address space layout of processes, making it harder to predict the location of specific functions or buffers. [2] Since code reuse attacks (e.g., ROP attacks) require the memory addresses of gadgets to be known to an attacker, techniques to randomize their entry points have become increasingly popular.

ASLR can be temporarily disabled at the system level (Linux):

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

Re-enable after testing:

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

Non-executable Stack (nx-stack)

A non-executable stack, or nx-stack, prevents execution of code in the stack, when designated as non-executable, mitigating buffer overflow attacks that inject shellcode into that region. [2]

To disable nx-stack on a program, use the -z execstack option during compilation. This allows execution of code in the stack memory:

gcc -z execstack -o vulnerable_program source.c

Data Execution Prevention (DEP)

Data Execution Prevention (DEP) is a security feature originally developed by MicrosoftR© for Windows XP SP2. There are two basic variants: hardware-based DEP and software-based DEP. If supported by the CPU as well as the process, hardware DEP will be employed; otherwise, DEP has to be carried out in software, which is part of the Windows operating system. The basic functionality of DEP is to prevent applications from executing code in a non-executable area of memory.

Stack Canaries

Stack canaries are small random values placed on the stack to detect and prevent buffer overflow attacks. If an overflow occurs and modifies the stack, the canary value will change, triggering a security alert or crash. [2] Typical types of canaries, which are supported by security hardening technologies like ProPolice or Stackguard (GCC), are terminator canaries, random canaries, and random XOR canaries.

To disable stack canaries when compiling a program, use the -fno-stack-protector option with gcc:

gcc -fno-stack-protector -o vulnerable_program source.c

Conclusion

Since the rise of C in the early 1970s, buffer overflows have become a serious security vulnerability. Even though high-level programming languages are typically not affected, the number of vulnerable systems is actually rising. At the same time, a wide array of countermeasures are also increasingly adopted and applied. Features like executable space protection (e.g., data execution prevention under Windows) have already been deployed since the mid-2000s, and on the compiler side, technologies like Stackguard support several detection and prevention mechanisms (e.g., different types of canaries). Furthermore, almost every widely used operating system supports Address Space Layout Randomization in order to minimize the attack surface for buffer overflow attacks. For example, at the beginning of 2020, most of the bigger operating systems (Linux, Windows, macOS, iOS, Android, Solaris, OpenBSD, etc.) will offer support for ASLR.

Another key point is the expansion of the Internet of Things (IoT). These widely distributed networks of hardware endpoints have deemed themselves the perfect target for buffer overflow attacks. This stems from the fact that IoT applications mostly utilize low-level, closely hardware-related languages such as C and C++, both of which are almost exclusively for buffer overflows.

References

  1. What is a Buffer Overflow Attack, [Online]. Available: https://www.wallarm.com/what/buffer-overflow-attack-definition-types-use-by-hackers-part-1. Accessed: Dec. 11, 2024
  2. 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 C. Anley, The Shellcoder’s Handbook: Discovering and Exploiting Security Holes, 2nd ed., Indianapolis: Wiley, 2007
  3. MITRE, "CVE Search Results for 'buffer overflow'," [Online]. Available: https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=buffer+overflow. Accessed: Oct. 16, 2024.
  4. MITRE, "CWE-119: Improper Restriction of Operations within the Bounds of a Memory Buffer," [Online]. Available: https://cwe.mitre.org/data/definitions/119.html. Accessed: Oct. 16, 2024.
  5. MITRE, "CWE-788: Access of Memory Location After End of Buffer," [Online]. Available: https://cwe.mitre.org/data/definitions/788.html. Accessed: Oct. 16, 2024.
  6. 6.0 6.1 6.2 A. One, "Smashing the Stack for Fun and Profit," Phrack, vol. 7, no. 49, Nov. 1996.
  7. M. Howard, D. LeBlanc, and J. Viega, *24 Deadly Sins of Software Security: Programming Flaws and How to Fix Them*, 1st ed. USA: McGraw-Hill, Inc., 2009.
  8. J. Erickson, *Hacking: The Art of Exploitation*, 2nd ed. San Francisco: No Starch Press, 2008.
  9. G. Mullen and L. Meany, "Assessment of Buffer Overflow Based Attacks On an IoT Operating System," 2019 Global IoT Summit (GIoTS), Aarhus, Denmark, 2019, pp. 1-6, doi: 10.1109/GIOTS.2019.8766434.
  10. Innovative Bytes, Ariane-5 disaster - (integer overflow - space requirements), Medium, October 2022
  11. Byoungyoung Lee, Chengyu Song, Yeongjin Jang, Tielei Wang, Taesoo Kim, Long Lu, and Wenke Lee. Preventing use-after-free with dangling pointers nullification. In Network and Distributed System Security Symposium, 2015

Further Reading