這篇文章講述了微軟最新修補的漏洞CVE-2015-0057的細節。
原文:《One-Bit To Rule Them All: Bypassing Windows’ 10 Protections using a Single Bit》
鏈接:http://breakingmalware.com/vulnerabilities/one-bit-rule-bypassing-windows-10-protections-using-single-bit/
今天微軟發布了最新的補丁。這個補丁修復了漏洞CVE-2015-0057,這是我們幾個月前負責任地披露給微軟的一個重要級別的可利用漏洞。 作為研究的一部分,我們發現這個權限提升漏洞,利用該漏洞,可使攻擊者完全控制權Windows系統。換句話說,獲得Windows訪問權的攻擊者(例如,通過網絡釣魚運動)可以利用此漏洞繞過Windows所有的安全措施,擊敗緩解措施,如沙箱,內核隔離和內存隨機化。 有趣的是,漏洞利用僅需要修改Windows操作系統一個比特。
我們已經證實了這一漏洞對所有支持的Windows桌面版本都是可用的,包括Windows10技術預覽版。
下面開始漏洞的細節。起初,我們似乎無法利用它。但是經過一番努力,我們成功地利用了它,我們將描述整個過程。作為分析的一部分,我們也展示了利用視頻。最后,通過分享我們認為有趣的死代碼bug來總結文章。
負責任的披露:盡管這篇博客是技術性的,但我們不會發布任何代碼,或完整的細節,以防止任何人重現漏洞。
在過去的幾年里,權限提升漏洞對于漏洞利用來說變得更加重要了,因為它使惡意代碼可以在內核權限上運行。因此,攻擊者利用權限提升漏洞可以繞過安全防護機制,如應用程序沙箱。
伴隨著攻擊技術的發展,微軟做了大量的努力來保護內核。其理由是,即使漏洞存在,利用也是很困難的,但不是不可能。 例如,這里僅僅是少數存在于Windows 8.1的內核保護機制:
內核DEP - 確保大多數內核數據區不能被執行
KASLR - 隨機化內核地址空間,以避免內核模塊位置固定
完整性等級 - 限制非特權應用程序泄漏內核相關信息的能力
常見攻擊向量的緩解 – 常被濫用的結構的保護(如Win32K WND PROC域)
SMEP - 防止執行內核模式到用戶模式的控制轉移
NULL解引用保護 - 禁止用戶空間前64K數據的映射
盡管存在這些保護機制,在過去的一年中,我們已經看到了一些演示,展示了繞過這些保護機制的技術。
我們在本文中描述的漏洞,就是新近披露的繞過這些保護的可利用的權限提升漏洞。
漏洞:Win32k.sys中的模塊的一個漏洞
這一漏洞出現在微軟Windows內核GUI組件中,即Win32k.sys模塊中。
本文假設對Win32k.sys模塊有很強的技術認識。此模塊的詳細信息,請參考Tajei Mandt的Kernel Attacks through UserMode Callbacks,Gilad Bakas 的Windows Kernel Vulnerability Research and Exploitation。
WIN32K模塊實際上管理著windows滾動條。這些滾動條 - 無論是水平或垂直 – 都是為每個窗口設置的。
圖1
正如圖1中看到的,每個SBDATA結構定義關于一個滾動條的信息。
WSBflags是一個位掩碼,決定了滾動條的狀態。
為了啟用和禁用一個窗口滾動條,可以使用xxxEnableWndSBArrows函數。通過簡單的函數調用,這個函數就可以改變滾動條的狀態。 漏洞正是存在于此功能中。
xxxEnableWndSBArrows原型如下:
wnd - 一個指向相關窗口的指針
wSBflags - 滾動條類型(例如水平或垂直)
wArrows - 指定滾動條的箭頭是否啟用或禁用,并指示那個箭頭啟用或禁用。
為了描述漏洞,我們就來看看在xxxEnableWndSBArrows功能的第一部分,可細分為3個邏輯部分的:
該功能首先通過檢查該窗口是否已經存在滾動信息,并根據需要分配一個新的滾動條信息結構。
從技術上講,該函數讀取pSBInfo域(這個字段指向tagSBINFO結構),測試指針是否為NULL。如果該字段為null和wArrows參數不是NULL,則為窗口分配一個tagSBINFO結構,滾動條的舊標志被設置為0,否則從現有的窗口滾動條的信息中復制舊的標志。該代碼可以在圖2中找到。
流程繼續測試是否應該改變水平滾動的狀態。
根據設定的wArrows參數是什么,函數啟用或禁用的箭頭(圖3)。
流程繼續檢查箭頭的狀態是否發生了變化。
從技術上講,這是通過檢查箭頭的標志來做的(注意,有幾個標志的檢查 - 但這些對于我們的目的來說都不是很有趣)。如果標志已經改變,同時該窗口可見,則xxxDrawScrollbar被調用。
這正是事情變得有趣的地方。
當深入研究代碼,似乎xxxDrawScrollBar可能會導致用戶模式回調(圖4)。
調用鏈中的關鍵函數是ClientLoadLibrary。這個函數執行回調到用戶模式函數__ClientLoadLibrary。
現在,讓我們回到xxxEnableWndSBArrows的代碼。
我們的檢查表明tagSBINFO指針在回調后,不任何驗證就使用了。最終,這可能會導致使用后釋放(UAF)漏洞,因為該函數可能繼續使用被釋放了的滾動條信息(圖5)。
回調之后,xxxEnableWndSBArrows函數繼續并且改變垂直滾動條的狀態。
在這個階段中,函數試圖啟用或禁止標志。然而,由于該結構已經被釋放,我們可以用它來將位于釋放的緩沖區的第一個DWORD與0xC按位或(如果我們禁止箭頭)或者清除第3位和第4(如果啟用箭頭)。見圖6。
為了簡便起見,我們將展示如何操縱2個比特位來完全控制系統。其實,操縱其中之一就足夠了。
似乎位操作起初還不足以造成任何顯著影響,但我們還是決定繼續嘗試。最需要去嘗試的事情是,或者增加一些緩沖(使用按位OR)的大小或者減少一定的參考計數器(使用按位AND)。
短暫的搜索后,我們發現了滿足第一個要求的對象。這個對象是一個窗口的屬性列表。
每個窗口都有一個屬性列表。通常GUI應用程序可以使用這些屬性來存儲任意值, Win32K也使用屬性列表以便存儲內部數據。
用于保存窗口的屬性的數據結構可以在圖7中看到。第一個域cEntries,是屬性數組中的條目數; iFirstFree是屬性列表中的第一空閑單元的索引;和props?是PROP數組。
應用程序可以使用SetProp API設置窗口的屬性。該函數的原型如下:
hWnd -窗口句柄。
lpString – 屬性或ATOM。
HDATA – 要存儲的數據。
添加屬性到一個窗口是通過CreateProp函數來完成,它在Win32K模塊中。
如圖8所示它的分配算法是相當簡單的。如果沒有空間可用于屬性列表中的新條目,則函數分配多一個條目的新列表。該函數然后將舊屬性的緩沖區復制到新的緩沖區,釋放舊的緩沖區,并增加條目計數。
這段代碼中還有幾個重要的點要注意: 首先,屬性是從桌面堆中分配(使用DesktopAlloc)。tagSBINFO也從該堆中分配。如果我們想用UAF漏洞來改變屬性結構,這是至關重要的。 第二,每一個新的條目都觸發緩沖區的重新分配。這意味著我們可以很容易地觸發緩沖區的重新分配,來達到tagSBINFO結構的大小。這樣做增加了緩沖區將被分配在釋放了的tagSBINFO結構的地址的機會。 第三,也是最重要的是,cEntries域位于該結構的第一個DWORD。這意味著我們可以增加它的大小(用按位OR)。增加屬性數組的大小后,我們基本上實現了經典的緩沖區溢出。
上面研究了權限提升漏洞的利用。我們就此停止,同時避免發布任何敏感代碼。 我們在64位Windows10技術預覽版上提供了概念證明。
視屏地址:https://www.youtube.com/watch?v=ukAr6MiA788
經過一番工作,我們成功地創建針對Windows所有版本的一個穩定利用 - Windows XP到Windows10預覽都可用(SMEP和其他保護都打開)。我們已經表明即使是微小的缺陷也可以用來完全控制任何Windows操作系統。
盡管如此,我們認為微軟在使其操作系統更安全方面做的努力起到了顯著作用,編寫可靠的攻擊代碼比以往更難了。 不幸的是,這些措施不能完全阻止攻擊者。我們預計,攻擊者將繼續整合漏洞利用到他們的犯罪工具包,使得妥協在所難免。
檢查xxxEnableWndSBArrows函數的代碼,可以看到有調用xxxWindowEvent函數。
乍一看,似乎這兩個函數會比xxxDrawScrollbar函數更容易用于漏洞利用,具體如上所述。
但是,在深入研究代碼之后,很快就明白,在代碼中的橫向滾動條部分調用xxxWindowEvent實際上是死代碼(圖9)。
看代碼,有兩個條件調用xxxWindowEvent函數。僅當滾動條信息的舊標記與新的標志不同時,這些調用才執行。然而,在這些條件出現時,舊的標志的值與新的標志總是相等的。因此,調用xxxWindowEvent的條件是永遠不會滿足的。這實際上意味著,這個死碼在那里大約15年沒有做任何事。