Content: This article mainly describes the principles of buffer overflow and introduces the exploitation of Linux and Solaris Vulnerabilities Based on actual examples.
This article does not show you how to write shell code.
Requirements: readers should have a little knowledge of C and assembly language basics.
Goal: I hope this article will be easy to understand, so that friends with a little basic computer knowledge can manually write their Exploit
If you think you understand all this, please stop reading it.
Overview
1. How is Buffer Overflow produced?
Buffer overflow: buffer overflow. This means that the buffer used is too small to fit.
So many things come out, as if the water tank cannot hold so much water, too much will overflow ;)
So why does buffer need to be used in programming? The simple answer is to serve as a transfer station for data processing.
2. Function calling mechanism of C language in UNIX and utilization of buffer overflow.
1) process images in memory.
Assume that there isProgramThe function calling sequence is as follows.
Main (...)-> func_1 (...)-> func_2 (...)-> func_3 (...)
That is, the main function calls func_1, func_1, func_2, and func_3.
When a program is transferred to the memory for running by the operating system, the image of the corresponding process in the memory is shown in.
(Memory high address)
+ -------------------------------------- +
| ...... |... Omitted some areas that we do not need to care about
+ -------------------------------------- +
| Env strings (Environment Variable string) | \
+ -------------------------------------- + \
| Argv strings (command line string) | \
+ -------------------------------------- + \
| Env pointers | shell environment variable and Command Line Parameter Storage Area
+ -------------------------------------- +/
| Argv pointers (command line parameter pointer) |/
+ -------------------------------------- +/
| Argc (number of command line parameters) |/
+ -------------------------------------- +
| Stack frame of the main function | \
+ -------------------------------------- + \
| Stack frame of func_1 function | \
+ -------------------------------------- + \
| Stack frame of func_2 function | \
+ -------------------------------------- + \
| Stack frame of func_3 function | Stack)
+ ...................................... +/
|/
....../
|/
+ ...................................... +/
| Heap (HEAP) |/
+ -------------------------------------- +
| Uninitialised (BSS) Data | non-initialized data (BSS) Area
+ -------------------------------------- +
| Initialised data | initialize the data Zone
+ -------------------------------------- +
| Text | Partition
+ -------------------------------------- +
(Low memory address)
Note the following:
(I) as the number of function call layers increases, the function stack frame is extended by blocks to the memory address.
As the number of function call layers in a process decreases, that is, the return of each function call, the stack frame will block
It is abandoned and scaled back to the high address of the memory.
The stack frame size of each function varies with the function nature, and is determined by the number of local variables of the function.
Ii) the process's dynamic application for memory occurs in heap (HEAP). That is to say
Heap (HEAP) may extend to a high-or low-address due to the increase in the number of memory allocated to the process.
Same as the implementation of CPU, but generally it increases to the high address of memory.
Iii) When BSS data or stack growth consumes the free memory allocated to processes by the system,
The process will be blocked, and the operating system will use a larger memory module to schedule the operation again.
(Although it has nothing to do with exploit, it is good to know)
Iv) The stack frame of the function contains the function parameters (as for the parameters of the called function, they are placed on the stack of the called function ).
Frame or called function stack frame, depending on the implementation of different systems ),
Its local variables and the stack frame (that is, the previous stack frame) required to restore the function that calls this function
Data, including the address of the next instruction to call the function.
V) The non-initialized data (BSS) area is used to store static variables of the program. The memory is initialized to zero.
The initialization data area is used to store the initialization data in the executable file.
These two zones are collectively referred to as data zones.
Vi) text is a read-only zone. Any attempt to write to this zone will result in a segment violation.
It is shared by multiple processes that run the executable file. The worker area stores the program.Code.
2) function stack frame.
The stack frame created during function calling contains the following information:
I) return address of the function. Whether the return address is stored in the stack frame of the called function or the stack frame of the called function,
Depends on the implementation of different systems.
Ii) stack frame information of the call function, that is, stack top and stack bottom.
Iii) space allocated for the local variables of the Function
Iv) The space allocated for the parameters of the called function depends on the implementation of different systems.
3) Utilization of buffer overflow.
The stack frame structure of the function shows that:
Because the memory allocation of local variables of the function occurs in the stack frame, if we define
If the buffer variable is used, the memory space occupied by the buffer variable is the stack frame created when the function is called.
Because the potential operations on the buffer (such as string replication) are from low memory address to high address, and stored in the memory
The return address of the function call is usually above the buffer (high address)-This is determined by the stack characteristics
This provides conditions for the return address of the covering function. When we have the opportunity to use content larger than the target buffer size
When the buffer is filled, you can rewrite the return address of the function stored in the function stack frame, so that the program runs
The process is transferred with our intention. In other words, the process accepts our control. We can make the process
Change the original Execution Process to execute the code we have prepared.
This is a flaw in Von Norman's computer architecture.
Below is the buffer overflow exploitation:
I) The operations of the function on the string buffer are generally directed from the memory low address to the high address.
For example, strcpy (S, "Aaa .....");
S + 1 S + 2 S + 3...
+ --- + -------- + --- +... +
(Low memory address) | A | ...... | A |... | (high memory address)
+ --- + -------- + --- +... +
Ii) override of the return address of the Function
/| ...... | (High memory address)
/+ -------------------- +
Call function stack frame | 0x41414141 |
\ + -------------------- +
\ | 0x41414141 | return address of the called Function
\ + -------------------- +
/| ...... |
/+ -------------------- + S + 8
/| 0x41414141 |
/+ -------------------- + S + 4
Called function stack frame | 0x41414141 |
\ + -------------------- + S
0x41414141 |
\ + -------------------- +
\ | ...... |
+ ...... +
| ...... | (Low memory address)
Note: The hexadecimal ASCII value of character a is 0x41.
Iii) We can see that if we use an address that a process can access rather than 0x41414141
To rewrite the return address of the call function. This address is the entry to the code we have prepared.
The process will execute our code. Otherwise, if the address of the segment cannot be accessed by the process
Cause process crash-segment fault core dumped (segment error kernel dump); if this address has
Invalid machine command data will lead to invalid command (illigal instruction) errors, and so on.
4) buffer zone in heap or BBS
I) If the buffer memory space is obtained through dynamic application in the function (for example, applying using the malloc () function ),
In the stack frame of the function, only the pointer is allocated to the memory space requested by the heap (HEAP ).
In this case, overflow occurs in the (HEAP) Heap. If you want to overwrite the corresponding function return address, it seems almost impossible.
Yes. The possibility of using this situation depends on the specific situation, but it is not impossible.
Ii) if the buffer is defined as static in the function, the buffer memory space is located in the non-initialized (BBS) area,
It is similar to the situation in heap (HEAP). It is possible to use it. However, there is also a special situation where it can be used.
Override the function pointer so that the process can call the corresponding function to call the code we specified.
3. What can be obtained from the use of buffer overflow?
From the above, we can see that the use of buffer overflow allows us to rewrite the memory content and the return address of the function, thus
Change the Code Execution Process and let the process execute the code we have prepared.
However, the process runs as the currently logged-on user. How can we execute the code we have prepared? We also
Does not break through the system's permission settings for the current user, and cannot do beyond the permissions.
In other words, to use buffer overflow to obtain higher permissions, we have to take advantage of some features of the system.
For UNIX, two features are available.
I) SUID and SGID Program
UNIX allows other users to execute this operation with the user ID or user group ID of the file owner of an executable file.
File, which is implemented by setting the File Attribute of the executable file to suid or sgid.
That is to say, if an executable file is set to suid or sgid, when other users in the system execute this file
Run the file as the user or user group of the file owner.
If the owner of an executable file is root and the file is set to SUID
The buffer overflow vulnerability can be used to execute the code we have prepared as root.
It is more attractive to generate a shell with the root user identity for us, isn't it?
Ii) various port daemon (service) Processes
In UNIX, many daemon (service) processes run as root. If these programs have available buffer overflow,
Then we can let them run the code we have prepared as the current user -- root.
Because the daemon is already running as root, we do not need the suid or sgid attribute for the corresponding executable file.
Because such exploitation is usually caused by malicious data being sent from a remote machine to a port on the target machine, it is called
"Remote overflow" exploitation.
4. A problematic program
The following examples are purely fictitious. If there are similarities, it is a coincidence.
/*
* File name: P. C
* Compilation: gcc-o p. c
*/
# Include <stdio. h>
Void vulfunc (char * s)
{
Char Buf [10];
Strcpy (BUF, S );
Printf ("string = % s \ n", Buf );
}
Main (INT argc, char * argv [])
{
If (argc = 2)
{
Vulfunc (argv [1]);
}
Else
{
Printf ("Usage: % S <a string> \ n", argv [0]);
}
}
This routine receives the string input from the user in the command line, and then prints it out on the standard output (screen). We can see that in
The vulfunc () function defines a buffer Buf that can contain up to 10 characters. If we enter
If the string is less than or equal to 10 characters, everything is normal. But what if the length of the string we enter is greater than 10? Situation
What will happen? The buffer is too small to fit, so it overflows? The answer remains to be analyzed.
For the analysis and attack simulation of this program in different operating systems, see section 2 basics.
Part 2 Basics
5. Linux x86 Platform
This article uses the following Linux platform:
Red Hat Linux Release 6.2 (Zoot)
Kernel 2.2.14-12 on an i586
The compiler and version used:
Bash $ gcc-V
Reading specs from/usr/lib/GCC-lib/i386-redhat-linux/egcs-2.91.66/specs
GCC version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)
Note: The machine commands generated by different compilers for compiling the same code may be different.
1) analysis of routine p.c on Linux X86 platform.
I) First, we compile P. C and use GDB to decompile related functions.
The result is shown in the following list:
Bash $ gcc-o p. c
Bash $ GDB P
Gnu gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
Welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux "...
(GDB) disas main
Dump of worker er code for function main:
0x804842c <main>: Push % EBP
0x804842d <main + 1>: mov % ESP, % EBP
0x804842f <main + 3>: CMPL $0x2, 0x8 (% EBP)
0x8048433 <main + 7>: JNE 0x8048448 <main + 28>
0x8048435 <main + 9>: mov 0xc (% EBP), % eax
0x8048438 <main + 12>: add $0x4, % eax
0x804843b <main + 15>: mov (% eax), % edX
0x804843d <main + 17>: Push % edX
0x804843e <main + 18>: Call 0x8048400 <vulfunc>
0x8048443 <main + 23>: add $0x4, % ESP
0x8048446 <main + 26>: JMP 0x804845b <main + 47>
0x8048448 <main + 28>: mov 0xc (% EBP), % eax
0x804844b <main + 31>: mov (% eax), % edX
0x804844d <main + 33>: Push % edX
0x804844e <main + 34>: Push $ 0x80484bb
0x8048453 <main + 39>: Call 0x8048330 <printf>
0x8048458 <main + 44>: add $0x8, % ESP
0x804845b <main + 47>: Leave
0x804845c <main + 48>: Ret
0x804845d <main + 49>: NOP
0x804845e <main + 50>: NOP
0x804845f <main + 51>: NOP
End of worker er dump.
(GDB) disas vulfunc
Dump of handler code for function vulfunc:
0x8048400 <vulfunc>: Push % EBP
0x8048401 <vulfunc + 1>: mov % ESP, % EBP
0x8048403 <vulfunc + 3>: Sub $ 0xc, % ESP
0x8048406 <vulfunc + 6>: mov 0x8 (% EBP), % eax
0x8048409 <vulfunc + 9>: Push % eax
0x804840a <vulfunc + 10>: Lea 0xfffffff4 (% EBP), % eax
0x804840d <vulfunc + 13>: Push % eax
0x8048340 <vulfunc + 14>: Call 0 <strcpy>
0x8048413 <vulfunc + 19>: add $0x8, % ESP
0x8048416 <vulfunc + 22>: Lea 0xfffffff4 (% EBP), % eax
0x8048419 <vulfunc + 25>: Push % eax
0x804841a <vulfunc + 26>: Push $0x80484b0
0x804841f <vulfunc + 31>: Call 0x8048330 <printf>
0x8048424 <vulfunc + 36>: add $0x8, % ESP
0x8048427 <vulfunc + 39>: Leave
0x8048428 <vulfunc + 40>: Ret
0x8048429 <vulfunc + 41>: Lea 0x0 (% Esi), % ESI
End of worker er dump.
Here we only perform disassembly Analysis on the Main and vulfunc functions of interest.
Ii) process running and In-memory Analysis
We use GDB to track how the process runs in the memory.
First, call the program.
Bash $ GDB P
Gnu gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
Welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux "...
(GDB)
Set the breakpoint to the first executable Assembly command of main.
(GDB) B * 0x804842c
Breakpoint 1 at 0x804842c
Run the program
(GDB) r aaaaaaaa
Starting program:/home/vcat/P aaaaaaaa
Breakpoint 1, 0x804842c in main ()
Stopped at the breakpoint.
Let's take a look at the values of each register.
(GDB) I reg
Eax 0x4010b3f81074836472
ECS x 0x804842c 134513708
EdX 0x4010d098 1074843800
EBX 0x4010c1ec 1074840044
ESP 0xbffff6bc-1073744196
EBP 0xbffff6d8-1073744168
ESI 0x4000ae60 1073786464
EDI 0 x bffff704-1073744124
EIP 0x804842c 134513708
Eflags 0x246 582
CS 0x23 35
SS 0x2b 43
DS 0x2b 43
Es 0x2b 43
FS 0x0 0
GS 0x0 0
CWD 0 x ffff037f-64641
SWD 0 x ffff0000-65536
TWD 0 xffffffff-1
FIP 0x40034d70 1073958256
FCS 0x35d0023 56426531
Fopo 0 x bfffe400-1073748992
Fos 0xffff002b-65493
What we care about here is the bottom stack (EBP), top stack (ESP) and command register (EIP ).
In this case, the EBP value is 0xbffff6d8, And the ESP value is 0xbffff6bc, with a difference of 28 bytes.
The EIP value is 0x804842c, which is exactly the breakpoint we set.
(Note: The value here may vary with the program running in different system environments)
Let's take a look at what is in the current stack frame?
(GDB) x/8x $ ESP
0xbffff6bc: 0x400349cb 0x00000002 0xbffff704 0xbffff710
0xbffff6cc: 0x40013868 0x00000002 0x08048350 0x00000000
That is to say, when the main function is called, the image of the relevant part of the process in the memory is as follows:
(Memory high address)
| ...... |
+ -------- +
| 1, 00000000 |
0xbffff6d8 + -------- + <-- EBP (EBP before calling the main function)
| 1, 08048350 |
+ -------- +
| 1, 00000002 |
+ -------- +
| 1, 40013868 |
+ -------- +
| Bffff710 |
+ -------- +
| Bffff704 |
+ -------- +
| 1, 00000002 |
+ -------- +
| 400349cb |
0xbffff6bc + -------- + <-- ESP (esp before calling the main function)
| ...... |
(Low memory address)
Let's take a look at what the following commands do?
0x804842c <main>: Push % EBP; esp value equals esp-4 (because EBP is 32 bits );
Put the EBP value into the 32-Bit Memory ticket referred to by ESP
; RMB (Note: Save the stack bottom here ).
0x804842d <main + 1>: mov % ESP, % EBP; the value of EBP is equal to the value of ESP (Note: here we set the original
; Stack top as the new stack bottom ).
run the two commands and check the register content and stack.
(GDB) Si
0x804842d in main ()
(GDB) Si
0x804842f in main ()
(GDB) I reg
eax 0x4010b3f8 1074836472
ECx 0x804842c 134513708
edX 0x4010d098 1074843800
EBX 0x4010c1ec 1074840044
ESP limit-1073744200
EBP 0xbffff6b8-1073744200
ESI 0x4000ae60 1073786464
EDI 0xbffff704-1073744124
EIP 0x804842f 134513711
eflags 0x346 838
CS 0x23 35
SS 0x2b 43
DS 0x2b 43
es 0x2b 43
FS 0x0 0
Gs 0x0 0
CWD 0xffff037f-64641
SWD 0xffff0000-65536
TWD 0 xffffffff-1
FIP 0x40034d70 1073958256
FCS 0x35d0023 56426531
fopo 0xbfffe400-1073748992
Fos 0xffff002b-65493
(GDB) x/9x $ ESP
0xbffff6b8: 0xbffff6d8 0x400349cb 0x00000002 0xbffff704
0xbffff6c8: 0xbffff710 0x40013868x00000002x08048350
Cost: 0x00000000
The process image is as follows:
(Memory high address)
| ...... |
+ -------- +
| 1, 00000000 |
0xbffff6d8 + -------- + <-- call the EBP before the main function
| 1, 08048350 |
+ -------- +
| 1, 00000002 |
+ -------- +
| 1, 40013868 |
+ -------- +
| Bffff710 |
+ -------- +
| Bffff704 |
+ -------- +
| 1, 00000002 |
+ -------- +
| 400349cb |
0xbffff6bc + -------- + <-- ESP before calling the main function
| Bffff6d8 |
0xbffff6b8 + -------- + <-- EBP, ESP
| ...... |
(Low memory address)
The following two commands:
0x804842f <main + 3>: CMPL $0x2, 0x8 (% EBP); 2 and EBP + 8 point to the memory (32-bit -- 4
; Byte.
0x8048433 <main + 7>: JNE 0x8048448 <main + 28>; jump to 0x08048448 if not supported
; Otherwise, execute the next command.
Here we can see that this is a C language statement
If (argc = 2)
{
...
}
Else
{
...
}
The memory address EBP + 8 stores the argc value.
(GDB) x/x $ EBP + 8
0xbffff6c0: 0x00000002
Let's take a look at the commands before calling the vulfunc function:
0x8048435 <main + 9>: mov 0xc (% EBP), % eax; four bytes of memory address EBP + 12
; Put the content in eax.
0x8048438 <main + 12>: add $0x4, % eax; eax equals eax + 4.
0x804843b <main + 15>: mov (% eax), % edX; points eax to the four memory byte units
.
0x804843d <main + 17>: Push % edX; esp is equal to the esp-4, put the value of edX to ESP
In four bytes of the memory address.
Let's see what EBP + 12 is on?
(GDB) x/x $ EBP + 12
0xbffff6c4: 0xbffff704
I suspect that this is the address pointing to the address of the argv [0] string. Check if it is correct.
(GDB) x/x 0xbffff704
0xbffff704: 0xbffff83e
(GDB) X/1 s 0xbffff83e
0xbffff83e: "/home/vcat/P"
Sure enough. Then, the four bytes of content referred to by $ EBP + 12 (the address of the argv [0] string) plus four should point
The argv [1] string address.
(GDB) x/x 0xbffff704 + 4
0xbffff708: 0xbffff856
(GDB) X/1 s 0xbffff856
0xbffff856: "aaaaaaaa"
We can see that these four commands are used to calculate the starting address of argv [1] (that is, the input string "aaaaaaaa" in the memory ),
Push the address to the stack as a parameter and pass it to the function vulfunc to be called.
Set a breakpoint at 0x804843e so that the program can continue to run until the vulfunc function is called.
(GDB) B * 0x804843e
Breakpoint 2 at 0x804843e
(GDB) c
Continuing.
(GDB) I reg
eax 0xbffff708-1073744120
ECx 0x804842c 134513708
edX 0xbffff856-1073743786
EBX 0x4010c1ec 1074840044
ESP 0xbff6b4-1073744204
EBP & n ...........