堆溢出和use after free的差异

文章译自:http://www.thegreycorner.com/2010/03/difference-between-heap-overflow-and.html

由于水平有限,大家可以去看看原文,本篇文章在后面加了一些小程序,方便大家理解。


前几天,我收到一个来自我博客读者的问题,他想问下关于堆溢出和use after free 漏洞之间的区别。我想了一下,这将是个很好的话题,因此我写下了这篇文章。

        

现在,我在回答这个问题之前,我想先说下内存管理机制,以及一些相关的理论。

        

内存管理中有两种重要的数据结构——栈和堆。栈是一种后进先出的结构,主要用作存储函数的局部变量和一些函数调用的数据。栈工作时想一堆盘一样,以至于你只能从栈的顶部处加入数据,或者移除数据。如果你想移除掉顶部第二个“盘子”,你必须先把它上面的先移走。栈是一个非常简单的结构,在X86处理器中有寄存器和指令,以及汇编语言来管理和访问。

        

堆是进程用作存储全局变量或者是栈上空间不足以满足变量的大小进行存储的内存空间。堆在x86处理器和汇编语言中没有专用的寄存器和指令,而是高级别的函数进行管理的,这些函数一遍是由C语言写的,包含在windows,GNU/linux等系统中。

        

从溢出的角度来说,由于栈和堆管理的差异使得当 OllyDbg 等汇编级调试器比较栈和堆变化时,难以跟上堆的变化,并且是那些基于堆的溢出变的更加难以使用,相对于基于栈的溢出。然而,这些增加了对管理机制的复杂度,也同样会提供不同类型的可利用的漏洞出现。所以,除了在堆和栈中的缓冲区溢出之外,堆中还存在一种叫做 use after free的漏洞。

        

考虑到use after free 这种类型的漏洞的出现时由于堆管理机制额外引进的复杂的管理机制所造成。我们必须首先理解堆是如何工作的,理解use after free 和堆溢出的漏洞,并且了解它们之间的差异。我们将我们将使用Windows操作系统的堆管理方法作为一个例子。

        

 在windows 中,每个进程都有一个或者多个堆用作存储程序数据,包括默认的进程堆和其它的可动态申请指定大小的,。动态申请堆可通过HeapCreate() 函数申请。每个堆可以有任意大小的堆块,可以被malloc(), HeaoAlloc(), LocalAlloc(), GlobalAlloc() 或者RtkAllocateHeap()申请分配空间。

        

既然有申请空间,同样释放也是必不可少的,为了让内存空间能够重复利用,当不在需要时。HeapFree()和HeapReAlloc()等函数是用做在堆中释放和重新申请分配堆空间的。

        

当然还需要一个能够跟踪在堆中申请分配空间的函数或者方法。这些都可以在堆基址的一大片的数据结构中找到,并且有一个记录着每个堆块的入口地址,用做标识哪些堆块已经分配的。每个已分配和未分配的堆块都有一个块首,上面提到的各种堆管理函数,申请和释放空间时都要使用这些结构进行操作。同样的,基于堆的溢出也是利用这些相同的堆管理函数来控制CPU去破坏堆的结构。

        

 特别地,堆溢出是通过堆块的溢出和重写下一个堆块的块首进行工作的,而下一个堆块中可能包含其它的堆的入口地址或者已经释放的堆的节块(每个都包含前面所述的块首)。之后,当堆管理器操作堆块时(比如说申请一个新的堆块),使的操作的空间指向了重写的堆块,这个错位的块首是可以被头管理函数访问的,但这将产生一个异常,在合适的条件下可以被利用。如前所述,由于利用这些漏洞包含操作堆管理的函数,需要特别注意的是,如果这些函数发生了改变(如在windows xp sp2 中引进的安全移除功能),使得一些堆溢出的编的exp更加难以利用了(或者不可能了)。


 Useafter free 的利用操作与堆溢出略有不同。首先需要一个指向堆块的地址通过申请(通过HeapAlloc),然后释放(HeapFree),之后在释放操作之后再次使用。当这些被释放了的堆块被利用时,可能会出现控制进程执行的机会。

        

程序:

Example1:

#include <stdio.h>
#include <unistd.h>
#define BUFSIZER1 512
#define BUFSIZER2 ((BUFSIZER1/2) - 8)
int main(int argc, char **argv) {
char *buf1R1;
char *buf2R1;
char *buf2R2;
char *buf3R2;
buf1R1 = (char *) malloc(BUFSIZER1);               //申请空间
buf2R1 = (char *) malloc(BUFSIZER1);               //申请空间
free(buf2R1);                                                 //释放
buf2R2 = (char *) malloc(BUFSIZER2);
buf3R2 = (char *) malloc(BUFSIZER2);
strncpy(buf2R1, argv[1], BUFSIZER1-1);             //这里释放之后,又利用了buf2R1
free(buf1R1);
free(buf2R2);
free(buf3R2);
}

Example2:

char* ptr = (char*)malloc (SIZE);
if (err) {
abrt = 1;
free(ptr);
}
...
if (abrt) {
logError("operation aborted before commit", ptr);
}
当一个错误发生时,立即释放指针。但是这个指针又错误的用在logError函数那。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值