All programmers are aware of the potential threat of buffer overflows in their programs. There are many threats associated with it, both in new and old software, regardless of the number of patches made. Attackers can take advantage of this error by injecting code specifically designed to cause an overflow of the initial part of the data set, and then write the rest to the memory address adjacent to the overflowed one.
The data may contain executable code that will allow attackers to run larger and more complex programs or provide them access to the system. The mistake is very difficult to find and fix, because the software code consists of millions of lines. Corrections to these errors are quite complex and, in turn, are also prone to errors, which complicates the process of elimination.
Buffer overflow detection
Before looking for overflow, you need to know what it is. As the name implies, these vulnerabilities are associated with buffers or memory allocation in languages that provide direct low-level read and write access.
When using the C and Assembler languages, reading or writing such distributions does not entail automatic border checking. In this connection, if an overflow of the stack buffer is detected in this application, there is no check for the possibility of placing the number of bytes in the buffer in question. In such cases, the program may "overflow" its capacity. This leads to the fact that the data written after filling, rewrite the contents of subsequent addresses on the stack and read additional. Overflow may occur unintentionally due to user errors.
It happens that it is caused by the malicious subject sending carefully crafted malicious input to the program, which then tries to save it to an insufficient buffer. If an overflow of the stack buffer is detected in this application, redundant data is written to the neighboring one, where they overwrite any existing data.
Usually they contain a pointer to the return of the function being used - the address to which the process should move on. An attacker can set new values to point to an address of choice. The attacker usually sets new values to indicate where the payload is located. This changes the way the process runs and instantly transfers control to the malicious code.
Using buffer overflows allows an attacker to control or terminate a process or change its internal variables. This violation takes place in the top 25 most dangerous software errors in the world (2009 CWE / SANS Top 25 Most Dangerous Programming Errors) and is defined as CWE-120 in the dictionary of enumerations of weak system places. Despite the fact that they are well studied, they continue to harm popular programs.
Simple buffer use vector
When working with source code, special attention should be paid to where buffers are used and modified. Of particular note are functions related to input provided by the user or other external source, as they provide a simple vector to use when a stack buffer overflow is detected. For example, when a user asks the question “yes” or “no”, it is advisable to save the user string data in a small buffer for the string “yes”, as shown in the following example.
Looking at the code, it is clear that border checking is not performed. If the user enters “maybe”, the program will crash, and not ask him for an answer that is written to buffer regardless of its length. In this example, since user answer is the only variable declared, the following values on the stack will be the value of the return address or the place in memory where the program will return after executing the ask Question function.
This means that if the user enters four bytes of data, which is enough to overflow the client command buffer, a valid return address will follow, which will be changed. This will cause the program to exit the function at a different point in the code than originally intended, and may lead to the fact that the software will behave in a dangerous and unintentional way.
If the first step to detect buffer overflows in the source code is to understand how they work, the second step is to study external input and buffer manipulation, then the third step will be to find out which functions are vulnerable to this vulnerability and which can act as “red flags” . The gets function is great for writing outside of the buffer provided to it. In fact, this quality extends to the entire family of related capabilities, including strcpy, strcmp, and printf / sprintf, wherever one of these overflow vulnerability features is used.
Removal from the code base
If a stack buffer overflow is detected in the source code, a consistent removal of them from the database will be required. To do this, you must be familiar with safe working methods. The easiest way to prevent these vulnerabilities is to use a language that does not allow them. C language has these vulnerabilities due to direct access to memory and the lack of strict typing of objects. Languages that do not share these aspects are usually invulnerable. These are Java, Python, and .NET, along with other languages and platforms that do not require special checks or changes.
Of course, it is not always possible to completely change the development language. In this case, safe methods are used to work with instruction buffer overflows. In the case of string processing functions, there was a lot of discussion about which methods are available, which are safe to use, and which should be avoided. The strcpy and strcat functions copy the string to the buffer and add the contents of one to the other. These two methods exhibit unsafe behavior because they do not check the boundaries of the target buffer, and write outside if there are enough bytes for this.
Alternative protection
One frequently suggested alternative is related versions, which write to the maximum size of the destination buffer. At first glance, this looks like the perfect solution. Unfortunately, these features have a slight nuance that causes problems. When the limit is reached, if the trailing character does not fit on the last byte, serious malfunctions occur when reading the buffer.
In this simplified example, the danger of lines that do not end in zero is visible. When foo is placed in a normal buffer, it ends with zero because it has extra space. This is the best case scenario. If the bytes in the buffer overflow on the stack are in another character buffer or another printable line, the print function continues reading until the trailing character of this line is reached.
The disadvantage is that C does not provide a standard, safe alternative to these functions. Nevertheless, there is a positive thing - the availability of several implementations for a particular platform. OpenBSD provides strlcpy and strlcat that work similarly to strn functions, except that they truncate the string one character earlier to make room for the null terminator.
Similarly, Microsoft provides its own secure implementations of commonly used string processing functions: strcpy_s, strcat_s, and sprintf_s.
Using the safe alternatives listed above is preferred. When this is not possible, manual boundary checking and zero termination are performed when processing string buffers.
Compilation vulnerabilities
If an unsafe function leaves open the possibility of buffer C overflow, then not everything is lost. When the program starts, compilers often create random values known as canary and push them onto the stack, so they are dangerous. Checking the value of the canary against its original value can determine if a Windows buffer overflow has occurred. If the value has been changed, the program will be closed or go into an error state, and not to the potentially changed return address.
Some modern operating systems provide additional protection against buffer overflows in the form of impracticable stacks and randomization of address space allocation (ASLR). Non-executable stacks - Data Execution Prevention (DEP) - mark the stack, and in some cases other structures, as areas where the code will not be executed. This means that the attacker cannot inject the exploit code onto the stack and wait for its successful execution.
Before fixing the buffer overflow, decompress ASLR on the PC. It was designed to protect against return-oriented programming as a workaround to impracticable stacks where existing code fragments are chained based on the offset of their addresses.
It works by randomizing the memory areas of structures, so their displacements are more difficult to determine. If this protection existed in the late 1980s, the Morris worm could have been prevented. This is due to the fact that it functioned partially, filling the buffer in the UNIX finger protocol with the exploit code, and then overflowing it to change the return address and pointing to the filled buffer.
ASLR and DEP complicate the precise determination of the address to be specified, making this memory area completely inactive. Sometimes a vulnerability slips through cracks that are open to a buffer overflow attack, despite the presence of controls at the development, compiler, or operating system level.
Static Coating Analysis
In a buffer overflow situation, there are two crucial tasks. Firstly, it is necessary to determine the vulnerability and change the code base to solve the problem. Secondly, they ensure that all versions of the buffer overflow vulnerability code are replaced. Ideally, this will start with an automatic update of all Internet-connected systems.
Such an update cannot be expected to provide sufficient coverage. Organizations or individuals can use the software on systems with limited Internet access that require manual updates. This means that update news should be distributed to any administrators who can use the software, and the patch should be easily accessible for download. Creation and distribution of patches is performed as close to vulnerability detection as possible, which minimizes vulnerability time.
By using the secure buffer handling functions and the corresponding security functions of the compiler and the operating system, you can create reliable buffer overflow protection. With these steps in mind, consistent identification of flaws is a crucial step to prevent exploit.
Combining lines of source code in search of potential threats can be tedious. In addition, there is always the possibility that human eyes may miss something important. Static analysis tools are used to ensure the quality of the code; they were developed specifically to detect security vulnerabilities during development.
Static coverage analysis sets red labels for potential buffer overflows. Then they are processed and fixed separately, so as not to manually search the database. These tools, combined with regular checks and knowledge of how to eliminate overflows, allow you to identify and eliminate the vast majority of shortcomings before the completion of software development.
Performing root attack
Encoding errors usually cause buffer overflows. Common mistakes in developing applications that can lead to it include the inability to allocate sufficiently large buffers and the lack of a mechanism to check for these problems. Such errors are especially problematic in C / C ++ languages, which do not have built-in overflow protection and are often targets of buffer overflow attacks.
In some cases, an attacker injects malicious code into memory that has been corrupted due to a stack buffer overflow. In other cases, they simply take advantage of damage to neighboring memory. For example, a program that requests a user password to provide him access to the system. In the code below, the correct password grants root privileges. If the password is incorrect, the program does not grant the user privileges.
In the above example, the program gives the user root privileges, even if he entered the wrong password. In this case, the attacker provides an input whose length is greater than the buffer can accommodate, creating an overflow that overwrites the memory of the integer pass. Therefore, despite the incorrect password, the pass value becomes non-zero, and the attacker gains root privileges.
Temporary storage area attack
A buffer is a temporary area for storing data. When a program or system process places more data than was originally allocated for storage, additional data overflows. This leads to the fact that some of them leak into other buffers, damage or overwrite data.
In an overflow attack, the additional data contains special instructions for actions intended by a hacker or a malicious user, for example, they trigger a response that damages files, modifies data or reveals personal information.
An attacker uses an overflow exploit to take advantage of a program that is waiting for user input. There are two types of buffer overflows: stack-based and heap-based. Heap-based ones are difficult to execute and least common, while attacking the application, filling up the space reserved for the program.
Stack is the memory space used to store user input. This overflow is more common among attackers using applications.
Modern compilers usually provide the ability to check overflows during compilation / linking, but at runtime it is quite difficult to check this problem without any additional protection mechanism for exception handling.
Options for the program:
- Input: 12345678 (8 bytes), the program works without failures.
- Input: 123456789 (9 bytes), the message “Segmentation error” appears, the program ends.
Vulnerability exists due to overflow if user input argv exceeds 8 bytes. For a 32-bit system (4 bytes) fill the memory with a double word (32 bits). The character size is 1 byte, so if you request a buffer with 5 bytes, the system will select 2 double words (8 bytes). This is why when you enter more than 8 bytes, the Buffer will be full.
Similar standard features that are technically less vulnerable exist. For example, strncpy (), strncat (), and memcpy (). The problem with these functions is that the responsibility for determining the size of the buffer lies with the programmer, not the compiler.
Every C / C ++ programmer needs to know the problem before starting coding. Many generated problems in most cases can be protected from overflow.
Dangers in C / C ++
C users should avoid using dangerous functions that do not check boundaries unless they are sure that the boundaries will not be exceeded. Functions that should be avoided in most cases to provide protection include strcpy functions. They should be replaced with features such as strncpy. The strlen function should be avoided if the user is sure that a trailing NIL character will be found. Scanf () family: scanf (3), fscanf (3), sscanf (3), vscanf (3), vsscanf (3) and vfscanf (3) - it is dangerous to use, it is not used to send data to a string without controlling the maximum lengths, "format% s" is a particularly common failure.
Officially snprintf () is not a standard C function in the ISO 1990 classification. These systems do not protect against buffer overflows, they simply call sprintf directly. It is known that the current version of Linux snprintf is working correctly, that is, it actually respects the established boundary. The return value of snprintf () also changes.
Version 2 of the Unix specification (SUS) and the C99 standard are different in that snprintf () returns.Some versions of snprintf don’t guarantee that the line ends in NIL, and if the line is too long, it will not contain NIL at all. The glib library has g_snprintf () with sequential return semantics, always ends with NIL and, most importantly, always takes into account the length of the buffer.
Communication port buffer overflow
buffer. . , , FIFO FIFO , .
, , . .
These extra bytes will be greater if the high priority process monitors the target processor in real time. Since the process of buffer overflow of the communication port has a higher priority than the interruption of VISA, the processor will not take any action until it is completed in real time.
The default VISA and Windows settings for the 16-byte FIFO are 14 bytes, leaving 2 bytes in the FIFO when the device tries to send a message from the source. At higher transfer rates on slow computers, it is possible to receive more than 4 bytes at the time when the processor requests the serial port, sending a signal to stop the transfer.
To solve the problem when a stack buffer overflow is detected in Windows 10, you need to open the device manager. Then find the COM port for which the settings are changed, and open the properties. Then click on the “Advanced” tab, a slider will appear that changes the size of the clipboard overflow so that UART enables flow control faster.
The default value is sufficient in most cases. However, if a buffer overflow error occurs, decrease the value. This will cause more interrupts to be sent to the processor with slower bytes in the UART.
Safe Development Methods
Safe development methods include regular testing to detect and eliminate overflows. The most reliable way to avoid or prevent it is to use automatic protection at the language level. Another fix is run-time bounds checking, which prevents overflows by automatically checking that data written to the buffer is within acceptable bounds.
The Veracode cloud service identifies code vulnerabilities such as buffer overflows, so developers fix them before they are used. Veracode's industry-unique patented binary static application security testing (SAST) technology analyzes it, including open source and third-party components, without the need for access to it.
SAST complements threat modeling and code reviews performed by developers by quickly and cost-effectively detecting errors and omissions in code through automation. As a rule, it starts in the early stages of the software development life cycle, since it is easier and cheaper to fix problems before embarking on a production deployment.
SAST identifies critical vulnerabilities such as SQL injection, crossite scripting (XSS), buffer overflow errors, unprocessed error conditions, and potential nooks and crannies. In addition, SAST binary technology provides useful information that prioritizes according to severity and provides detailed correction instructions.
The buffer overflow vulnerability has been around for almost 3 decades, but it is still burdensome. Hackers around the world continue to consider it their default tactic due to the sheer number of susceptible web applications. Developers and programmers are spending tremendous efforts to combat this evil of IT-technologies, coming up with more and more new ways.
The main idea of the latter approach is to implement a remediation tool that makes multiple copies of the return addresses on the stack and then randomizes the location of all copies in addition to the number. All duplicates are updated and checked in parallel, so that any discrepancy between them indicates a possible attack attempt and throws an exception.