標籤:
CVE-2016-0143漏洞分析
0x00 背景
4月20日,Nils Sommer在exploitdb上爆出了一枚新的Windows核心漏洞PoC。該漏洞影響所有版本的Windows作業系統,攻擊者利用成功後可獲得許可權提升,微軟在4月補丁日修複了該漏洞。
0x01 漏洞分析
Nils Sommer並沒有說明該漏洞為何種類型的漏洞,咋看崩潰情境會認為是NULL Pointer dereference或者UAF漏洞,粗略分析後,覺得是整數溢出漏洞,但是最後還是將其定義為特殊的NULL Pointer dereference漏洞。下面對漏洞成因進行簡單分析。
In xxxRealDrawMenuItem
崩潰的地方是在win32k!xxxRealDrawMenuItem函數內,在windbg中查看崩潰時的記憶體狀態:
崩潰上下文代碼的IDA,將其命名為過程A(最後說明時會用到):
在crash之前,eax會和[ebp+arg_8]做有符號乘法,並將結果和ebx做比較,來確定是否執行crash的指令。
此時的eax為PoC中r.bottom和r.top運算後的結果,具體操作在win32k!xxxDrawMenuBarTemp內,演算法為:eax= r.bottom-r.top-1;
[ebp+arg_8]為PoC中的info.bmiHeader.biSize;
PoC 給的值會讓"imul eax,[ebp+arg_8]"產生溢出,走向crash流程。這裡看起來是由於整數溢出造成的漏洞,其實正常流程都會走這個流程的,這並不是漏洞成因所在。
所操作的ecx從[ebp+var_28]擷取,原始值為1,[ebp+var_28]本應該為DIBObject的地址,但是在為其分配記憶體的函數win32k!SURFMEM::bCreateDIB中,並沒有為其分配記憶體空間,漏洞的關鍵在這裡。
下面分析建立DIBObject失敗的原因。
In SURFMEM::bCreateDIB
SURFMEM::bCreateDIB的函數調用棧:
xxxDrawMenuBarTemp
à GreCreateDIBitmapReal
àSURFMEM::bCreateDIB
在SURFMEM::bCreateDIB函數內有這樣一段代碼,將其命名為過程B:
此時eax等於xxxRealDrawMenuItem函數中的edi,[ebp+arg_0]為PoC中的info.bmiHeader.biSize*4,按照PoC中設定的值,兩者分別為0x7fffff69和0x274。
當兩者進行乘法運算後,同樣發生了溢出,但由於是無符號運算,所以edx!=0。調用函數ULongLongAdd後,[ebp+AllocationSize+4]=edx=139h!=0,因此便走向了失敗的流程,不會為DIBObject分配記憶體,也導致GreCreateDIBitmapReal函數的傳回值為0。
如果未發生溢出,並且兩者的乘積(+0x154)小於7FFFFFFFh, SURFMEM::bCreateDIB函數就會根據這個乘積(+0x154)為DIBObject分配一塊記憶體。分配記憶體的代碼在IDA中的:
分配成功後,會將AllocateObject的傳回值作為GreCreateDIBitmapReal函數傳回值,並且賦值給xxxRealDrawMenuItem 函數中的[ebp+var_28]。
0x02 補丁對比
來看看微軟是怎麼來補這個漏洞的,補丁後的部分xxxRealDrawMenuItem函數代碼在IDA中的:
可以看到,補丁後,xxxRealDrawMenuItem函數在調用GreCreateDIBitmapReal函數後,對傳回值做了檢查:如果傳回值等於0,表示建立DIBObject失敗,則不會再進入到操作DIBObject的流程,也就是過程A中了。
0x03 可能的利用
目前還沒有人公開自己的利用代碼,下面對該漏洞存在的可能利用方式做一個說明。
crash處是一個對ecx進行迴圈操作的程式碼片段,迴圈次數為"imul eax,[ebp+arg_8]"的乘積。正常情況下是對申請的DIBObject進行操作。
這個迴圈操作中,存在一個可控的寫入操作指令,在IDA中的:
紅框中的指令就是所說的寫入操作指令,edx雖然經曆多條指令操作後才得到,但是參與操作的都是ecx相關記憶體值,因為ecx也就是零頁地址的內容可控,所以edx也是可控的。
那麼理論情況下,在win8之前的系統中,是可以將這條指令操作轉變為:
mov [HalDispatchTable+4],shellcodeAddress
之後使用者層再觸發一下就完成了提權。
0x04 其他
經過深入分析後,要觸發漏洞,r.bottom、r.top和info.bmiHeader.biWidth滿足一定的約束就行了,不一定需要和作者給出PoC的數值完全相同。r.bottom、r.top和info.bmiHeader.biWidth的值能滿足下面兩個條件就可以導致crash:
- 過程B分配DIBObject失敗。
- 過程A走向crash流程。
過程B和過程A要滿足這兩個條件,具體情況如下。
過程B
1. 過程B未發生溢出,並且乘積後的值 < 0x7FFFFFFF,造成AllocateObject調用失敗。
2. 過程B未發生溢出,並且乘積後的值 > 0x7FFFFFFF,不走調用AllocateObject的流程。
3. 過程B發生溢出,不走調用AllocateObject的流程。
過程A
1.未溢出。
2.溢出後的結果為負數,並且改變sf位為1(有符號數比較)。
根據上面分析給出的過程A和過程B會導致crash的情況,這裡給出兩種具體的組合,有興趣的可以修改原PoC中對應的值嘗試一下,都會crash的。
組合1
過程B情況1+過程A的情況1,兩個地方都沒有溢出,但是還是crash了,也說明了不是整數溢出漏洞。
組合2
過程B情況3+過程A情況2:
CVE-2016-0143 漏洞分析(2016.4)