漏洞背景
本人技術有限,有很多可能沒有分析到位的地方請各位大佬指點。
GDI利用技術很早就流行了,不過在國內 文章搜出來基本就幾篇。正好前段時間看到了利用GDI 提權漏洞,趕緊學習一波。
在2016年 2月份 微軟 修復了 CVE-2016-0040 核心級別的特權提升漏洞。該漏洞成因為 在 ntoskrnl 的WMI子系統中利用未初始化的堆疊變數
漏洞分析
一般分析一個CVE 我都會先去Kali 裡面看看
雖然 微軟 說影響情況很多 不過在kali中只提供了 Win7 sp0 /sp1 X64的利用。最後在GitHub上找到了 EXP 和利用的原始碼
利用成功情況
先對EXP 進行 裁剪 造成崩潰
可以看到裁剪 POC 在3環 通過控制碼 對0環 WMIDataDevice進行通訊
而傳送 的資料為 WMIRECEIVENOTIFICATION 這個結構
剛開始再 32位下 分析
可以看到觸發了漏洞 檢視 堆疊 是在 nt!WMIPRECEIVENOTIFICATIONS函式中。之後在wrk 原始碼中發現了此函式 (為分析漏洞已刪除 無關程式碼)
NTSTATUS WMIPRECEIVENOTIFICATIONS(
PWMIRECEIVENOTIFICATION RECEIVENOTIFICATION,
PULONG OUTBUFFERSIZE,
PIRP IRP
)
{
#DEFINE MANY_NOTIFICATION_OBJECTS 16
ULONG I;
PWMIGUIDOBJECT GUIDOBJECT;
ULONG HANDLECOUNT;
PHANDLE3264 HANDLEARRAY;
OBJECT_EVENT_INFO *OBJECTARRAY;
OBJECT_EVENT_INFO STATICOBJECTS[MANY_NOTIFICATION_OBJECTS]; //本地
#ENDIF
HANDLECOUNT = RECEIVENOTIFICATION->HANDLECOUNT; //攻擊點 r14d
HANDLEARRAY = RECEIVENOTIFICATION->HANDLES;
//
// CREATE SPACE TO STORE THE OBJECT POINTERS SO WE CAN WORK WITH THEM
//
IF (HANDLECOUNT > MANY_NOTIFICATION_OBJECTS)
{
OBJECTARRAY = WMIPALLOC(HANDLECOUNT * SIZEOF(OBJECT_EVENT_INFO));
IF (OBJECTARRAY == NULL)
{
RETURN(STATUS_INSUFFICIENT_RESOURCES);
}
} ELSE {
OBJECTARRAY = STATICOBJECTS;
}
#IF DBG
RTLZEROMEMORY(OBJECTARRAY, HANDLECOUNT * SIZEOF(OBJECT_EVENT_INFO));
#ENDIF
} ELSE IF (RECEIVENOTIFICATION->ACTION == RECEIVE_ACTION_CREATE_THREAD) {
GUIDOBJECT = OBJECTARRAY[0].GUIDOBJECT;//
GUIDOBJECT->USERMODECALLBACK = (PUSER_THREAD_START_ROUTINE)(ULONG_PTR)RECEIVENOTIFICATION->USERMODECALLBACK.HANDLE;
GUIDOBJECT->EVENTQUEUEACTION = RECEIVE_ACTION_CREATE_THREAD;
GUIDOBJECT->USERMODEPROCESS = USERMODEPROCESS;
GUIDOBJECT->STACKSIZE = STACKSIZE;
GUIDOBJECT->STACKCOMMIT = STACKCOMMIT;
THREADLISTHEAD = &GUIDOBJECT->THREADOBJECTLIST;
INITIALIZELISTHEAD(THREADLISTHEAD);
從函式原始碼 可以看到 如果 當傳入的HandleCount 為0時 就會使用本地一個陣列 。可以看到 這個陣列並沒進行 初始化 ,往下看 看到 在CHK 版本中進行了 初始化(。。。) 之後函式 會進行檢查一些操作 當action 為RECEIVE_ACTION_CREATE_THREAD 時 看到 對 ObjectArray 進行了使用 ,不過當設定 HandleCount 為0 時 ObjectArray 是未初始化 ,在下面部分 又對這個初始化的指標 進行了使用。在利用之後 就造成了典型的核心 任意記憶體 讀寫
GDI利用
GdiShareHandleTable Win32k!gpentHmgr的部分 對應程式中每個GDI物件
而其中每一項都使用 GDICELL64 此結構
而通過 一個GDI Handle 我們就可以知道表中的地址
在GDICELL64 結構中 pKernelAddress 指向 SURFACE
在 SURFOBJ 結構中 ,sizlBitmap 它是一個SIZEL 結構 系統 通過 該變數 來確定 bitMap點陣圖的長和寬 而 pvScan0和pvBits成員變數都表示 指向bitMap 點陣圖的指標
在該利用 還有兩個 關鍵的 函式 GetBitmapBits 該函式 主要用於 讀取 pvScan0 或者 pvBits 指標指向 的cBytes位元組 的Bitmap點陣圖內容 相反 SetBitmapBits 函式 則是對指向的bitMap點陣圖 進行寫入
在一些 paper 中 利用技巧為 建立兩個BitMap物件 (Manager/Worker) 之後通過 控制 Manager Bitmap 物件 的 sizelBitmap或者 pvScan0 成員 去 控制 Worker BitMap 物件 pvScan0 成員 的目的 最終實現核心任意記憶體 讀寫
總結 來說 就是 Manager 控制 著 Worker 讀取 和 寫入 的地址,而Worker 則通過 Set和Get BitmapBits 去對 該地址 進行 讀寫
最關鍵一點 需要 把Manager 的 pvScan0 指向 Worker的 pvScan0 一般通過漏洞去設定
漏洞利用
結合 GDI 利用基本原理 去分析 該漏洞 (分析時 藍屏真痛苦)
直接 對 nt!WmipReceiveNotifications 下斷
結合原始碼 去看 很容易去理解 可以看到 RSI 為本地陣列
因為又利用 核心棧噴射技巧 剛開始 調的時候 到後一直 BSOD 浪費了好長時間
可以看到 棧被噴成 HmangerAddress
在 該地址 加 50 的位置 就是 pvScan0
跟進去看一下 就是 利用程式碼中 建立的 點陣圖
在當初尋找相關文章的時候,我一直在想漏洞與 該 利用技術 的關係 。文章都說 利用 漏洞 去寫 pvScan0 。之後我一直除錯去找這個地方 。一直沒找到。最終還是在此漏洞 該EXP 並沒有這麼做 .
在 這位置 它把 pvBits 給了 pvScan0 .可以在看一下 SURFOBJ64結構
注意 現在 操作的Manager 結構
之後 走幾步 該函式 就結束了
而此時 的情況 為 pvBits 裡為它本身, pvScan0 為pvBits
那在哪把 hManger 的pvScan0 寫成 hWorker 的 ,直接對 pvScan0 下寫入斷點
有時候雙擊去除錯 列印的資訊都不顯示
因為除錯過 ,為方便除錯 插入了 _debugbreak
命中 了 硬體斷點 檢視 下 堆疊 是在 SetBitmapBits 中 進行了 memcopy 。此時看一下 hManger 和hWorker
可以看到 已經 寫進去了
這裡關於為什麼能 改變 hManger pvScan0 扯一下
在核心函式 離開之後 可以看到上面的圖 當時的 hManger pvScan0 為pvBits 而pvBits 裡面又是它自己
當呼叫 setBitmapBits 時 會先去找到 hManger 的pvScan0---->pvBits--->pvBits 所以在寫的時候 會直接 從pvBit地址覆蓋 也把pvScan0 覆蓋掉了
之後 的利用 就是 讀取 system token 內容 寫入 當前 程式 token 就不在詳細 寫了
最後
分析有錯的地方 請大佬指點下 。之後 把 搜到的 paper 和 原始碼 附上
核心噴射技術
本文作者:Cestlavie呀(看雪ID)
原文連結:https://bbs.pediy.com/thread-246433.htm
看雪推薦閱讀:
1、[翻譯]繞過資料執行保護
2、[原創]某Unity3D遊戲加固產品分析
3、Nexus6P 7.1.2 核心編譯修改 TracerPid
4、[原創]階乘演算法效能分析與 DOUBLE FAULT 藍屏故障排查 PART I
5、[翻譯]VR頭戴(HTC Vive)裝置內的現實危險