漏洞概要
該漏洞是一個棧溢出漏洞,該漏洞是由於Microsoft Windows Common Controls的MSCOMCTL.TreeView、
MSCOMCTL.ListView2、MSCOMCTL.TreeView2、MSCOMCTL.ListView控件(MSCOMCTL.OCX)中存在錯誤,可被
利用破壞內存,導致任意代碼執行。
漏洞分析
目前在互聯網上已經出現了利用該漏洞的DOC文件,下面以其中的一個文件爲基礎分析該漏洞的產生原因。下面是該
文件的截圖
在該文件中我們首先找到\object\objocx這個標籤,這個標籤標誌着完整的objdata數據的起始。在objdata的後面以“D0CF11E0”這樣的數據開頭的部分便是objdatatext。在這裏我們找到了ListView的ClsID:BDD1F04B-858B-11D1-B16A-00C0F0283628。這個Clsid便是出現問題的幾個ID之一。
之後在繼續查找EleName爲Contents的OLESSDirectoryEntry結構
正是在解析該結構中的Data數據時出現了問題。如下圖:
Data數據截圖
我們看究竟是如何解析的該段數據的。
讀入Contents數據塊,並創建相關的數據流對象。
讀入Stream的頭部,校驗頭部是否爲固定的數據0x12344321,即之前我們看到的Data數據的頭部。
注意:這裏在操縱讀入的數據流時,使用的是棧讀取數據,之後正是由於沒有對讀取的數據進行大小的合法性校驗,造成了棧溢出。
再簡單介紹下CExposedStream::Read這個函數:
該函數的第二個參數爲要讀入數據返回的buffer,第三個參數爲要讀入數據的大小。很明顯這裏,將讀入的數據返回到
了8字節的棧空間中。
比較Stream頭部標誌之後的數據是否爲8,如果不是則直接返回錯誤。
再繼續讀取8個字節,讀取完的數據如圖中藍色陰影部分。之後的0x2758F97F處的函數對於剛纔讀取的這8個字節做了
處理和校驗。具體目的不詳。
成功則返回0。
再讀取0x1C個字節的數據
繼續比較一個標誌是否爲0xEB7D084E,如果是則進入其對應的處理例程。如果不是則比較是否是0x58DA8D8B或者
0xF4D83600並進入相應的處理例程。
拷貝該結構的後面0xC個字節
根據得到的size再讀取對應size大小的數據。此處大小爲0x1C。
下面是對該段數據進行處理,與本文漏洞無關,略去分析。
讀取下面一個結構爲0xC的結構
同樣是對該結構數據進行一些校驗和處理。
注意這裏傳進來一個8,是checksize,主要作用是做接下來讀取的數據的檢查。另外在這裏傳進來的第一個參數
也是棧上的,該參數用於接收0x275C876D返回的數據。
繼續讀取4個字節(圖“Data數據”中偏移0x62處的數據)
讀出來爲8
我們可以看到之後用剛纔讀出來的8作爲申請內存的size。在申請內存之前將其與剛纔傳進來的參數8做了檢查,因此可
以判斷此處需要申請的buf必須爲8。否則則直接返回錯誤。
之後在申請的8字節的緩衝區中讀入數據。
將剛纔讀出來的數據拷貝到上個函數傳進來的第一個參數的地址中。(一個棧中的地址)
之後該函數返回後,對剛纔返回的數據進行校驗。
解析下一個結構的函數入口
由checksize得知,函數0x275C876D需要讀入的下一個Dword爲0xC,也即爲其要申請的buf大小。
讀取的數據爲0xC,符合條件。繼續申請0xC字節的大小的buf。
讀取到的0xC字節的數據如圖藍色陰影所示。
同樣將剛纔讀出來的數據拷貝到上個函數傳進來的第一個參數的地址中。(一個棧中的地址)
注意圖中的註釋。猜測該處傳入的checksize應該爲小於等於8,而這裏只驗證了小於8的情況,而沒有驗證大於8的情況。此處爲0x8282
再讀入4字節,仍然是0x8282。
隨後便在堆上申請了0x8282字節的內存。
讀取了0x8282字節的數據到剛纔申請的堆中。(佈置好的shellcode)
悲催的拷貝到了上個函數分配的8字節的棧內存中。
注意圖中的註釋
構造符合跳轉到較近retn指令條件的數據。(兩次檢查)
此時棧中的數據如圖。
由於系統版本的原因,0x7FFA24CE並非一個有效指令的地址。通過分析發現,作者可能需要找一個jmp esp指令。之後
便可以從棧中執行已經佈置好的shellcode。該shellcode的缺點是直接從棧中執行了指令,無法繞過DEP等保護技術。
相信較好的shellcode會馬上出來。
Shellcode截圖
總結:MSCOMCTL.OCX在解析數據時候將需要處理的數據拷貝到了棧上,其中在解析一個標誌爲Cobj的結構的
時候對其後面需要拷貝的Data數據大小未做正確校驗,導致可以將數據直接覆蓋到棧中,造成了一個標準的棧溢
出漏洞。