堆溢出
介绍
堆溢出是指程序向某个堆块中写入的字节数超过了堆块本身可使用的字节数(之所以是可使用而不是用户申请的字节数,是因为堆管理器会对用户所申请的字节数进行调整,这也导致可利用的字节数都不小于用户申请的字节数),因而导致了数据溢出,并覆盖到**物理相邻的高地址**的下一个堆块。
不难发现,堆溢出漏洞发生的基本前提是
- 程序向堆上写入数据。
- 写入的数据大小没有被良好地控制。
堆溢出是一种特定的缓冲区溢出(还有栈溢出, bss 段溢出等)。但是其与栈溢出所不同的是,堆上并不存在返回地址等可以让攻击者直接控制执行流程的数据,因此我们一般无法直接通过堆溢出来控制 EIP 。一般来说,我们利用堆溢出的策略是
-
覆盖与其**物理相邻的下一个 chunk** 的内容。
- prev_size
- size,主要有三个比特位,以及该堆块真正的大小。
- NON_MAIN_ARENA
- IS_MAPPED
- PREV_INUSE
- the True chunk size
- chunk content,从而改变程序固有的执行流。
-
利用堆中的机制(如 unlink 等 )来实现任意地址写入( Write-Anything-Anywhere)或控制堆块中的内容等效果,从而来控制程序的执行流。
基本示例
下面我们举一个简单的例子:
#include <stdio.h>
int main(void)
{
char *chunk;
chunk=malloc(24);
puts("Get input:");
gets(chunk);
return 0;
}
这个程序的主要目的是调用 malloc 分配一块堆上的内存,之后向这个堆块中写入一个字符串,如果输入的字符串过长会导致溢出 chunk 的区域并覆盖到其后的 top chunk 之中 (实际上 puts 内部会调用 malloc 分配堆内存,覆盖到的可能并不是 top chunk)。
0x602000: 0x0000000000000000 0x0000000000000021 <===chunk
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000020fe1 <===top chunk
0x602030: 0x0000000000000000 0x0000000000000000
0x602040: 0x0000000000000000 0x0000000000000000
print 'A'*100 进行写入
0x602000: 0x0000000000000000 0x0000000000000021 <===chunk
0x602010: 0x4141414141414141 0x4141414141414141
0x602020: 0x4141414141414141 0x4141414141414141 <===top chunk(已被溢出)
0x602030: 0x4141414141414141 0x4141414141414141
0x602040: 0x4141414141414141 0x4141414141414141