execute_python is now safe + test

This commit is contained in:
Clement Rouault
2015-12-22 16:17:30 +01:00
parent dea58c8c77
commit 5a29c59b3e
7 changed files with 136 additions and 16 deletions
+9 -1
View File
@@ -3,7 +3,7 @@ from ctypes import *
from ctypes.wintypes import *
from .winstructs import *
functions = ['ExitProcess', 'TerminateProcess', 'GetLastError', 'GetCurrentProcess', 'CreateFileA', 'CreateFileW', 'NtQuerySystemInformation', 'NtQueryInformationProcess', 'NtQueryVirtualMemory', 'NtCreateThreadEx', 'NtQueryInformationThread', 'VirtualAlloc', 'VirtualAllocEx', 'VirtualFree', 'VirtualFreeEx', 'VirtualProtect', 'VirtualQuery', 'VirtualQueryEx', 'GetModuleFileNameA', 'GetModuleFileNameW', 'CreateThread', 'CreateRemoteThread', 'VirtualProtect', 'CreateProcessA', 'CreateProcessW', 'GetThreadContext', 'NtGetContextThread', 'SetThreadContext', 'OpenThread', 'OpenProcess', 'CloseHandle', 'ReadProcessMemory', 'NtWow64ReadVirtualMemory64', 'WriteProcessMemory', 'CreateToolhelp32Snapshot', 'Thread32First', 'Thread32Next', 'Process32First', 'Process32Next', 'Process32FirstW', 'Process32NextW', 'GetProcAddress', 'LoadLibraryA', 'LoadLibraryW', 'OpenProcessToken', 'LookupPrivilegeValueA', 'LookupPrivilegeValueW', 'AdjustTokenPrivileges', 'FindResourceA', 'FindResourceW', 'SizeofResource', 'LoadResource', 'LockResource', 'GetVersionExA', 'GetVersionExW', 'GetVersion', 'GetCurrentThread', 'GetCurrentThreadId', 'GetCurrentProcessorNumber', 'AllocConsole', 'FreeConsole', 'GetStdHandle', 'SetStdHandle', 'SetThreadAffinityMask', 'WriteFile', 'GetExtendedTcpTable', 'GetExtendedUdpTable', 'SetTcpEntry', 'AddVectoredContinueHandler', 'AddVectoredExceptionHandler', 'TerminateThread', 'ExitThread', 'RemoveVectoredExceptionHandler', 'ResumeThread', 'SuspendThread', 'WaitForSingleObject', 'GetThreadId', 'LoadLibraryExA', 'LoadLibraryExW', 'SymInitialize', 'SymFromName', 'SymLoadModuleEx', 'SymSetOptions', 'SymGetTypeInfo', 'DeviceIoControl', 'GetTokenInformation', 'RegOpenKeyExA', 'RegOpenKeyExW', 'RegGetValueA', 'RegGetValueW', 'RegCloseKey', 'Wow64DisableWow64FsRedirection', 'Wow64RevertWow64FsRedirection', 'Wow64EnableWow64FsRedirection', 'Wow64GetThreadContext', 'SetConsoleCtrlHandler']
functions = ['ExitProcess', 'TerminateProcess', 'GetLastError', 'GetCurrentProcess', 'CreateFileA', 'CreateFileW', 'NtQuerySystemInformation', 'NtQueryInformationProcess', 'NtQueryVirtualMemory', 'NtCreateThreadEx', 'NtQueryInformationThread', 'GetExitCodeThread', 'GetExitCodeProcess', 'VirtualAlloc', 'VirtualAllocEx', 'VirtualFree', 'VirtualFreeEx', 'VirtualProtect', 'VirtualQuery', 'VirtualQueryEx', 'GetModuleFileNameA', 'GetModuleFileNameW', 'CreateThread', 'CreateRemoteThread', 'VirtualProtect', 'CreateProcessA', 'CreateProcessW', 'GetThreadContext', 'NtGetContextThread', 'SetThreadContext', 'OpenThread', 'OpenProcess', 'CloseHandle', 'ReadProcessMemory', 'NtWow64ReadVirtualMemory64', 'WriteProcessMemory', 'CreateToolhelp32Snapshot', 'Thread32First', 'Thread32Next', 'Process32First', 'Process32Next', 'Process32FirstW', 'Process32NextW', 'GetProcAddress', 'LoadLibraryA', 'LoadLibraryW', 'OpenProcessToken', 'LookupPrivilegeValueA', 'LookupPrivilegeValueW', 'AdjustTokenPrivileges', 'FindResourceA', 'FindResourceW', 'SizeofResource', 'LoadResource', 'LockResource', 'GetVersionExA', 'GetVersionExW', 'GetVersion', 'GetCurrentThread', 'GetCurrentThreadId', 'GetCurrentProcessorNumber', 'AllocConsole', 'FreeConsole', 'GetStdHandle', 'SetStdHandle', 'SetThreadAffinityMask', 'WriteFile', 'GetExtendedTcpTable', 'GetExtendedUdpTable', 'SetTcpEntry', 'AddVectoredContinueHandler', 'AddVectoredExceptionHandler', 'TerminateThread', 'ExitThread', 'RemoveVectoredExceptionHandler', 'ResumeThread', 'SuspendThread', 'WaitForSingleObject', 'GetThreadId', 'LoadLibraryExA', 'LoadLibraryExW', 'SymInitialize', 'SymFromName', 'SymLoadModuleEx', 'SymSetOptions', 'SymGetTypeInfo', 'DeviceIoControl', 'GetTokenInformation', 'RegOpenKeyExA', 'RegOpenKeyExW', 'RegGetValueA', 'RegGetValueW', 'RegCloseKey', 'Wow64DisableWow64FsRedirection', 'Wow64RevertWow64FsRedirection', 'Wow64EnableWow64FsRedirection', 'Wow64GetThreadContext', 'SetConsoleCtrlHandler']
# ExitProcess(uExitCode):
ExitProcessPrototype = WINFUNCTYPE(VOID, UINT)
@@ -49,6 +49,14 @@ NtCreateThreadExParams = ((1, 'ThreadHandle'), (1, 'DesiredAccess'), (1, 'Object
NtQueryInformationThreadPrototype = WINFUNCTYPE(NTSTATUS, HANDLE, THREAD_INFORMATION_CLASS, PVOID, ULONG, PULONG)
NtQueryInformationThreadParams = ((1, 'ThreadHandle'), (1, 'ThreadInformationClass'), (1, 'ThreadInformation'), (1, 'ThreadInformationLength'), (1, 'ReturnLength'))
# GetExitCodeThread(hThread, lpExitCode):
GetExitCodeThreadPrototype = WINFUNCTYPE(BOOL, HANDLE, LPDWORD)
GetExitCodeThreadParams = ((1, 'hThread'), (1, 'lpExitCode'))
# GetExitCodeProcess(hProcess, lpExitCode):
GetExitCodeProcessPrototype = WINFUNCTYPE(BOOL, HANDLE, LPDWORD)
GetExitCodeProcessParams = ((1, 'hProcess'), (1, 'lpExitCode'))
# VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect):
VirtualAllocPrototype = WINFUNCTYPE(LPVOID, LPVOID, SIZE_T, DWORD, DWORD)
VirtualAllocParams = ((1, 'lpAddress'), (1, 'dwSize'), (1, 'flAllocationType'), (1, 'flProtect'))
-1
View File
@@ -858,7 +858,6 @@ PM128A = POINTER(_M128A)
# Struct _CONTEXT64 definitions
class _CONTEXT64(Structure):
_pack_ = 8
_fields_ = [
("P1Home", DWORD64),
("P2Home", DWORD64),
+41 -2
View File
@@ -1,3 +1,6 @@
import struct
import ctypes
import windows
import windows.utils as utils
@@ -55,7 +58,7 @@ def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_
code = x64.MultipleInstr()
# Do stack alignement
code += x64.Push('RAX')
code += x64.Push('RCX')
# Load python27.dll
code += x64.Mov('RCX', PYDLL_addr)
code += x64.Mov('RAX', LoadLibraryA)
@@ -89,7 +92,7 @@ def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_
code += x64.Call('RAX')
code += Clean_space_for_call
# Remove stack alignement
code += x64.Pop('RAX')
code += x64.Pop('RCX')
code += x64.Ret()
return code.get_code()
@@ -130,3 +133,39 @@ def inject_python_command(process, code_injected, PYDLL="python27.dll\x00"):
def execute_python_code(process, code):
shellcode_remote_addr = inject_python_command(process, code)
return process.create_thread(shellcode_remote_addr, 0)
retrieve_exc = r"""
import traceback
import sys
addr = {0}
txt = "".join(traceback.format_exception(sys.last_type, sys.last_value, sys.last_traceback))
import ctypes
size = ctypes.c_uint.from_address(addr)
size.value = len(txt)
buff = (ctypes.c_char * len(txt)).from_address(addr + ctypes.sizeof(ctypes.c_uint))
buff[:] = txt
"""
def retrieve_last_exception_data(process):
# TODO : FREE THIS
mem = process.virtual_alloc(0x1000)
execute_python_code(process, retrieve_exc.format(mem))
size = struct.unpack("<I", process.read_memory(mem, ctypes.sizeof(ctypes.c_uint)))[0]
data = process.read_memory(mem + ctypes.sizeof(ctypes.c_uint), size)
return data
class RemotePythonError(Exception):
pass
def safe_execute_python(process, code):
t = execute_python_code(process, code)
t.wait() # Wait terminaison of the thread
if t.exit_code == 0:
return True
if t.exit_code != 0xffffffff:
raise ValueError("Unknown exit code {0}".format(hex(t.exit_code)))
data = retrieve_last_exception_data(process)
raise RemotePythonError(data)
+1 -2
View File
@@ -1,4 +1,3 @@
from mytest import WindowsTestCase, pop_calc_32, pop_calc_64
from mytest import WindowsTestCase, pop_calc_32, pop_calc_64, Calc32, Calc64
__all__ = ["WindowsTestCase"]
+19 -4
View File
@@ -129,7 +129,7 @@ class WindowsTestCase(unittest.TestCase):
with Calc64() as calc:
data = calc.virtual_alloc(0x1000)
calc.execute_python('import ctypes; ctypes.c_uint.from_address({0}).value = 0x42424242'.format(data))
time.sleep(0.1)
#time.sleep(0.1)
dword = struct.unpack("<I", calc.read_memory(data, 4))[0]
self.assertEqual(dword, 0x42424242)
@@ -137,7 +137,7 @@ class WindowsTestCase(unittest.TestCase):
with Calc32() as calc:
data = calc.virtual_alloc(0x1000)
calc.execute_python('import ctypes; ctypes.c_uint.from_address({0}).value = 0x42424242'.format(data))
time.sleep(0.1)
#time.sleep(0.1)
dword = struct.unpack("<I", calc.read_memory(data, 4))[0]
self.assertEqual(dword, 0x42424242)
@@ -169,7 +169,7 @@ class WindowsTestCase(unittest.TestCase):
ctypes.c_uint.from_address({1}).value = GetCurrentProcessId
""".format(os.getcwd(), data)
calc.execute_python(textwrap.dedent(remote_python_code))
time.sleep(0.5)
#time.sleep(0.5)
dword = struct.unpack("<I", calc.read_memory(data, 4))[0]
self.assertEqual(dword, get_current_proc_id)
@@ -190,7 +190,7 @@ class WindowsTestCase(unittest.TestCase):
ctypes.c_ulonglong.from_address({1}).value = GetCurrentProcessId
""".format(os.getcwd(), data)
calc.execute_python(textwrap.dedent(remote_python_code))
time.sleep(0.5)
#time.sleep(0.5)
dword = struct.unpack("<Q", calc.read_memory(data, 8))[0]
self.assertEqual(dword, get_current_proc_id)
@@ -228,6 +228,21 @@ class WindowsTestCase(unittest.TestCase):
_winreg.OpenKey(*open_args)
self.assertEqual(ar.exception.winerror, 0x11223344)
def test_thread_exit_value_32(self):
with Calc32() as calc:
res = calc.execute_python("import time;time.sleep(0.1); 2")
self.assertEqual(res, True)
with self.assertRaises(windows.injection.RemotePythonError) as ar:
t = calc.execute_python("import time;time.sleep(0.1); raise ValueError('BYE')")
def test_thread_exit_value_64(self):
with Calc64() as calc:
res = calc.execute_python("import time;time.sleep(0.1); 2")
self.assertEqual(res, True)
with self.assertRaises(windows.injection.RemotePythonError) as ar:
t = calc.execute_python("import time;time.sleep(0.1); raise ValueError('BYE')")
if __name__ == '__main__':
alltests = unittest.TestSuite()
+1 -1
View File
@@ -11,7 +11,7 @@ def fixedpropety(f):
except AttributeError:
setattr(self, cache_name, f(self))
return getattr(self, cache_name)
return property(prop)
return property(prop, doc=f.__doc__)
def swallow_ctypes_copy(ctypes_object):
+65 -5
View File
@@ -25,7 +25,7 @@ import windows.pe_parse as pe_parse
class AutoHandle(object):
"""An abstract class that allow easy handle creation/destruction"""
"""An abstract class that allow easy handle creation/destruction/wait"""
def _get_handle(self):
raise NotImplementedError("{0} is abstract".format(type(self).__name__))
@@ -44,6 +44,9 @@ class AutoHandle(object):
self._handle = self._get_handle()
return self._handle
def wait(self, timeout=INFINITE):
return winproxy.WaitForSingleObject(self.handle, timeout)
def __del__(self):
if hasattr(self, "_handle") and self._handle:
winproxy.CloseHandle(self._handle)
@@ -156,6 +159,16 @@ class WinThread(THREADENTRY32, AutoHandle):
def _get_handle(self):
return winproxy.OpenThread(dwThreadId=self.tid)
@property
def is_exit(self):
return self.exit_code != STILL_ACTIVE
@property
def exit_code(self):
res = DWORD()
winproxy.GetExitCodeThread(self.handle, byref(res))
return res.value
def __repr__(self):
owner = self.owner
if owner is None:
@@ -168,9 +181,32 @@ class WinThread(THREADENTRY32, AutoHandle):
def _from_handle(handle):
tid = winproxy.GetThreadId(handle)
try:
return [t for t in System().threads if t.tid == tid][0]
# Really useful ?
thread = [t for t in System().threads if t.tid == tid][0]
# set AutoHandle _handle
thread._handle = handle
return thread
except IndexError:
return (tid, handle)
return DeadThread(handle, tid)
class DeadThread(AutoHandle):
"""A simple object arround an already dead thread"""
def __init__(self, handle, tid=None):
if tid is None:
tid = winproxy.GetThreadId(handle)
self.tid = tid
# set AutoHandle _handle
self._handle = handle
@property
def is_exit(self):
return self.exit_code != STILL_ACTIVE
@property
def exit_code(self):
res = DWORD()
winproxy.GetExitCodeThread(self.handle, byref(res))
return res.value
class Process(AutoHandle):
@@ -207,6 +243,16 @@ class Process(AutoHandle):
def virtual_alloc(self, size):
raise NotImplementedError("virtual_alloc")
@property
def exit_code(self):
res = DWORD()
winproxy.GetExitCodeProcess(self.handle, byref(res))
return res.value
@property
def is_exit(self):
return self.exit_code == STILL_ACTIVE
def execute(self, code):
"""Execute some raw code in the context of the process"""
x = self.virtual_alloc(len(code))
@@ -267,6 +313,10 @@ class CurrentThread(AutoHandle):
"""Exit the thread"""
return winproxy.ExitThread(code)
def wait(self):
"""Raise ValueError to prevent deadlock :D"""
raise ValueError("wait() on current thread")
class CurrentProcess(Process):
"""The current process"""
@@ -348,7 +398,7 @@ class CurrentProcess(Process):
return True
def read_memory(self, addr, size):
"""Read size from adddr"""
"""Read size from addr"""
dbgprint('Read CurrentProcess Memory', 'READMEM')
buffer = (c_char * size).from_address(addr)
return buffer[:]
@@ -366,6 +416,10 @@ class CurrentProcess(Process):
"""Exit the process"""
return winproxy.ExitProcess(code)
def wait(self):
"""Raise ValueError to prevent deadlock :D"""
raise ValueError("wait() on current thread")
class WinProcess(PROCESSENTRY32, Process):
"""A Process on the system"""
@@ -453,7 +507,9 @@ class WinProcess(PROCESSENTRY32, Process):
def create_thread(self, addr, param):
"""Create a remote thread"""
if windows.current_process.bitness == 32 and self.bitness == 64:
return windows.syswow64.NtCreateThreadEx_32_to_64(ProcessHandle=self.handle, lpStartAddress=addr, lpParameter=param)
thread_handle = HANDLE()
windows.syswow64.NtCreateThreadEx_32_to_64(ThreadHandle=byref(thread_handle) ,ProcessHandle=self.handle, lpStartAddress=addr, lpParameter=param)
return WinThread._from_handle(thread_handle.value)
return WinThread._from_handle(winproxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param))
def load_library(self, dll_path):
@@ -464,6 +520,10 @@ class WinProcess(PROCESSENTRY32, Process):
return self.create_thread(LoadLibrary, x)
def execute_python(self, pycode):
"""Execute Python code into the remote process"""
return injection.safe_execute_python(self, pycode)
def execute_python_unsafe(self, pycode):
"""Execute Python code into the remote process"""
return injection.execute_python_code(self, pycode)