安全中国首页 > 文章中心 > 溢出文章
 
安全中国网友投稿专用上传FTP空间:
Ftp服务器:download.anqn.com
Ftp端口:21
用户名:anqn
密 码:anqn.com
 

通过覆盖__atexit进行缓冲区溢出攻击

更新时间:2007-11-22 0:58:47
责任编辑:流火
热 点:
  

当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]$Content$nbsp;gcc -o at at.c 
pb@nod [406]$Content$nbsp;./at 
__atexit at 0x280e46a0 
malloced at 0x804b000 
static at 0x8049660 

pb@nod [407]$Content$nbsp;gcc -o at -static at.c 
pb@nod [408]$Content$nbsp;./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]); 

上一页 1 2 3 下一页

 
相关文章
一日一文章
 
一日一软件
一日一动画