A buffer overflow is the amount of data that a computer program fills into a buffer that exceeds the capacity of the buffer itself. Overflow data is overwritten with legitimate data. Ideally, the program checks the length of the data and does not allow the input of strings that exceed the buffer length. However, most programs assume that the data length always matches the allocated storage space, which is a hidden danger for buffer overflow.
The buffer used by the operating system is also known as a stack, where instructions are temporarily stored in the stack and a buffer overflow occurs on the stack. When an extra-long data goes into the buffer, the excess is written to the other buffers, the other buffers may hold the data, pointers to the next instruction, or the output of other programs, which are overwritten or destroyed. A small amount of data or an overflow of a set of instructions can cause a program or operating system to crash.
#include <stdio.h> #include <str ing . H> #include <iostream> using namespace Std; int Main (int argc, char *argv[]) { char buf[< Span style= "color: #800080;" >10 ]; strcpy (buf, argv[ 1 <<BUF; return 0
an overflow occurs when you enter 20 characters in a row.
C language commonly used strcpy, sprintf, strcat and other functions are very easy to cause buffer overflow problem.
When the program runs, its memory usually contains these parts:
(1) Procedure parameters and program environment;
(2) The
program stack (
The stack is very special, mainly when the function is called to
save the scene, so that the function can continue to run after return
), it usually grows when the program executes, in general, It grows downward toward the heap .
(3) heap, it also grows when the program executes, instead, it grows upward toward the stack;
(4) A BSS segment that contains uninitialized, globally available data (for example, global variables);
(5) A data segment that contains the initialized globally available data (usually a global variable);
(6) A text segment that contains read-only program code.
BSS, data, and text segments compose static memory: The size of these segments is fixed before the program runs. While the program is running, you can change individual variables, but you cannot assign data to those segments.
Take the following procedure as an example:
#include <stdio.h> char buf[3"ABC"; int i; int Main () { 1; return 0 ; }
among them,
I belongs to BBS segment, and buf belongs to data segment. Both are static memory , because they can change the value in the program, but the allocated memory size is fixed, such as BUF's data is greater than three characters, will overwrite other data.
in contrast to static memory, heaps and stacks are dynamic and can change size when the program is running . The programmer interface for the heap varies by language. In the C language, the heap is accessed through malloc () and other related functions, while the new operator in C + + is the programmer interface of the heap. The stack is special, mainly when the function is called to save the scene so that it can continue to run after the function returns.
Buffer overflow attack
A brief introduction to buffer overflow attacks:
A fatal use of a buffer overflow is
to let the program perform functions that it would otherwise not be willing to execute . This is a common way to attack the system through computer network security. Typically,
a string is entered into the program, which contains the byte encoding of some executable code, which becomes the attack code . In addition, there are some bytes that
overwrite the return address with a pointer to the attack code . Then,
the effect of executing the RET instruction is to jump to the attack code . My God, the hacker successfully invaded. A form of attack in which an attack code initiates a shell using a system call, providing an attacker with a set of operating system functions. Another form of attack, the attack code performs some unauthorized tasks, repairs the damage to the stack, and then executes the RET instruction for the second time (normally returned to the caller on the surface). So how do you protect against buffer overflow attacks? (1)
Stack randomization (mainly limited by Linux system version, old version does not support stack randomization): Make the stack position change every time the program runs。 In order to insert the attack code in the system, the attacker will not only insert code,
You also need to insert a pointer to this code (point to the first address/stack address of the attack code), this pointer is also part of the attack string. Generating this pointer requires knowing the stack address where the string is placed. The old system version, if the same program is running on the same system, the stack position is quite fixed. So hackers can be on a machine to study how the stack on the system is assigned the address, you can invade other hosts.
Implementation : At the beginning of the program, allocate a
random size space
between 0~n bytes on the stack. The allocated range n must be large enough to get a wide variety of stack address changes, but small enough to not waste too much space on the program.
T
Radeoff. (2)
stack break detection (mostly limited by GCC version, old GCC version does not support stack break detection): When stack corruption is detected。 From functions such as strcpy, we can see that the destruction usually occurs when the boundary beyond the local buffer is exceeded. In the C language,
There is no reliable way to prevent an out-of-bounds write of an array。 However, we can try to detect it and terminate the program before it has caused any harmful results to occur when the cross-border is written.
the way to achieve :
Canary, add a stack protection mechanism . in the stack frame, where a Sentinel (Canary) is placed immediately after the local buffer position, the sentinel value is randomly generated, and the attacker has no easy way to know what it is. Before resuming the register state and returning the function, the program checks to see if the value of the canary has changed and terminates the program immediately if a change occurs. In-depth understanding of the operating system P182 page, there is a particularly good example. (3)
Restrict executable code areas (primarily subject to hardware version limitations, hardware support required): Eliminates the ability of an attacker to insert executable code into the system by restricting the memory area that can hold executable code. In a typical system,
only the portion of memory that holds the code generated by the compiler needs to be executable, and the rest can be restricted to read and write only. The general system allows three kinds of access forms:
read (read data from memory), write (store data to memory) and execute (treat the contents of the memory as machine-level code) 。 before
x86 architecture combines read and execute access control into 1-bit flagsSo that any page that is marked as readable is executable. The stack is also required to be both readable and writable, so the bytes on the x86 architecture stack are executable. There are also systems that can limit the number of pages that are readable but not enforceable, but these systems generally have serious performance losses.
Implementation : AMD has introduced "NX" (No-execute, non-execution) bits for its content protection of its 64-bit memory, separating read and execute access patterns, and Intel has followed up. From there, the stack can be marked as readable, writable, but not executable.
check whether the page can be executed by hardware, without loss of efficiency.
Buffer overflow and buffer overflow attacks