详细分析Pwn2Own 2019上曝出的Edge的Canvas 2D API漏洞(CVE-2019-0940)利用

xiaohui 漏洞 2019年5月28日发布
Favorite收藏

导语:Pwn2Own 2019再次向世界证明了世界上没有一个完全安全的系统。

Pwn2Own 2019再次向世界证明了世界上没有一个完全安全的系统。黑客通过访问特制的页面,已经成功入侵了macOS上的Safari、Windows 10上的Edge和Firefox浏览器,甚至还从两个虚拟机上逃脱在本地硬件上运行代码。

今天我们就以Microsoft Edge浏览器为例,说一说黑客团队是如何发现其中的漏洞,并成功运用它的。该漏洞利用包括两大块:

1. Edge的渲染器重复释放 (double-free)漏洞利用,实现任意读写;

2.沙箱逃逸的逻辑漏洞利用,实现拥有介质完整性等级(Medium Integrity Level)的任意代码执行;

由于篇幅所限,这篇文章只会介绍利用Microsoft Edge 64位渲染器过程中利用无双重漏洞的情况。沙箱逃逸的逻辑漏洞利用会在下一篇介绍。

重复释放漏洞

该漏洞位于Canvas 2D API组件中,该组件负责创建Canvas模式。重复释放漏洞是由以下JavaScript代码触发的:

1.jpg

如果你运行这个测试用例,你可能会注意到漏洞并不总是发生,可能需要多次尝试才会出现一次漏洞利用。启用pageheap后,漏洞将如下所示:

(470.122c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
edgehtml!TDispResourceCache::Remove+0x60:
00007ffd`2e5cd820 834708ff        add     dword ptr [rdi+8],0FFFFFFFFh ds:00000249`2681fff8=????????
0:016> r
rax=000002490563a4a0 rbx=0000000000000000 rcx=0000000000000000
rdx=0000000000000000 rsi=000000798c7fa710 rdi=000002492681fff0
rip=00007ffd2e5cd820 rsp=000000798c7fa680 rbp=0000000000000000
 r8=0000000000000000  r9=0000024909747758 r10=0000000000000000
r11=0000000000000025 r12=00007ffd2e999310 r13=0000024904993930
r14=0000024909747758 r15=0000000000000002
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
edgehtml!TDispResourceCache::Remove+0x60:
00007ffd`2e5cd820 834708ff        add     dword ptr [rdi+8],0FFFFFFFFh ds:00000249`2681fff8=????????
0:016> k L7
 # Child-SP          RetAddr           Call Site
00 00000079`8c7fa680 00007ffd`2e5c546d edgehtml!TDispResourceCache<CDispNoLock,1,0>::Remove+0x60
01 00000079`8c7fa6b0 00007ffd`2f054ad8 edgehtml!CDXSystemShared::RemoveDisplayResourceFromCache+0x6d
02 00000079`8c7fa710 00007ffd`2f054b54 edgehtml!CCanvasPattern::~CCanvasPattern+0x34
03 00000079`8c7fa740 00007ffd`2e7ac4d9 edgehtml!CCanvasPattern::`vector deleting destructor'+0x14
04 00000079`8c7fa770 00007ffd`2eb2703c edgehtml!CBase::PrivateRelease+0x159
05 00000079`8c7fa7b0 00007ffd`2f053584 edgehtml!TSmartPointer<CCanvasPattern,CStrongReferenceTraits,CCanvasPattern * __ptr64>::~TSmartPointer<CCanvasPattern,CStrongReferenceTraits,CCanvasPattern * __ptr64>+0x18
06 00000079`8c7fa7e0 00007ffd`2f050755 edgehtml!CCanvasRenderingProcessor2D::CreatePatternInternal+0xd8
0:016> ub @rip;u @rip
edgehtml!TDispResourceCache::Remove+0x46:
00007ffd`2e5cd806 488b742440      mov     rsi,qword ptr [rsp+40h]
00007ffd`2e5cd80b 488b7c2448      mov     rdi,qword ptr [rsp+48h]
00007ffd`2e5cd810 4883c420        add     rsp,20h
00007ffd`2e5cd814 415e            pop     r14
00007ffd`2e5cd816 c3              ret
00007ffd`2e5cd817 488b7808        mov     rdi,qword ptr [rax+8]
00007ffd`2e5cd81b 4885ff          test    rdi,rdi
00007ffd`2e5cd81e 74d5            je      edgehtml!TDispResourceCache<CDispNoLock,1,0>::Remove+0x35 (00007ffd`2e5cd7f5)
edgehtml!TDispResourceCache::Remove+0x60:
00007ffd`2e5cd820 834708ff        add     dword ptr [rdi+8],0FFFFFFFFh
00007ffd`2e5cd824 488b0f          mov     rcx,qword ptr [rdi]
00007ffd`2e5cd827 0f85dbe04e00    jne     edgehtml!TDispResourceCache<CDispNoLock,1,0>::Remove+0x4ee148 (00007ffd`2eabb908)
00007ffd`2e5cd82d 48891f          mov     qword ptr [rdi],rbx
00007ffd`2e5cd830 488bd5          mov     rdx,rbp
00007ffd`2e5cd833 48890e          mov     qword ptr [rsi],rcx
00007ffd`2e5cd836 498bce          mov     rcx,r14
00007ffd`2e5cd839 e8b2f31500      call    edgehtml!CHtPvPvBaseT<&nullCompare,HashTableEntry>::Remove (00007ffd`2e72cbf0)
0:016> !heap -p -a @rdi
    address 000002492681fff0 found in
    _DPH_HEAP_ROOT @ 2497e601000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                249259795b0:      2492681f000             2000
    00007ffd51857608 ntdll!RtlDebugFreeHeap+0x000000000000003c
    00007ffd517fdd5e ntdll!RtlpFreeHeap+0x000000000009975e
    00007ffd5176286e ntdll!RtlFreeHeap+0x00000000000003ee
    00007ffd2e5cd871 edgehtml!TDispResourceCache<CDispNoLock,1,0>::CacheEntry::`scalar deleting destructor'+0x0000000000000021
    00007ffd2e5cd846 edgehtml!TDispResourceCache<CDispNoLock,1,0>::Remove+0x0000000000000086
    00007ffd2e5c546d edgehtml!CDXSystemShared::RemoveDisplayResourceFromCache+0x000000000000006d
    00007ffd2f054ad8 edgehtml!CCanvasPattern::~CCanvasPattern+0x0000000000000034
    00007ffd2f054b54 edgehtml!CCanvasPattern::`vector deleting destructor'+0x0000000000000014
    00007ffd2e7ac4d9 edgehtml!CBase::PrivateRelease+0x0000000000000159
    00007ffd2e89f579 edgehtml!CJScript9Holder::CBaseFinalizer+0x00000000000000a9
    00007ffd2de66f5d chakra!Js::CustomExternalObject::Dispose+0x000000000000002d
    00007ffd2de3c012 chakra!Memory::SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>::ForEachPendingDisposeObject<<lambda_37407f4cdaf1d704a79fcdd974872764> >+0x0000000000000092
    00007ffd2de3bf0b chakra!Memory::HeapInfo::DisposeObjects+0x000000000000013b
    00007ffd2de81faa chakra!Memory::Recycler::DisposeObjects+0x0000000000000096
    00007ffd2de81e9a chakra!ThreadContext::DisposeObjects+0x000000000000004a
    00007ffd2dd5ac35 chakra!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000000000003a5
    00007ffd2dea7956 chakra!amd64_CallFunction+0x0000000000000086
    00007ffd2dd5f9d0 chakra!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >+0x00000000000002f0
    00007ffd2dd5fac8 chakra!Js::InterpreterStackFrame::OP_ProfiledCallIWithICIndex<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > >+0x00000000000000b8
    00007ffd2dd5fd41 chakra!Js::InterpreterStackFrame::ProcessProfiled+0x0000000000000161
    00007ffd2dd48a21 chakra!Js::InterpreterStackFrame::Process+0x00000000000000e1
    00007ffd2dd486ff chakra!Js::InterpreterStackFrame::InterpreterHelper+0x000000000000088f
    00007ffd2dd4775e chakra!Js::InterpreterStackFrame::InterpreterThunk+0x000000000000004e
    00000249226f1fb2 +0x00000249226f1fb2

漏洞分析

Javascript createPattern()触发本机CCanvasRenderingProcessor2D::CreatePatternInternal()调用:

3.jpg

在第21行上,堆管理器为Canvas模式对象分配空间,在接下来的行中,某些成员被设置为0。注意CCanvasPattern::Data成员填充在第28行,这一点很重要。

接下来是对CCanvasRenderingProcessor2D::EnsureBitmapRenderTarget()方法的调用,该方法负责为目标设备上的Canvas模式对象分配视频内存。在某些情况下,该方法返回一个错误。对于重复释放漏洞来说,当Windows GDI D3DKMTCreateAllocation()返回错误STATUS_GRAPHICS_NO_VIDEO_MEMORY(错误代码0xc01e0100)时,将触发该错误。将Canvas对象的宽度和高度设置为巨大的值会导致视频设备返回内存不足的错误。下面的调用堆栈显示了Canvas对象的宽度和高度被设置为较大值之后以及在连续调用createPattern()之后所采用的路径:

Breakpoint 1 hit
GDI32!D3DKMTCreateAllocation:
00007ffe`67a72940 48895c2420      mov     qword ptr [rsp+20h],rbx ss:000000b3`f59f82b8=000000000000b670
0:015> k
 # Child-SP          RetAddr           Call Site
00 000000b3`f59f8298 00007ffe`61fd598e GDI32!D3DKMTCreateAllocation
01 000000b3`f59f82a0 00007ffe`61fd39b5 d3d11!CallAndLogImpl<long (__cdecl*)(_D3DKMT_CREATEALLOCATION * __ptr64),_D3DKMT_CREATEALLOCATION * __ptr64>+0x1e
02 000000b3`f59f8300 00007ffe`605a1b4f d3d11!NDXGI::CDevice::AllocateCB+0x105
03 000000b3`f59f84c0 00007ffe`605a24dc vm3dum64_10+0x1b4f
04 000000b3`f59f8540 00007ffe`605ab258 vm3dum64_10+0x24dc
05 000000b3`f59f86a0 00007ffe`605ac163 vm3dum64_10!OpenAdapterWrapper+0x1b8c
06 000000b3`f59f8750 00007ffe`61fc3ce2 vm3dum64_10!OpenAdapterWrapper+0x2a97
07 000000b3`f59f87d0 00007ffe`61fc3a13 d3d11!CResource<ID3D11Texture2D1>::CLS::FinalConstruct+0x2b2
08 000000b3`f59f8b70 00007ffe`61fb98ba d3d11!TCLSWrappers<CTexture2D>::CLSFinalConstructFn+0x43
09 000000b3`f59f8bb0 00007ffe`61fbd107 d3d11!CDevice::CreateLayeredChild+0x2bca
0a 000000b3`f59fa410 00007ffe`61fbcf73 d3d11!NDXGI::CDeviceChild<IDXGIResource1,IDXGISwapChainInternal>::FinalConstruct+0x43
0b 000000b3`f59fa480 00007ffe`61fbca1c d3d11!NDXGI::CResource::FinalConstruct+0x3b
0c 000000b3`f59fa4d0 00007ffe`61fbd3c0 d3d11!NDXGI::CDevice::CreateLayeredChild+0x1bc
0d 000000b3`f59fa640 00007ffe`61fb43bb d3d11!NOutermost::CDevice::CreateLayeredChild+0x1b0
0e 000000b3`f59fa820 00007ffe`61fb297c d3d11!CDevice::CreateTexture2D_Worker+0x4cb
0f 000000b3`f59fade0 00007ffe`46cd68db d3d11!CDevice::CreateTexture2D+0xac
10 000000b3`f59fae70 00007ffe`46cd3dcd edgehtml!CDXResourceDomain::CreateTexture+0xfb
11 000000b3`f59faf20 00007ffe`46cd3d5e edgehtml!CDXSystem::CreateTexture+0x59
12 000000b3`f59faf70 00007ffe`46ed2dda edgehtml!CDXTextureTargetSurface::OnEnsureResources+0x15e
13 000000b3`f59fb010 00007ffe`46ed2e78 edgehtml!CDXTargetSurface::EnsureResources+0x32
14 000000b3`f59fb050 00007ffe`46ed2c71 edgehtml!CDXRenderTarget::EnsureResources+0x68
15 000000b3`f59fb0a0 00007ffe`46da4ba4 edgehtml!CDXRenderTarget::BeginDraw+0x81
16 000000b3`f59fb100 00007ffe`470180b5 edgehtml!CDXTextureRenderTarget::BeginDraw+0x34
17 000000b3`f59fb170 00007ffe`46cd8033 edgehtml!CDispSurface::BeginDraw+0xf5
18 000000b3`f59fb1d0 00007ffe`46cd7fa6 edgehtml!CCanvasRenderingProcessor2D::OpenBitmapRenderTarget+0x6b
19 000000b3`f59fb230 00007ffe`47831881 edgehtml!CCanvasRenderingProcessor2D::EnsureBitmapRenderTarget+0x52
1a 000000b3`f59fb260 00007ffe`4782eaa5 edgehtml!CCanvasRenderingProcessor2D::CreatePatternInternal+0x85
1b 000000b3`f59fb2c0 00007ffe`47539d46 edgehtml!CCanvasRenderingContext2D::Var_createPattern+0xc5
1c 000000b3`f59fb330 00007ffe`47174135 edgehtml!CFastDOM::CCanvasRenderingContext2D::Trampoline_createPattern+0x52
1d 000000b3`f59fb380 00007ffe`464dc47e edgehtml!CFastDOM::CCanvasRenderingContext2D::Profiler_createPattern+0x25
0:015> pt
GDI32!D3DKMTCreateAllocation+0x18e:
00007ffe`67a72ace c3              ret
0:015> r
rax=00000000c01e0100 rbx=000000b3f59f8508 rcx=1756445c6ae30000
rdx=0000000000000000 rsi=0000000000000000 rdi=00007ffe62186ae0
rip=00007ffe67a72ace rsp=000000b3f59f8298 rbp=000000b3f59f8530
 r8=000000b3f59f81c8  r9=000000b3f59f84e0 r10=0000000000000000
r11=0000000000000246 r12=0000000000000000 r13=0000000000000000
r14=000002ae9f3326c8 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=00000202
GDI32!D3DKMTCreateAllocation+0x18e:
00007ffe`67a72ace c3              ret

要触发错误,就得具备一个前提,目标硬件具有集成视频卡或具有低内存的视频卡。这些条件在VMWare图形虚拟化硬件或一些预算设备上都可以满足。不过,这可能触发不依赖于目标硬件资源的其他错误。

在正常情况下(即调用CCanvasRenderingProcessor2D::EnsureBitmapRenderTarget()方法不会返回任何错误),会调用CCanvasPattern::Initialize()方法:

5.jpg

在第40行,Canvas模式对象成员之一被设置为指向CCanvasPattern::Data对象。

在调用CCanvasPattern::InitializeFromCanvas()方法期间,会执行一系列调用,这最终会导致调用以下方法:

6.jpg

上述方法将显示资源添加到缓存中,在当前情况下,显示资源是DXImageRenderTarget对象,缓存是在TDispResourceCache类中实现的哈希表。

在第32行,会发生对TDispResourceCache< cdisnolock,1,0>::Add()方法的调用:

7.jpg

第27行被分配了易受攻击的对象,要注意,该对象不是通过MemGC机制分配的。

哈希表项由键值对(Key-Value Pair) 组成,键是CCanvasPattern::Data,数据对象的值是DXImageRenderTarget。哈希表的初始大小允许它最多容纳29个项,但其实该表需要37个项的空间。因此,需要额外的表项来减少可能的哈希冲突。每个键都应用一个哈希函数来推断哈希表中的位置。当哈希表被填满时,调用CHtPvPvBaseT <&int nullCompare(…),HashTableEntry> :: Grow()方法以增加哈希表的容量。在此调用期间,键值对将被移动到新的索引,键将从以前的位置删除,但值仍然保留。如果在容量增长之后必须删除键值对(例如释放Canvas模式对象),则释放该值,并且仅在新位置删除键值。

当表项数量低于某个值时,调用CHtPvPvBaseT<&int nullCompare(…),HashTableEntry>::Shrink()方法来减少哈希表的容量。当CHtPvPvBaseT<&int nullCompare(…)、HashTableEntry>::Shrink()方法被调用时,键值对被移动到先前的位置。

当释放Canvas模式对象时,通过以下方法调用删除包含相应CCanvasPattern::Data对象的哈希表项:

__int64 __fastcall TDispResourceCache<CDispNoLock,1,0>::Remove(
    __int64 resourceCache,
    __int64 a2,
    _QWORD *a3
)
{
    __int64 entries; // r14
    unsigned int hr; // ebx
    _QWORD *savedPtr_out; // rsi
    __int64 entryKey; // rbp
    HashTableEntry *hashTableEntry; // rax
    VulnObject *freedObject; // rdi
    bool doFreeObject; // zf
    __int64 savedPtr; // rcx
    void *v12; // rdx
 
    entries = resourceCache + 0x10;
    hr = 0;
    *a3 = 0i64;
    savedPtr_out = a3;
    entryKey = a2;
    hashTableEntry = CHtPvPvBaseT<&int nullCompare(void const *,void const *,void const *,bool),HashTableEntry>::FindEntry((resourceCache + 0x10), a2);
    if ( hashTableEntry && (freedObject = hashTableEntry->value) != 0i64 )
    {
        doFreeObject = LODWORD(freedObject->refCounter)-- == 0;
        savedPtr = freedObject->ptrToDXImageRenderTarget;
        if ( doFreeObject )
        {
            freedObject->ptrToDXImageRenderTarget = 0i64;
            *savedPtr_out = savedPtr;
            CHtPvPvBaseT<&int nullCompare(void const *,void const *,void const *,bool),HashTableEntry>::Remove(entries, entryKey);
            TDispResourceCache<CDispSRWLock,1,1>::CacheEntry::`scalar deleting destructor`(freedObject, v12);
        }
        else
        {
            *savedPtr_out = savedPtr;
            (*(*savedPtr + 8i64))(savedPtr);
        }
    }
    else
    {
        hr = 0x80004005;
    }
    return hr;
}

该方法通过调用CHtPvPvBaseT<&int nullCompare(…),HashTableEntry>::FindEntry() 方法检索哈希表项值。

如果对CCanvasRenderingProcessor2D::EnsureBitmapRenderTarget()的调用返回一个错误,Canvas模式对象就有一个未初始化的成员,该成员应该持有指向CCanvasPattern::Data对象的指针。尽管如此,canvas模式对象析构函数调用CHtPvPvBaseT<&int nullCompare(…),HashTableEntry>::FindEntry()方法,并提供一个nullptr键。如果有的话,该方法返回第一个值。如果哈希表先增长然后再减小,它将存储指向已释放的DXImageRenderTarget对象的指针。在这种情况下,TDispResourceCache< cdisnolock,1,0>::Remove()方法将对已释放的对象(变量freedObject)进行操作。

需要多次尝试才能触发漏洞,因为在第一个位置并不总是有表项。

可以通过以下两种方式利用此漏洞:

1.分配一些对象来代替已释放的对象并释放它,从而在几乎任意的对象上产生一个Use-after-free漏洞(简称UaF漏洞);

2.分配一些具有合适布局的对象(第一个quad-word必须是指向具有虚函数表的对象的指针) 来调用虚函数并导致诸如破坏一些有用数据的副作用;

选择第一种方法进行利用是因为很难找到适合第二种方法的对象。

漏洞利用

由于以下原因,该漏洞的攻击力显得非常具有攻击力:

1.Microsoft Edge在不同堆上分配不同大小和类型的对象,这减少了可用对象的数量;

2.释放的对象在使用LFH的默认Windows堆上分配,这使得不可能创建相邻的分配,并降低了成功覆盖对象的机会;

3.释放的对象是0x10字节,这种大小的对象通常用于内部维修,这使得相关堆区域繁忙,同时也降低了利用可靠性;

4.只有有限的0x10字节大小的LFH对象可以从Javascript中获得,而且实际上是有用的;

5.Javascript中可用于控制的对象只允许有限的控制;

6.在利用过程中使用的任何对象都不允许直接破坏任何字段,从而导致有用的效果(例如可控写入);

7.需要多个小堆分配和释放来获得对具有感兴趣字段的对象的控制。

渲染器利用过程的高级概述:

1.已准备好堆,并对攻击所需的对象进行喷洒;

2.所有0x10字节的DXImageRenderTarget对象都被释放(其中一个对象将再次被释放);

3.喷洒音频缓冲对象,这也会创建具有任意大小和内容的0x10字节原始数据缓冲区对象,有些缓冲区会占用已释放的位置;

4.触发重复释放漏洞,释放一个0x10字节的原始数据缓冲区对象(可以读写这个对象)

5.喷洒0x10字节大小的对象,它们包含两个指针(0x8字节)到0x20字节大小的原始数据缓冲区对象;

6.该漏洞遍历第3步分配的原始数据缓冲区对象,并搜索覆盖;

7.在步骤5中分配的对象被释放(具有0x20字节大小的对象)并且在它们上面喷洒0x20字节大小的数组;

8.漏洞利用指向两个喷涂类型的数组;

9.喷洒0x10字节大小的对象,它们包含指向0x200字节大小的原始数据缓冲区对象的两个指针,音频源将继续写入这些缓冲区;

10.漏洞利用指向两个喷洒的写入缓冲区对象;

11.该漏洞攻击开始播放音频,并开始写入循环中的类型化数组的可控(易受攻击)对象地址(地址增加0x10字节,指向类型数组的长度),音频缓冲区源节点继续写入0x200字节的数据缓冲区,但是重写指向0x10字节对象中缓冲区的指针;

12.在一定量的迭代之后,漏洞停止循环并检查类型化数组是否增加了长度;

13.此时,漏洞利用已经实现了相对读写原语;

14.该漏洞使用相对读取来查找WebCore::AudioBufferData和WTF::NeuteredTypedArray对象(它们位于堆上的相邻位置);

15.该漏洞使用前一步中找到的数据来构造一个可用于任意读写的类型化数组;

16.该漏洞会创建一个虚拟化DataView对象,以便更方便地访问内存;

17. 实现任意读写后,该漏洞将启动沙箱逃脱;

下图可以帮助你理解所描述的步骤:

9.png

获取相对读写原语

为了要触发此漏洞,需要创建30个canvas模式对象,这将迫使哈希表不断增长。然后释放Canvas模式对象并收缩哈希表,以此在哈希表项中创建一个指向DXImageRenderTarget的悬空指针,现在还无法访问指向已释放对象的指针。

在通过TDispResourceCache< cdisnolock,1,0>::Remove方法释放DXImageRenderTarget对象之后,执行喷洒来分配音频上下文数据缓冲区对象,我们先将该方法称为spray“A”。数据缓冲区对象是通过调用音频上下文createBuffer()创建的。该函数的原型如下:

360截图16220501355625.jpg

numOfchannels参数表示要创建的多个指向通道数据的指针,length是数据缓冲区的长度,sampleRate对于利用并不重要。Javascript createBuffer()触发对CDOMAudioContext::Var_createBuffer()的调用,最终调用WebCore::AudioChannelData::Initialize():

10.jpg

在第17行WTF::IEOwnedTypedArray对象被分配到默认的Windows堆上。这个对象很有趣,因为它包含以下元数据:

11.png

第21行被分配了数据缓冲区(也在默认的Windows堆上)。其中一个缓冲区替代已释放的DXImageRenderTarget对象。这个数据缓冲区的布局如下:

微信截图_20190524161740.png

第二个quad-word是一个参考计数器,除1以外的值会触发对不存在的虚拟功能表的访问并导致漏洞,引用计数器值为1表示对象将被释放。

在整个漏洞利用期间,在释放对象的位置上分配的数据缓冲区用于读取和写入放置在该缓冲区中的值。

在第二次释放对象之前,通过调用Javascript createBufferSource()创建音频上下文缓冲源( buffer source)。这个函数不接受任何参数,但是期望设置buffer属性。分配是在释放易受攻击的对象之前进行的,这样可以避免堆上不必要的噪音,我们先将该方法称为spray“B”。通过调用createBuffer()将buffer属性设置为在启动期间(即触发漏洞之前)创建的一个buffer对象,我们先将该方法称为spray“C”。在此属性访问过程中,将调用以下方法:

360截图16390411112123134.jpg

第71行分配了另一个数据缓冲区,字节的数量取决于通道的数量。每个通道创建一个指针,指向具有任意大小和可控制内容的数据。这是一个有用的原语,稍后将在利用过程中会被用到。

要触发对WebCore::AudioBufferSourceNode::setBuffer()方法的调用,音频必须已经在播放:要么使用已经设置的buffer属性调用start(),要么设置buffer属性,然后调用start()。

接下来,触发重覆释放漏洞,并释放其中一个音频通道数据缓冲区,但会保留来自Javascript的控制。

在spray“B”的每个对象上调用音频缓冲源对象的start()方法,这将创建多个0x10字节大小的对象,其中两个指针指向spray“C” 的0x20字节大小的数据缓冲区对象。在这个喷洒过程中,被喷洒的对象将接管从喷洒“A”中释放的对象。

然后,该漏洞利用spray“A”迭代,以找到包含更改内容的数据缓冲区。spray“A”的每个对象都有getChannelData(),它将通道数据作为Float32Array类型的数组返回。getChannelData()只接受通道号参数。找到更改后,就会创建一个类型化数组。这个类型化数组是可读可写的,并且在漏洞利用和写入指针时还被多次使用,我们暂且称该方法为类型化数组“TA1”。

找到可控通道数据类型数组后,释放所有的spray“B”对象。所有与spray“B”相关的数据都只适用于一个函数。这需要从spray“C”中删除Javascript到数据缓冲区的所有内部引用。否则,以后将不可能释放数据缓冲区。

函数返回之后,又生成了另一次喷洒,我们将其称为spray“D”。这个喷洒会为下一步准备一个音频缓冲源数据,并接管释放的对象。此时,覆盖的对象不包含数据。

然后该漏洞遍历spray“D”并调用每个对象的start()函数,这会向释放的对象写入两个指向0x200字节大小的对象的指针。这些对象被音频上下文用来编写要播放的音频数据。需要注意的是,数据会定期写入此缓冲区,以及不断写入0x10字节对象的指针。不过,这带来了另一个问题,这些指针也会通过“TA1”类型数组泄漏。

然后释放用于spray“B”的缓冲区对象,并执行另一个喷洒方法来接管刚刚释放的数据缓冲区,我们将其称为Spray“E”。Spray“E”分配类型化数组(大小为0x20字节),其中一个类型化数组覆盖释放的0x20字节数据缓冲区的内容。这允许通过类型化数组“TA1”泄漏指向两个喷洒型数组的指针。该漏洞利用只需要一个指向类型化数组的指针,让我们将其称为类型化数组“TA2”。这个类型化数组指向0x30字节的数据缓冲区,这个缓冲区的大小很重要,因为它允许在附近放置其他对漏洞利用有用的对象。

此时,我们已经知道两个类型化数组和两个音频写入缓冲区的位置。此时,该漏洞进入一个循环过程,该循环不断地将指向“TA2”类型数组的指针写入0x10字节对象。写入的指针增加0x10字节,以指向length字段。循环过程是发现漏洞所必需的过程,因为音频上下文线程会不断在循环过程中重写0x10字节对象中的指针。经过一定次数的迭代后,循环结束,此时漏洞搜索也会覆盖整个类型化数组。

被覆盖的WTF::IEOwnedTypedArray类型化数组给出了相对读写原语。

获取任意读写原语

在触发该漏洞之前,该漏洞已经生成了另一个喷洒过程,它分配了缓冲源并为源分配了适当的缓冲区,让我们称之为spray“F”。在这个过程中,使用以下内存布局创建0x30字节大小的WebCore::AudioBufferData对象:

0:016> dq 000001b0`379e9570 L30/8
000001b0`379e9570  00007ffe`47f85988 00000000`45fa0000
000001b0`379e9580  00000000`0000000c 000001b0`379e9420
000001b0`379e9590  0000000a`0000000a 00000000`00000001
0:016> ln 00007ffe`47f85988
(00007ffe`47f85988)   edgehtml!RefCounted<WebCore::AudioBufferData,MultiThreadedRefCount>::`vftable`

这些对象被放置在由类型数组“TA2”控制的数据缓冲区附近,大小为0x30字节的WTF :: NeuteredTypedArray对象也放在附近。

0:016> dq 000001b0`379e97b0 L30/8
000001b0`379e97b0  00007ffe`47f8b460 000001b0`21fa7fa0
000001b0`379e97c0  00000000`00000020 000001b0`20e6e550
000001b0`379e97d0  00000000`00000001 000001b0`381fc380
0:016> ln 00007ffe`47f8b460
(00007ffe`47f8b460)   edgehtml!WTF::NeuteredTypedArray<1,float>::`vftable`

在获得相对读写原语之后,通过搜索特定的模式可以找到从类型化数组“TA2”缓冲区开始到这些对象的偏移量。

知道WebCore::AudioBufferData对象的偏移量后,就可以找到指向音频通道数据缓冲区的指针。音频通道数据用于创建一个虚拟的可控DataView对象,最终实现一个任意的读写原语。在WebCore::AudioBufferData对象的偏移0x18处,存储指向音频通道数据缓冲区的指针。在调用getChannelData()之前,通道数据缓冲区的内存布局如下:

17.jpg

调用WebCore::AudioBufferData对象的getChannelData()成员后,通道数据缓冲区中的指针将被移动,并开始指向Chakra堆上分配的类型化数组对象。这一点很重要,因为它允许类型化数组指针发生泄漏并创建一个虚拟化类型化数组。以下是调用getChannelData()后通道数据缓冲区的内存布局:

18.jpg

知道WTF::NeuteredTypedArray对象的偏移量,就可以实现任意读取原语。

此对象指向的缓冲区不能用于写入操作,一旦写入操作发生,缓冲区就会被移动到另一个堆。由于启用了安全断言,因此不可能增加缓冲区的长度。试图用修改后的长度写入缓冲区会导致渲染器进程崩溃。

WTF :: NeuteredTypedArray对象的布局如下:

19.jpg

指向数据缓冲区的指针存储在偏移量8处,可以覆盖这个指针并指向任意读取内存的地址。

使用任意读取原语,类型化数组的内容和WebCore :: AudioBufferData对象的通道数据缓冲区被泄露。通过写入相对类型数组的能力,将以下内容放在可控缓冲区中。

20.jpg

在此操作之后,WebCore::AudioBufferData对象指向虚拟化通道数据(位于0x00000140e87e7da0)。通道数据包含指向虚拟化DataView对象的指针(位于0x00000140e87e7eb0)。最初,Float32Array对象被泄漏和放置,但它不是一种非常方便的类型用于攻击。若要将其转换为DataView对象时,必须在元数据中更改类型标记。Float32Array对象类型的类型标记是0x31, DataView对象的类型标记是0x38。

通过调用WebCore::AudioBufferData对象的getChannelData()访问虚拟化DataView对象,此时就可以实现任意的读写原语。

本文翻译自:https://blog.exodusintel.com/2019/05/19/pwn2own-2019-microsoft-edge-renderer-exploitation-cve-2019-9999-part-1/如若转载,请注明原文地址: https://www.4hou.com/vulnerable/18214.html
点赞 2
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论