談談15年5月修復的兩個0day
0x00 前言
作者:[email protected] Team
北京時間2015年5月12日, 微軟推送了5月的補丁日補丁,包含IE、Windows核心、Windows核心驅動、Office等多個元件的安全更新。 本月修復的兩個0day漏洞
MS15-052中修復的Windows核心安全特性繞過漏洞:CVE-2015-1674 (https://technet.microsoft.com/en-us/library/security/MS15-052)
MS15-051中修復的Windows核心模式驅動許可權提升漏洞:CVE-2015-1701(https://technet.microsoft.com/en-us/library/security/MS15-051)也引起了我們的注意。
經過確認, CVE-2015-1674是筆者在2014年中發現的一個核心KASLR繞過漏洞,CVE-2015-1701則是Fireeye在今年4月18日釋出的關於Operation RussianDoll(俄羅斯套娃行動)報告中發現的,俄羅斯APT28駭客組織發起的針對極其特定目標的攻擊中使用的用於許可權提升的核心0day漏洞,在微軟釋出補丁上線同時,來自俄羅斯安全社群kernelmode.info的駭客hfiref0x也在他的Github上公佈了針對CVE-2015-1701漏洞的完整攻擊程式碼(https://github.com/hfiref0x/CVE-2015-1701)。 本篇Blog筆者就這三個本月修復的0day漏洞,講講他們的原理、細節和修補方法和一些周邊資訊。
0x01 CVE-2015-1674/MS15-052
漏洞資訊
MS15-052是微軟專為修復CVE-2015-1674漏洞釋出的針對CNG.sys的安全更新。該漏洞實際和筆者在去年10月微軟釋出Windows 10 第一個預覽版(9860)時釋出的一篇微博(http://weibo.com/1648808737/BpGpHhEyD)上介紹的漏洞CVE-2015-0010/MS05-010(https://technet.microsoft.com/library/security/MS15-010)是同一個問題,屬於微軟在修復CVE-2015-0010時沒有修復完全,遺留的安全漏洞。
在Windows 10釋出後,筆者在其上測試了兩個KASLR繞過的漏洞,一個是j00ru在NoSuchCon 2013上公佈的一個利用核心KiTrap01處理除錯異常的問題探測核心地址繞過KASLR的問題(http://j00ru.vexillium.org/blog/21_05_13/nsc2013_slides.pdf) ,另一個則是筆者在2014年逆向Windows 8.1核心發現的一個CNG.sys中存在的尚未公開的KASLR繞過漏洞,在當時釋出的Windows 9860預覽版上,這兩個漏洞都沒有被修復。
由於CNG.sys的裝置(\Device\CNG)是系統中為數不多的設定了ALL APPLICATION PACKAGES DACL,從而允許高度隔離的AppContainer
也可以隨意訪問的裝置, 而且此問題同時影響x86和x64的系統(j00ru的KASLR繞過僅能用於x86系統),因此後者的實用性更強。此漏洞是360Vulcan Team為Pwn2Own型別的比賽儲備的核心漏洞/缺陷之一,而且微軟對KASLR繞過型別漏洞一直是比較曖昧的態度(j00ru的KASLR繞過直至目前Windows 10最新版本10074上仍未得到修復),因此筆者未將此漏洞報告給微軟,在此次補丁日中, CVE-2015-1674即是筆者這裡提到的該漏洞。
可能是由於該漏洞會影響IE和Spartan中的EPM(增強保護模式,主要使用AppContainer
進行保護),微軟還是決定在Windows 10的新版本中修復這個漏洞,我們看到,在2015年1月釋出的Windows 10 9926中,微軟就已經悄悄地完全修復了這個漏洞,同時在2月的補丁日中, 微軟也為同樣受影響的Windows 8/8.1/Server 2012/Server 2012 R2推送了MS05-010的來試圖修復這一問題。
但是很有意思的是,在MS05-010中,儘管微軟給予了該漏洞CVE-2015-0010的編號,但實際並沒有完全修復這個問題,導致這個漏洞最終在Pwn2Own 2015上被來自韓國的選手lokihardt使用,攻破Windows核心。也正是因為Pwn2Own 2015上使用,微軟才再次釋出MS15-052安全更新,為該漏洞換了一個新的編號:CVE-2015-1674。實際這個“新”漏洞和CVE-2015-0010是幾乎完全相同的問題,屬於CVE-2015-0010沒有完全修復而遺留的問題,讓人難以理解的是,在Windows 10 9926裡,CVE-2015-0010和CVE-2015-1674的問題都是被一次性修復的,不得不說微軟內部似乎在開發Windows 10的過程中,對補丁修復和管理發生了疏忽和混亂,才引發了現在的問題。
在ZDI的官方網站上已經公開了這個漏洞的一些細節:http://www.zerodayinitiative.com/advisories/ZDI-15-189/ ,由於cng.sys中的攻擊面並不多,具備經驗的安全研究人員根據這個資訊已經可以比較輕易地發現這個漏洞的細節,因此這裡筆者就直接介紹這個漏洞的具體資訊了。
漏洞細節
這個漏洞存在於cng.sys的裝置控制處理程式碼中。CNG.SYS是微軟的下一代核心密碼學驅動,它透過裝置控制(DeviceControl)和函式輸出提供很多密碼學相關介面,和很多Windows核心驅動一樣,他的裝置控制處理中混合了同時開放給其他核心驅動和使用者模式程式的控制功能。這往往是很多核心安全漏洞的來源,我在ISC 2014上關於360 XP盾甲3.0核心防護中曾提到過一個曾影響Windows系統,至今仍影響許多重要第三方驅動的控制介面內的KASLR繞過,也存在類似的問題。
CNG.sys的特殊性在於,他在建立裝置(\Device\CNG)後,會使用ObSetSecurityObjectByPointer
為裝置設定一個特殊的Security Descriptor
,該安全描述符是允許ALL APPLICATION PACKAGES
許可權的使用者完全控制該裝置的。對微軟的AppContainer/EPM
機制稍有了解的同學可以知道,設定了這個許可權的裝置,即使在IE或Spartan的隔離保護模式下的渲染程式,也是可以直接訪問的,CNG這麼設定的目的也是希望所有程式都能夠訪問它的相關介面,所以在驅動的IRP_MJ_CREATE
處理中,也是直接允許任意訪問,沒有做任何檢查,也就是CNG.SYS內的相關介面,即使是被IE/Spartan
的保護模式或者增強保護模式保護的程式,也都可以隨意訪問的。
在CNG.SYS的裝置控制碼中,有多個控制碼是專門提供給外部驅動使用的,如0x39024,0x39040,0x39044,0x39048,0x39064
等,這些裝置控制碼將為呼叫者返回包括FIPSSHA, FIPS3Des,HMAC MD5,FIPS GenRandom ,SSL加解密和 Key管理,BCrypto系列介面等一系列在CNG內部實現的函式介面地址, 透過這樣,外部驅動可以直接呼叫這些函式的介面,進行相關的密碼學操作,而無需自己實現這些介面。
這裡的問題就是,針對這些專門外核心模式驅動設定的介面,並沒有檢查IRP的來源是否是核心模式,因此使用者模式的程式直接透過DeviceIoControl
函式,一樣可以呼叫這些裝置控制碼,獲取這些函式的介面。當然,使用者模式的程式無法直接使用這些介面,但配合CNG.SYS的映象佈局,使用者模式的程式就可以獲得CNG.sys的基址和相關關鍵資料的位置,這樣就完全繞過了微軟核心的KASLR核心模式地址隨機化技術。
在微軟的MS05-010中,針對0x39024
這個介面進行了處理和修復(CVE-2015-0010),而就在修復程式碼的正下方一行,就躺著存在問題的0x39040/0x39044/0x39048/0x39064
等介面,直接在2月的補丁中被忽略了。使用該漏洞參加3月Pwn2Own的lokihardt選手看到補丁時,肯定是笑得的開了花:)
直到這個月微軟的MS15-052中,才正式修復了0x39040~0x39064
系列介面的問題,並將這個未成功修復的漏洞重新命名為CVE-2015-1674。
這裡再介紹一些背景知識:在微軟Windows 8.1作業系統之前,微軟並不關注本地的KASLR問題,儘管從Windows NT開始, 微軟的核心模組就是隨機地址的(尤其在x64系統 + Windows 8以上使用了高熵隨機數後),但還僅限於為遠端核心漏洞增加難度。這主要是因為在Windows 8.1作業系統之前, 任意許可權的程式都可以透過NtQuerySystemInformation
,使用SystemModuleInformation
來獲得核心和核心驅動程式、模組的基址,直接可以無視KASLR。
在Windows 8.1作業系統中,情況改變了,微軟為了進一步對抗在IE的保護模式、增強保護模式下使用核心漏洞進行穿透沙箱的攻擊,引入了針對本地程式的KASLR緩和機制,Alex Ionescu在其BLOG上分析了這一機制:(http://www.alex-ionescu.com/?p=82),在Windows 8.1作業系統中,如果程式執行在低完整性級別以下(保護模式或增強保護模式),那麼SystemModuleInformation
等相關獲得核心模組基址的方法都會被阻止,這樣,即使攻擊者在保護模式或增強保護模式下觸發了核心漏洞,由於無法獲得核心基址,也很難進行進一步利用。
CNG的這個安全漏洞就為攻擊者開啟了在這種緩和下繞過KASLR的大門,因為他正好是一個在保護模式和增強保護模式下也可以使用的漏洞,同時可以在x64和x86系統上都穩定呼叫,獲得了cng相關的關鍵資料地址後,如果攻擊者具備一個核心任意地址寫入的漏洞,就可以直接覆蓋儲存在CNG.SYS資料段的函式列表,當其他核心模式驅動進行呼叫時,直接獲得核心程式碼執行的權利。
漏洞修復方法
該漏洞的修復相對就簡單了,在IRP_MJ_DEVICE_CONTROL
的處理程式碼中,針對0x39024,0x39040
等控制碼,檢查Irp->RequestMode
,就可以區分核心模式驅動和使用者模式的呼叫,避免漏洞的發生。 如此簡單的漏洞,幾乎並列的程式碼,卻被微軟補丁修復部門忽略,要分兩次,歷時半年才能完全修復,不得不說是個悲劇。
0x02 CVE-2015-1701 / MS15-051
漏洞資訊
CVE-2015-1701是Fireeye在今年4月18日的報告“Operation RussianDoll: Adobe & Windows Zero-Day Exploits Likely Leveraged by Russia’s APT28 in Highly-Targeted Attack”中提到的一個被用於配合Adobe Flash 漏洞使用進行攻擊的Windows核心許可權提升漏洞,由於漏洞在4月還沒有被修復,因此Fireeye官網上關於此漏洞的資訊也是語焉不詳。
在這個月的補丁日中,微軟透過MS15-051補丁修復了這一許可權提升漏洞,而就在補丁釋出的幾乎同時,俄羅斯kernelmode.info論壇上的駭客hfiref0x在其github上公佈利用該漏洞的完整攻擊程式碼(https://github.com/hfiref0x/CVE-2015-1701),似乎暗示著該漏洞同俄羅斯的密切關係。
該漏洞同去年10月曝光的CVE-2014-4113漏洞有相似之處,是Windows核心驅動win32k.sys在建立視窗過程的一處時序問題引發的核心視窗物件標誌錯亂,導致核心任意程式碼執行。從Windows 8 作業系統開始,微軟就修復了這一問題,但不知為何此漏洞一直遺留在Windows XP/2003/Vista/Windows 7/ Server 2008/ Server 2008 R2作業系統中。
微軟的另外一個錯誤似乎是,在MS15-051的補丁公告中(https://technet.microsoft.com/en-us/library/security/MS15-051)提到, 該漏洞在Windows 7/Windows 2008 R2上的影響是:None,但實際這個漏洞是影響Windows 7和Server 2008 R2的, Github上的攻擊程式碼就是針對的Windows7系統。
漏洞細節
透過分析攻擊程式碼,我們可以知道,這個漏洞是在核心模式驅動win32k.sys中的視窗建立函式:xxxCreateWindowEx
中發生的,該函式是GDI核心中建立核心視窗物件的複雜函式,透過使用者模式的user32!CreateWindow(Ex)->NtUserCreateWindowEx
可以最終呼叫到該核心函式。
xxxCreateWindows
在分析一系列引數後,會使用HMAllocateObject分配一個視窗型別的win32物件, 併為其填充一些引數,如視窗的Class(類)物件等。使用Hex-rays Decompiler
,我們看到,在Windows 8之前+沒修復漏洞的作業系統上,有這樣的一個邏輯過程:
#!c++
if ( pcls->spicn && !pcls->spicnSm )
{
xxxCreateClassSmIcon(pcls);
}
pwnd->hModule = hMoudle;
pwnd->lpfnWndProc = MapClientNeuterToClientPfn(pcls, 0, bansi);
這裡xxxCreateClassSmIcon
的目的是為該視窗類的圖示建立小圖示快取,接下來,系統會透過MapClientNeuterToClientPfn
根據視窗類為視窗設定WindowProc。
看上去這裡似乎沒有問題,但我們深入來看xxxCreateClassSmIcon
的實現就會發現,這個xxxCreateClassSmIcon
是透過xxxClientCopyImage->KeUserModeCallback
來實現的,也就是說這個呼叫最終是配合使用者模式回撥來實現的。
對Window視窗管理相關比較熟悉的讀者可能知道,KeUserModeCallback
實際最後會呼叫放置在PEB->KernelCallbackTable
中的對應函式來實現功能的,而這些函式都是最終實現在使用者模式的,例如這裡就將最終呼叫user32!__xxxClientCopyImage
函式來實現。
Kernel Mode Callback是一套win32k專用的user mode-kernel mode
互動回撥機制,也是在視窗核心系統中為了提升效能而設計的功能,而由於這個機制的存在,使用者模式程式可能透過掛鉤系統的kernel callback函式來實現控制、中斷核心執行的邏輯流程,從而引發了很多安全漏洞, 包括歷史上的CVE-2013-3167漏洞等都是該機制引發的。
曾是Norman Threat Research團隊(現在安全公司Azimuth Security)的核心牛人Tarjei Mandt,也曾在Blackhat 2011上發表過專門深入分析該類call back機制引發的安全漏洞的議題《Kernel Attacks through User-Mode Callbacks》,講述了多個由該機制引發的安全漏洞,有興趣的讀者可以深入瞭解下。
我們繼續來講解這個漏洞,從剛才的分析我們知道, 透過掛鉤user32!__xxxClientCopyImage
函式,我們就可以實現在上面程式碼的xxxCreateClassSmIcon
位置,也就是在系統填充pwn->lpfnWndProc
前中斷xxxCreateWindowEx
的過程,並執行我們想要的操作。 那麼接下來該如何利用這點呢? 在hfiref0x貼的程式碼中我們看到,在hook了該函式後,程式碼只做了一行操作:
#!c++
SetWindowLongPtr(GetFirstThreadHWND(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc);
SetWindowLongPtr
是設定視窗相關資料、屬性的函式,這裡GWLP_WNDPROC
這個功能索引(index)的作用是對視窗進行子類化(subclass)/去子類化(unsubclass),可以透過子類化,替換視窗的呼叫過程為自己的函式,來接管視窗的一些處理,也可以透過設定為DefWindowProc
來去子類化,取消接管過程。
這裡面GetFirestThreadHWND
是一個獲得當前正在被建立的視窗控制程式碼的一個技巧,因為現在xxxCreateWindowEx
正在被中斷在核心過程中,僅僅透過使用者模式的程式碼和xxxCreateClassSmIcon
的資訊,我們是無法得知當前正在被建立的視窗物件/控制程式碼的。
但是我們知道,在Win32k核心中,所有的核心視窗資訊是全部被對映到使用者模式的一塊記憶體地址上的,透過user32!gSharedInfo
我們可以得到它的地址(是核心模式視窗資訊列表的一個只讀對映),而我們剛才說過核心視窗物件在我們中斷時已經經由HMAllocateObject
被建立了,那麼它實際就已經可以在gSharedInfo中檢索到。這並不是什麼新鮮的技巧,在過去的一些漏洞攻擊程式碼,例如MWR Labs在Pwn2Own2013中使用的CVE-2013-1300 Exploit中就使用了類似的技巧。
我們看到這裡程式碼使用SetWindowLongPtr
將當前執行緒正在建立的視窗的WindowProc
替換為了DefWindowProc
,接下來似乎就得到了核心程式碼執行的能力,這是為什麼呢?我們深入看看SetWindowLongPtr->NtUserSetWindowLongPtr
的實現就可以得到解答。 透過分析核心的實現我們可以得知,NtUserSetWindowLongPtr->xxxSetWindowLongPtr
,當index (GWLP_WNDPROC(-4) ) <0
,會呼叫xxxSetWindowData
來完成最終的設定,我們繼續看xxxSetWindowData
的實現,發現有這樣的邏輯:xxxSetWindowData
在判斷到index是GWLP_WNDPROC
時,會執行如下邏輯:
#!c++
ptr = MapClientToServerPfn(dwData);
if ( ptr )
{
ClrWF(pwn, WFANSIPROC);
SetWF(pwn, WFSERVERSIDEPROC);
pwn->lpfnWndProc =ptr;
這裡的邏輯,是檢查此處GWLP_WNDPROC
是不是一個去子類化操作(unsubclass),如果是的話,就認為這裡需要設定為核心來接管視窗過程,給視窗設定Server Side Proc
的標誌,這個標誌的含義是視窗的視窗過程函式將在核心模式下呼叫。
這裡MapClientToServerPfn
的檢查方法是核對給當前視窗設定的WindowProc
(也就是SetWindowLongPtr
的引數dwData
)是否是gpsi(PCSERVERINFO)
內apfnClientA/W
中預先設定的函式,即user32
中預先準備的針對各類視窗的處理函式,例如ScrollBarWndProcW
,MenuWndProcW
,ButtonWndProcW
,等等,當然也包括這裡的DefWindowProcW
。
如果驗證是這些預設的視窗過程,那麼就將視窗標誌設定為在核心模式執行視窗過程函式,並將視窗過程函式修改為gpsi中->aStoCidPfn中
對應包含的核心處理函式,例如xxxSBWndProc
,xxxDefWindowProc
,xxxMenuWindowProc
,等等。
這裡看似沒什麼問題,因為在核心模式中執行的總是被xxxWindowData
設定的WindowProc(win32k!xxxDefWindowProc)
。
但是,我們放到最前面我們說到的xxxCreateWindowEx
這個邏輯時序來看,就會發現這裡會引發嚴重的問題: xxxCreateWindowEx
:
1. xxxCreateWindowEx呼叫HMAllocateObject建立視窗物件
2. 呼叫xxxCreateClassSmIcon -> xxxClientCopyImage
3. xxxClientCopyImage被中斷,使用者模式函式呼叫SetWindowLongPtr給當前正在建立的視窗設定WindowProc
4. WindowProc是user32!DefWindowProc,被xxxSetWindowData認為是去子類化,於是將視窗過程設定為核心模式執行,並將WindowProc設定為核心函式win32k!xxxDefWindowProc
5. 從xxxCreateClassSmIcon返回,繼續呼叫MapClientNeuterToClientPfn轉化當前視窗類函式的預設WindowProc(也就是使用者模式可控的函式),再將視窗物件的WindowProc設定為使用者自己的視窗物件
我們看到,因為這個中斷過程恰好在xxxCreateWindowProc
為視窗設定WindowProc
前面,所以xxxSetWindowData
修改視窗的WindowProc
為xxxDefWindowProc
是無效的,視窗的WindowProc
還是被修改為使用者模式應用程式設定的WindowProc
,而此時,這個視窗的標誌已經被設定為是需要在核心模式執行WindowProc
,那麼接下來再遇到xxxSendMessage
等函式對這個視窗傳送訊息時,就會在核心模式下直接跳轉、呼叫實際在使用者模式的函式來進行處理,從而直接導致核心模式程式碼執行。
上面就是這個漏洞的大致原理,也就是說透過在即將給視窗設定WindowProc前,中斷xxxCreateWindowEx
的過程,並透過SetWindowLongPtr
讓系統認為是在去子類化,並設定視窗為核心模式執行,返回xxxCreateWindowEx
時,設定視窗過程函式時覆蓋了去子類化的函式,但沒有去掉視窗函式的核心模式執行標記,導致了核心模式任意程式碼執行。
Exploit程式碼最終獲取了系統程式的token後覆蓋自身的token,來進行許可權提升,這就沒什麼好說的了。
漏洞修復方法
在Windows 8及以後的作業系統上,微軟已經修正了這塊的時序邏輯,先修改視窗的WindowProc,再呼叫xxxCreateClassSmIcon
,這樣xxxCreateClassSmIcon
即使被中斷並去去子類化視窗,最終視窗的處理函式也將是win32k!xxxDefWindowProc
,而不會被修改為使用者模式的函式,因此不存在這個漏洞。
本月的補丁中,針對Windows 2003/Vista/7/Server 2008/Server 2008 R2也是如此修復這個漏洞的。
在Windows 8及以上的作業系統上,如果CPU支援SMEP功能(IvyBridge以上),這種核心模式直接呼叫使用者模式函式會被攔截,不過攻擊者也可能將payload隱藏在核心地址空間來繞過這一攔截。 對於微軟沒有官方補丁的XP作業系統,360 XP盾甲從v2.0開始就提供了win32k核心函式接管處理,針對CVE-2015-1701這種型別的漏洞無需升級,就可以直接免疫。
相關文章
- 淺談被加殼ELF檔案的DUMP修復2020-08-19
- 谷歌釋出安全更新,修復今年第十個0day漏洞2021-09-14谷歌
- 微軟10月累積更新,修復4個0day漏洞2021-10-13微軟
- 微軟補丁星期二:修復108個漏洞,含5個0day2021-04-14微軟
- 微軟三月補丁更新修復3個0day漏洞2022-03-09微軟
- Chrome釋出最新版本,修復一個在野0day2021-02-05Chrome
- 談談WINDOWS下重灌系統後oracle的恢復2019-07-20WindowsOracle
- 很認真的談一談程式設計師的自我修養2020-11-24程式設計師
- Chrome安全更新發布,修復遭在野利用的0day2022-02-17Chrome
- 修復6個0day漏洞,微軟11月累積更新發布2022-11-10微軟
- 微軟10月累積更新發布,修復一個0day在內的85個漏洞2022-10-13微軟
- 微軟1月累積更新發布,修復6個0day、9個高危漏洞2022-01-13微軟
- 蘋果釋出安全更新,修復已被利用的0day漏洞2021-10-12蘋果
- 淺談ES7的修飾器2018-07-11
- Linux核心發現兩個沒有被修復的DoS漏洞2018-12-28Linux
- SQL Server誤區30日談-Day16-資料的損壞和修復2021-09-09SQLServer
- 談談我工作中的23個設計模式2022-11-29設計模式
- 談談最近做的一個自動化平臺2021-12-05
- 時隔兩年,PuTTY 釋出了一個新的安全修復版本2019-03-21
- 談談最近的思考2018-06-06
- 談談制定資料戰略的7個關鍵要素2023-01-28
- 談談近況,談談自由職業,談談“金飯碗”2020-11-11
- 蘋果修復舊款蘋果裝置兩個0day漏洞,可導致敏感內容劫持、任意程式碼執行攻擊2023-12-01蘋果
- 從Ori系列兩代間的變化淺談銀河城類遊戲的復興2020-03-26遊戲
- 談springboot兩種實現結構2024-05-07Spring Boot
- 漫談公理邊緣系列:單人造出兩座城 復古思路之上的開拓2021-10-09
- 再談談這個沉重的話題--程式設計師的出路2021-05-26程式設計師
- Chrome最新更新:修復一個0day,請儘快更新至最新版本2019-03-08Chrome
- 談談JavaScript 的Navigator 物件2019-08-06JavaScript物件
- 談談Koa 中的next2019-02-22
- 談談 JavaScript 的作用域2018-03-28JavaScript
- 談談Shiro的原理及在SSM和SpringBoot兩種環境下的使用姿勢(下篇)2019-03-04SSMSpring Boot
- 談談資料質量管理中的5個關鍵要素2023-02-09
- 談談如何設計一個 Network->Model 元件2018-09-01元件
- 和兩位製作人談談:如何做一款好的國產遊戲?2021-05-07遊戲
- 淺談國內SLG遊戲的兩級分化2019-07-10遊戲
- 談談Raft2022-01-09Raft
- 談談Promise2018-05-20Promise