Buffer overflow attack
On the previous post, I introduced to you the concept of buffer overflow. On this post, I am going to guide you how to find and exploit buffer overflow vulnerability.
Stack overflows are usually the easiest to use for all buffer overflows. Before understanding the stack overflow, first understand the following concepts:
- Buffers
In short, it is a contiguous area of computer memory that can hold multiple instances of the same data type. - Stack
A stack is an abstract data type that is often used in computer science. The object in the stack has a feature: the last object placed on the stack is always taken first, and this feature is usually called a last-in, first-out (LIFO) queue. The stack defines some operations. The two most important are PUSH and POP. The PUSH operation adds an element to the top of the stack. POP operation Instead, remove one element at the top of the stack and decrement the stack by one. - Register ESP, EBP, EIP
- The ESP register of the CPU holds the top pointer of the current thread,
- The EBP register holds the bottom pointer of the current thread.
- The EIP register of the CPU stores the memory address stored in the next CPU instruction. When the CPU executes the current instruction, it reads the memory address of the next instruction from the EIP register and then continues execution.
Modern computers are designed to understand the high-level language of people’s minds. In the use of high-level language constructor of the most important techniques are the process (procedure) and function (function). From this point of view, a procedure call can change the control flow of a program like a jump command, but unlike a jump, the function returns control to the statement or instruction after the call when the job completes. This high-level abstraction is achieved by the help of the stack. The stack is also used to dynamically allocate space to the local variables used in the function, as well as passing parameters and function return values to the function.
The stack consists of logical stack frames. When a function is called, the logical stack frame is pushed onto the stack and the logical stack frame is popped off the stack when the function returns. The stack frame contains the parameters of the function, the local variables of the function, and the data required to restore the previous stack frame, including the value of the instruction pointer (IP) at the time of the function call.
The first thing that must be done when a routine is called is to save the previous FP (so that it can be restored when the routine exits). Then it copies the SP to the FP, creates a new FP, and moves the SP forward for the local variable reservation. This is called the prolog work of the routine. When the routine exits, the stack must be clean, this is called the end of the routine (epilog) work. Intel’s ENTER and LEAVE instructions, Motorola’s LINK and UNLINK instructions, can be used for the effective prologue and finishing.
Below we use a simple example to show the appearance of the stack: example.c:
To understand what the program does when calling a function(), we compile with the -S option of gcc to produce assembly code output:
By looking at the assembly language output, we see that the call to a function() is translated to:
We call the three parameters of the function into the stack from backward to forward, and then call function (). The instruction call will also push the instruction pointer (IP) on the stack. We call this saved IP as the return address (RET). The first thing done in the function is the prologue of the routine:
The frame pointer EBP is pushed onto the stack, and the current SP is copied to EBP, making it a new frame pointer. We call this saved FP the SFP. Next we reduce the value of SP to reserve space for local variables We must remember that memory can only be addressed in words, where a word is 4 bytes, 32 bits, so a buffer of 5 bytes takes up 8 bytes (2 words) of memory, And 10 bytes of the buffer will take 12 bytes (3 words) of memory space.This is why the SP to lose 20 reasons so that we can imagine function () is called when the stack looks like:
So, from the above chart, if we input buffer1 long, and directly overwrite the back of the sfp and ret, you can modify the function of the return address. Let’s look at an example.
Examples
On how to prepare Shell Code, how to prepare in advance a dangerous memory in the implementation of the code and how to accurately calculate the implementation of the buffer overflow that period of dangerous code while the return address back to the original return address … … This involves too much of the underlying Compilation of knowledge, brother Fucai also just fly, can not become a real hacker master. However, the level of the level of hacker friends seems to improve our code security is very necessary!
So, in this example, we assume that the so-called dangerous code is already in the source code, the function bar. Function foo is a normal function, the main function is called, the implementation of a very unsafe strcpy work. Using unsafe strcpy, we can pass a buffer over the length of the buf string, the implementation of the copy, the buffer overflow, the ret return address into the address of the function bar, to call the purpose of the function bar.
Compile the above program with GCC, and turn off the Buffer Overflow Protect switch:
In order to find out the return address, I use gdb to debug the above-compiled program.
Therefore, as long as we enter a long string, covering 0x08048499, become bar function address 0x8048419, to achieve the purpose of calling the bar function.To enter something like 0x8048419 into the application, we need to use a Perl or Python script, such as the following Python script:
import os
arg = ‘ABCDEFGHIJKLMN’ + ‘”x19″x84″x04″x08’
cmd = ‘./test ‘ + arg
os.system(cmd)
Note that the above 08 04 84 19 to two anti-written. Do the following:
$python hack.py
Address of foo = 0x80483d4
Address of bar = 0x8048419
My stack looks like:
0xb7fc24e0
0x8048616
0xbf832484
0xbf832484
0xb7fc1ff4
0xbf832498
0x8048499
buf = ABCDEFGHIJKLMN
Now the stack looks like:
0xbf83246e
0x8048616
0x42412484
0x46454443
0x4a494847
0x4e4d4c4b
0x8048419
Heap Overflows
A heap is an area of memory that is used by the application and is dynamically allocated at runtime. Heap memory is different from stack memory in that it is more persistent between functions. This means that the memory allocated to a function will remain allocated until it is completely freed. This indicates that a heap overflow may have occurred but has not been noticed until the memory segment is used later. Here is a simple look at the following to see one of the most simple examples of heap overflow:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *input = malloc(20);
char *output = malloc(20);
strcpy(output, “normal output”);
strcpy(input, argv[1]);
printf(“input at %p: %s\n”, input, input);
printf(“output at %p: %s\n”, output, output);
printf(“\n\n%s\n”, output);
}
We look at the implementation of the results:
[root@localhost]# ./heap1 hackshacksuselessdata
input at 0x8049728: hackshacksuselessdata
output at 0x8049740: normal outputnormal output
[root@localhost]# ./heap1 hacks1hacks2hacks3hacks4hacks5hacks6hacks7hackshackshackshackshackshackshacks
input at 0x8049728: hacks1hacks2hacks3hacks4hacks5hacks6hacks7hackshackshackshackshackshackshacks
output at 0x8049740: hackshackshackshacks5hacks6hacks7hackshacks5hackshacks6hackshacks7
[root@localhost]# ./heap1 “hackshacks1hackshacks2hackshacks3hackshacks4what have I done?”
input at 0x8049728: hackshacks1hackshacks2hackshacks3hackshacks4what have I done?
output at 0x8049740: what have I done?
Formatted string error
This error is the use of printf, sprintf, fprint and other functions, the format is not used string, for example the correct usage is:
printf(“%s”, input)
If written directly:
There will be loopholes, when the input of some illegally manufactured characters, the memory will be rewritten, the implementation of some illegal instructions.
Unicode and ANSI buffer sizes do not match
We often encounter the need to convert between Unicode and ANSI, the vast majority of Unicode functions in accordance with the wide character format (double byte) size, rather than in accordance with the byte size to calculate the size of the buffer, so when the conversion is not careful It may cause overflow. For example, the most commonly attacked function is MultiByteToWideChar, see the following code:
BOOL GetName(char *szName)
{
WCHAR wszUserName[256];
// Convert ANSI name to Unicode.
MultiByteToWideChar(CP_ACP, 0,
szName,
-1,
wszUserName,
sizeof(wszUserName));
}
WszUserName is wide, so sizeof (wszUserName) will be 256 * 2 bytes, so there is a potential buffer overflow problem. The correct wording should be this:
MultiByteToWideChar(CP_ACP, 0,
szName,
-1,
wszUserName,
sizeof(wszUserName) / sizeof(wszUserName[0]));
The Internet Print Protocol Buffer Overflow has been the real cause of such problems.
Prevention and detection
- Unsafe function
Avoid the use of unsafe string handling functions, such as the use of safe functions instead:Insecure function Safety function strcpy strncpy strcat strncat sprint _snprintf gets fgets
- Visual C ++ .NET / GS Options
/GS option can prevent the destruction of the stack to ensure the integrity of the stack, but can not completely prevent the buffer overflow problem, for example, for heap overflow, / GS is powerless. - Source code scanning
The simplest source code scan:grep strcpy *.c