堆溢出 arbitrary DWORD reset 入门
0.引言
arbitrary DWORD reset 在0day安全中叫做DWORD SHOOT,本文是参考《0 day安全》第5章的学习笔记。
arbitrary DWORD reset的攻击原理是,系统采用双向链表维护空闲堆块,通过溢出修改空闲堆块首部的pre和next指针,最后在申请该块时达到任意修改内存的目的。下面使用 的方法是:修改PEB 中的一个函数指针(进程结束时会调用),使它指向我们构造的shellcode,这样进程结束前会去执行shellcode.
1.双向链表组织空闲堆块
空闲堆块用双向链表组织,每个空闲堆块首部占16字节,前8个字节用于标识该块的相关信息(如块大小、是否占用等),后8字节用于存储pre和next两个指针。
当申请一个空闲堆块时,从双向链表删除的过程大致如下:
int deleteNode(ListNode *node)
{
node->next->pre = node->pre;
node->pre->next = node->next;
}
2.arbitrary DWORD reset
2.1.覆盖空闲堆首部
假定一个堆A是已经申请的,堆B在A后面,是空闲的,如|—A—|—B—| 在向堆A中拷贝数据时,数据长度超出了堆A的大小,那么将覆盖堆B的首部,破坏了空闲堆双向链表结构。
B->pre = (ListNode*)addr_x
B->next = (ListNode*)addr_y
2.2.申请被覆盖的空闲堆
这时系统还是将堆块B从双向链表中删除(系统不知道双向链表结构被破坏),模拟deleteNode操作
B->next->pre = B->pre
B->pre->next = B->next
也就是
((ListNode*)addr_y)->pre = (ListNode*)addr_x
((ListNode*)addr_x)->next = (ListNode*)addr_y
在addr_y开始的0~3字节处 存储addr_x 在addr_x开始的4~7字节处 存储addr_y
addr_y[0~3] = addr_x
addr_x[4~7] = addr_y
(注意ListNode不是空闲堆块的头部)
3.实例分析
3.1. 获取shellcode的起始地址,也就是堆块h1的地址
在源程序加入断点,OD打开exe程序,F9执行到断点处,发现EAX:0×00360688,即shellcode的起始地址,F8单步调试,执 行完REP MOVS DWORD….指令,它对应memcpy函数,可在内存0×00360688处看到shellcode的内容。
3.2. 获取RtlEnterCriticalSection()函数的地址,两个方法
1)在OllyDbg汇编区,Ctrl+G输入RtlEnterCriticalSection查找函数的地址; 2)《0day安全》书中说存储该函数指针的位置是固定的,0x7FFDF020,也可以在OD下方的内存区Ctrl+G输入7FFDF020查找该内存地址存储的内容,地址是 0x77f89103。
3.3. shellcode分析
shellcode有216字节,前200字节填充 大小为200字节的堆区h1,它后面紧跟着就是空闲堆块
201~208字节存放堆块首部
209~212字节存放shellcode的起始地址,需要实际调试确定,本实验是0×00360688
213~216字节存放P.E.B函数指针的地址,即0x7FFDF020
3.4. 溢出
当执行完memcpy后,再申请空闲堆时,申请的是被覆盖的堆B
B->next = 0×00360688
B->pre = 0x7FFDF020
在从空闲堆双向链表中删除该堆块时,会在0×00360688处第4~7字节写入0x7FFDF020,这是shellcode前12字节为NOP的原因;
在0x7FFDF020处第0~3字节写入0×00360688,进程结束时想去执行ExitProcess()时,会转而执行我们的shellcode。
在执行shellcode时,为了让shellcode结束后调用ExitProcess(),在shellcode中又将0x77f89103写回0x7FFDF020处。
注:
实验环境 winodws 2000 sp4,参考《0 day安全第二版》
留下评论