作者:
cssembly
·
2015/02/15 10:15
0x00 前言
微軟最新的補丁包修補了CVE2015-0057的提權漏洞,同一天,漏洞的發現者發表了分析文章《One-Bit To Rule Them All: Bypassing Windows’ 10 Protections using a Single Bit》,看完文章,想嘗試構造一下樣本,原本以為很簡單,結果期間遇到了幾個問題,分享出來,希望能與大家一起討論。
0x01 分析
由於分析文章裡提到漏洞是由xxxEnableWndSBArrows引起的,就透過CreateWindowEx建立ScrollBar,然後呼叫EnableScrollBar,執行到xxxDrawScrollBar,按照分析文章裡的說明,完整流程如下:
結果發現可以執行到xxxGetColorObjects,但是總是沒法走到xxxDefWindowProc,因為(*((_WORD *)P + 0x15) & 0x3FFF) == 0x29A總是成立,最後google了一下,發現這裡是判斷當前窗體是不是ScrollBar。
*((_WORD *)P + 0x15)表示FNID,是透過NtUserSetWindowFNID在建立窗體時設定的,可以在reactos的程式碼中看到,windows包含了如下的FNID。
#!c++
+// FNID's for NtUserSetWindowFNID
+#define FNID_BUTTON 0x02A1
+#define FNID_COMBOBOX 0x02A2
+#define FNID_COMBOLBOX 0x02A3
+#define FNID_DIALOG 0x02A4
+#define FNID_EDIT 0x02A5
+#define FNID_LISTBOX 0x02A6
+#define FNID_MDICLIENT 0x02A7
+#define FNID_STATIC 0x02A8
+#define FNID_IME 0x02A9
NtUserSetWindowFNID中,可以看到這裡會對(_WORD *)(v2 + 0x2A)處的值進行設定,(_WORD *)P + 0x15與等價(_WORD *)(v2 + 0x2A)。
看來直接透過建立ScrollBar是不能執行到xxxDefWindowProc的。透過核心偵錯程式,在此處設定斷點,在螢幕上點動各種窗體,當開啟工作管理員,點選顯示所有使用者程式時,觸發了斷點。檢視此時的呼叫棧,看到了CListView!!!看來可能可以透過MFC的CListCtrl來觸發xxxDefWindowProc函式的執行。
過載了CListCtrl,當ListCtrl顯示的東西過多時,就會出現ScrollBar,這樣在CListCtrl裡處理一下afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)訊息,就能夠處理CListCtrl裡的ScrollBar的訊息,進而釋放掉ScrollBar窗體。當點選CListCtrl裡的橫向ScrollBar時,就會觸發CListCtrlEx::OnHScroll,這次再呼叫EnableScrollBar,就能執行到xxxDefWindowProc函式。
#!c++
void CListCtrlEx::OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar )
{
hScroll = this->m_hWnd;
BOOL bEnable = ::EnableScrollBar( this->m_hWnd , SB_HORZ , ESB_DISABLE_BOTH );
CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}
不過又遇到了新的問題,signed int __stdcall xxxLoadUserApiHook()函式里總是沒法執行到xxxLoadHmodIndex,顯然這個函式是與UserApiHook相關的,(1 << gihmodUserApiHook) & (_DWORD *)(((_DWORD *)gptiCurrent + 46) + 0xBC)的條件總是成立,好吧,斷點設定在win32k!xxxLoadUserApiHook+0x2b處,結果程式啟動的時候,載入user32.dll,呼叫InitUserApiHook,最終會執行幾次xxxLoadHmodIndex,之後就不再呼叫了。
重新看了一下分析文章,核心就是在使用者態回撥時,釋放掉窗體,因此只要找到另一條路徑,在xxxEnableWndSBArrows返回前,將窗體釋放就可以了。在xxxDefWindowProc裡,會呼叫到SfnDWORD,該函式中就存在一個回撥,如下圖。KeUserModeCallback的第一個引數2表示的是ApiNumber,也就是說最終會透過使用者態的KiUserCallbackDispatcher呼叫USER32!__fnDWORD函式,如果能夠在呼叫該函式的時候,將窗體釋放掉,也是能夠達到觸發漏洞的效果的。
定義一個forFree函式,在KeUserModeCallback回撥到USER32!__fnDWORD時,呼叫這個函式將窗體物件釋放掉,第一次呼叫DestroyWindow( hScroll )釋放窗體,第二次則對窗體是否被釋放進行驗證。透過在USER32!__fnDWORD處呼叫forFree,可以看到呼叫EnableScrollBar最終觸發了forFree執行。注意USER32!__fnDWORD會被頻繁呼叫,因此要進行區分。
#!c++
void forFree()
{
DestroyWindow( hScroll );
DestroyWindow( hScroll );
return ;
}
第一次呼叫DestroyWindow後,可以看到函式執行成功,返回值eax=1。再次呼叫DestroyWindow後,win32k!ValidateHwnd就無法根據hScroll的值來獲取到對應的核心窗體結構,返回值eax=0。從xxxDefWindowProc返回之後,實際上窗體已經不再與之前傳遞給EnableScrollBar的控制程式碼對應了。
不過我在32位win7上,並沒有找到分析文章裡提到的OR操作和AND操作。
0x02 結語
上面就是我對CVE2015-0057漏洞樣本構造過程的探索,與分析文章文章裡的內容存在差異,有什麼不對的地方,希望能與大家交流,我的郵箱:[email protected]
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!