示例#1
0
class PykdTarget(ServerTarget):
    '''
    PykdTarget will run an application for each fuzzed payloads.
    To use PykdTarget your need install PYkd and Windbg first.
    Document link https://pykd.codeplex.com/documentation.
    '''
    def __init__(self,
                 name,
                 process_path,
                 process_args=[],
                 break_points=[],
                 handler=None,
                 logger=None,
                 timeout=3):
        '''

        :param name: name of the object.
        :param process_path: path to the target executable.
        :param process_args: arguments to pass to the process.
        :param break_points: break points to set.
        :param handler: pykd event handler.
        :param logger: logger for this object (default: None)
        :param timeout: seconds to wait for the process before kill (default: 3)

        :example:

            ::
                PykdTarget(
                    name='PykdTarget',
                    process_path="/tmp/myApp",
                    process_args=['-a', '-c']
                    handler=MyEventHandler,
                    break_points=[],
                    timeout=2)

            Will run ``/tmp/myApp -a -c mutational_data`` using pykd for evey mutation with timeout of 2 seconds

        '''
        super(PykdTarget, self).__init__(name, logger)
        assert (process_path)
        assert (os.path.exists(process_path))
        self._process_path = process_path
        self._process_name = os.path.basename(process_path)
        self._process_args = process_args
        self.process_data = None
        self._break_points = break_points
        self._handler = handler
        self._timeout = timeout
        self._process = None
        self._pid = None
        self._system_pid = None
        self.is_crash = threading.Event()
        self.crash_dump_finished = threading.Event()

    def _get_exe_status(self):
        try:
            status = str(pykd.getExecutionStatus())
            return status
        except Exception as err:
            self.logger.debug(
                "Can't get process execution status because of: %s" % err)
            return None

    def _wait_break(self, timeout=2):
        '''
        :param timeout: timeout in seconds. (default: 2)
        :return: True if Server is in break status, False otherwise
        '''
        end_time = time.time() + timeout
        delay = 0.0005  # 500 us -> initial delay of 1 ms
        while True:
            remaining = end_time - time.time()
            if remaining <= 0:
                return False
            time.sleep(delay)
            try:
                status = self._get_exe_status()
                if status == 'Break':
                    self.logger.debug('Process is in break status')
                    return True
            except Exception as err:
                self.logger.error(
                    "Received exception when wait process break: %s" % err)
                continue

    def _get_correct_process_id(self):
        while self._pid == 0xffffffff:
            try:
                self._pid = pykd.getCurrentProcessId()
            except Exception as err:
                self.logger.debug(
                    "Can't get correct process id because of: %s" % err)
                continue

    def _debug_server(self):
        '''
        debugger thread
        '''
        self._system_pid = None
        self.logger.info('Init pykd environment')
        pykd.initialize()
        try:
            # Start a new process for debugging
            argv = [self._process_path
                    ] + self._process_args + self.process_data
            argv = ' '.join(argv)
            self.logger.debug('Debugger starting server: %s' % argv)
            try:
                self.logger.info('Start running program with cmd:"%s"' % argv)
                self.report.add('cmd', argv)
                self._pid = pykd.startProcess(argv)
                self._get_correct_process_id()
                self.logger.debug('Process started. pykd_pid=%d' % self._pid)
                self._process = pykd.getCurrentProcess()
                self.logger.debug('Process is %s' % hex(self._process))
            except WindowsError:
                self.logger.error('debug_server received exception',
                                  traceback.fmt_exc())
            # Get Process System ID
            self._wait_break()
            while self._system_pid is None:
                try:
                    self._system_pid = pykd.getProcessSystemID(self._pid)
                    self.logger.info('process system_id=%d' % self._system_pid)
                except Exception as err:
                    self.logger.debug("Get system id fail because of: %s" %
                                      err)
                    continue
            # Set break points
            if self._wait_break():
                self.logger.info(
                    "Server is in break status setting break points")
                for bp in self._break_points:
                    pykd.setBp(bp)
                self.logger.info("Start register event handle")
                # This will register our handle
                handler = self._handler(self)
                self.logger.debug('Handler object is : %s' % handler)
                self.logger.info('Go !!!!!')
                pykd.go()
        except:
            self.logger.error('Got an exception in _debug_server')
            self.logger.error(traceback.format_exc())

    def _start_server_thread(self):
        '''
        start the server thread
        '''
        self._server_thread = FuncThread(self._debug_server)
        self._server_thread.start()

    def _kill_all_processes(self):
        '''
        kill all processes with the same name
        :return: True if all matching processes were killed properly, False otherwise
        '''
        if self._process:
            try:
                status = self._get_exe_status()
                self.logger.debug("Current status is %s start kill process" %
                                  status)
                if status == 'Break':
                    self.logger.info(
                        "Process is in break status, kill process now")
                    pykd.killAllProcesses()
                    self._pid = None
                    self._process = None
                else:
                    self.logger.info("Break in process, kill process now")
                    pykd.breakin()
                    self._wait_break()
                    # TODO: need find a way to replace time.sleep
                    time.sleep(0.05)
                    pykd.killAllProcesses()
                    self._pid = None
                    self._process = None
            except:
                self.logger.error('failed to kill process [%s]' %
                                  traceback.format_exc())
                return False
            return True

    def teardown(self):
        self._stop_process()
        self._pid = None
        self._process = None
        super(PykdTarget, self).teardown()

    def pre_test(self, test_number):
        super(PykdTarget, self).pre_test(test_number)
        self.is_crash.clear()
        self.crash_dump_finished.clear()
        self._stop_process()

    def post_test(self, test_num):
        if self.is_crash.wait(self._timeout):
            self.crash_dump_finished.wait(self._timeout)
        self._stop_process()
        # for each test case we need deinitialize the pykd, other wise event handle will not work.
        pykd.deinitialize()
        super(PykdTarget, self).post_test(test_num)

    def _send_to_target(self, payload):
        '''

        :param payload: payload to send (might be file path)
        '''
        self.logger.debug('send called')
        self.process_data = [payload]
        self._start_server_thread()

    def _stop_process(self):
        '''
        Stop the process (if running)
        '''
        return self._kill_all_processes()

    def _restart(self):
        '''
        restart the process
        '''
        self._stop_process()
        self._server_thread.join(1)
        self._start_server_thread()
class WinAppDbgTarget(ServerTarget):
    def __init__(self,
                 name,
                 process_path,
                 process_args=[],
                 sql_crash_db='sqlite:///crashes.sqlite',
                 logger=None):
        '''
        :param name: name of the object
        :param process_path: path to the target executable
        :param process_args: arguments to pass to the process
        :param attach: try to attach if process path
        :param sql_crash_db: sql alchemy connection string to crash db (default:sqlite:///crashes.sqlite)
        :param logger: logger for this object (default: None)
        '''
        super(WinAppDbgTarget, self).__init__(name, logger)
        assert (process_path)
        assert (os.path.exists(process_path))
        self._process_path = process_path
        self._process_name = os.path.basename(process_path)
        self._process_args = process_args
        self._process = None
        self._sql_crash_db = sql_crash_db
        self._crash_event_complete = threading.Event()
        self._server_is_up = threading.Event()
        self._crash_event_complete.set()
        self._debug = Debug(lambda x: _my_event_handler(self, x),
                            bKillOnExit=True)

    def _debug_server(self):
        '''
        debugger thread
        '''
        try:
            self._process = None
            # Start a new process for debugging.
            argv = [self._process_path] + self._process_args
            self.logger.debug('debugger starting server: %s' % argv)
            try:
                self._process = self._debug.execv(argv, bFollow=True)
            except WindowsError:
                self.logger.error('debug_server received exception',
                                  traceback.fmt_exc())
            self._pid = self._process.get_pid()
            self.logger.info('process started. pid=%d' % self._pid)

            # Wait for the debugee to finish.
            self._server_is_up.set()
            self._debug.loop()
        except:
            self.logger.error('Got an exception in _debug_server')
            self.logger.error(traceback.format_exc())
        # Stop the debugger.
        finally:
            self._debug.stop()
            self._process = None
            self._pid = -1
            self._crash_event_complete.set()

    def _start_server_thread(self):
        '''
        start the server thread
        '''
        self._server_is_up.clear()
        self.server_thread = FuncThread(self._debug_server)
        self.server_thread.start()
        self.logger.info('waiting for server to be up')
        self._server_is_up.wait()
        self.logger.info('server should be up')

    def _kill_all_processes(self):
        '''
        kill all processes with the same name
        :return: True if all matching processes were killed properly, False otherwise
        '''
        res = True
        # Lookup the currently running processes.
        self._debug.system.scan_processes()
        # For all processes that match the requested filename...
        for (process, name) in self._debug.system.find_processes_by_filename(
                self._process_name):
            process_pid = process.get_pid()
            self.logger.info('found process %s (%d) - trying to kill it' %
                             (name, process_pid))
            try:
                process.kill()
                self.logger.info('successfully killed %s (%d)' %
                                 (name, process_pid))
            except:
                self.logger.error('failed to kill %s (%d) [%s]' %
                                  (name, process_pid, traceback.format_exc()))
                res = False
        return res

    def teardown(self):
        self._stop_process()
        self._process = None
        super(WinAppDbgTarget, self).teardown()

    def pre_test(self, test_number):
        # we need kill all process before fuzzing
        super(WinAppDbgTarget, self).pre_test(test_number)
        self._stop_process()

    def post_test(self, test_num):
        self.logger.debug('in')
        time.sleep(1)
        self.logger.debug('after sleep')
        res = self._crash_event_complete.wait()
        self.logger.debug('after wait')
        if not res:
            self.report.failed('incomplete crash detected')
        super(WinAppDbgTarget, self).post_test(test_num)
        self.logger.debug('out')

    def _send_to_target(self, data):
        '''
        this is the key off windbgtarget
        :param data: data is the file path
        '''
        self.logger.info('send called')
        '''
        starting
        '''
        self._process_args = [data]  # this may need implement
        self._start_server_thread()

    def _stop_process(self):
        '''
        Stop the process (if running)
        '''
        return self._kill_all_processes()

    def _stop_process_old(self):
        '''
        :return: True if process was killed, False otherwise
        '''
        if self._is_victim_alive():
            self._process.kill()
            time.sleep(0.5)
            if self._is_victim_alive():
                self._process.kill()
                time.sleep(0.5)
                if self._is_victim_alive():
                    raise Exception('Failed to kill client process')
            self._debug.stop()
            return True
        else:
            self._debug.stop()
            return False

    def _restart(self):
        '''
        restart the process
        '''
        self._stop_process()
        self.server_thread.join(1)
        time.sleep(3)
        self._server_is_up.clear()
        self._start_server_thread()

    def _is_victim_alive(self):
        '''
        check if process running
        '''
        if self._process:
            self.logger.debug('process pid: %d' % self._pid)
            is_alive = self._process.is_alive()
            is_debugee_started = self._debug.is_debugee_started(self._pid)
            self.logger.debug('is_alive = %s' % is_alive)
            self.logger.debug('is_debugee_started = %s' % is_debugee_started)
            return (is_alive and is_debugee_started)
        else:
            self.logger.debug('_process is None')
        return False
示例#3
0
class PykdTarget(ServerTarget):
    '''
    PykdTarget will run an application for each fuzzed payloads.
    To use PykdTarget your need install PYkd and Windbg first.
    Document link https://pykd.codeplex.com/documentation.
    '''

    def __init__(self, name, process_path, process_args=[], break_points=[], handler=None, logger=None, timeout=3):
        '''

        :param name: name of the object.
        :param process_path: path to the target executable.
        :param process_args: arguments to pass to the process.
        :param break_points: break points to set.
        :param handler: pykd event handler.
        :param logger: logger for this object (default: None)
        :param timeout: seconds to wait for the process before kill (default: 3)

        :example:

            ::

                PykdTarget(
                    name='PykdTarget',
                    process_path="/tmp/myApp",
                    process_args=['-a', '-c']
                    handler=MyEventHandler,
                    break_points=[],
                    timeout=2)

            Will run ``/tmp/myApp -a -c mutational_data`` using pykd for evey mutation with timeout of 2 seconds

        '''
        super(PykdTarget, self).__init__(name, logger)
        assert(process_path)
        assert(os.path.exists(process_path))
        self._process_path = process_path
        self._process_name = os.path.basename(process_path)
        self._process_args = process_args
        self.process_data = None
        self._break_points = break_points
        self._handler = handler
        self._timeout = timeout
        self._process = None
        self._pid = None
        self._system_pid = None
        self.is_crash = threading.Event()
        self.crash_dump_finished = threading.Event()

    def _get_exe_status(self):
        try:
            status = str(pykd.getExecutionStatus())
            return status
        except Exception as err:
            self.logger.debug("Can't get process execution status because of: %s" % err)
            return None

    def _wait_break(self, timeout=2):
        '''
        :param timeout: timeout in seconds. (default: 2)
        :return: True if Server is in break status, False otherwise
        '''
        end_time = time.time() + timeout
        delay = 0.0005  # 500 us -> initial delay of 1 ms
        while True:
            remaining = end_time - time.time()
            if remaining <= 0:
                return False
            time.sleep(delay)
            try:
                status = self._get_exe_status()
                if status == 'Break':
                    self.logger.debug('Process is in break status')
                    return True
            except Exception as err:
                self.logger.error("Received exception when wait process break: %s" % err)
                continue

    def _get_correct_process_id(self):
        while self._pid == 0xffffffff:
            try:
                self._pid = pykd.getCurrentProcessId()
            except Exception as err:
                self.logger.debug("Can't get correct process id because of: %s" % err)
                continue

    def _debug_server(self):
        '''
        debugger thread
        '''
        self._system_pid = None
        self.logger.info('Init pykd environment')
        pykd.initialize()
        try:
            # Start a new process for debugging
            argv = [self._process_path] + self._process_args + self.process_data
            argv = ' '.join(argv)
            self.logger.debug('Debugger starting server: %s' % argv)
            try:
                self.logger.info('Start running program with cmd:"%s"' % argv)
                self.report.add('cmd', argv)
                self._pid = pykd.startProcess(argv)
                self._get_correct_process_id()
                self.logger.debug('Process started. pykd_pid=%d' % self._pid)
                self._process = pykd.getCurrentProcess()
                self.logger.debug('Process is %s' % hex(self._process))
            except WindowsError:
                self.logger.error('debug_server received exception', traceback.fmt_exc())
            # Get Process System ID
            self._wait_break()
            while self._system_pid is None:
                try:
                    self._system_pid = pykd.getProcessSystemID(self._pid)
                    self.logger.info('process system_id=%d' % self._system_pid)
                except Exception as err:
                    self.logger.debug("Get system id fail because of: %s" % err)
                    continue
            # Set break points
            if self._wait_break():
                self.logger.info("Server is in break status setting break points")
                for bp in self._break_points:
                    pykd.setBp(bp)
                self.logger.info("Start register event handle")
                # This will register our handle
                handler = self._handler(self)
                self.logger.debug('Handler object is : %s' % handler)
                self.logger.info('Go !!!!!')
                pykd.go()
        except:
            self.logger.error('Got an exception in _debug_server')
            self.logger.error(traceback.format_exc())

    def _start_server_thread(self):
        '''
        start the server thread
        '''
        self._server_thread = FuncThread(self._debug_server)
        self._server_thread.start()

    def _kill_all_processes(self):
        '''
        kill all processes with the same name
        :return: True if all matching processes were killed properly, False otherwise
        '''
        if self._process:
            try:
                status = self._get_exe_status()
                self.logger.debug("Current status is %s start kill process" % status)
                if status == 'Break':
                    self.logger.info("Process is in break status, kill process now")
                    pykd.killAllProcesses()
                    self._pid = None
                    self._process = None
                else:
                    self.logger.info("Break in process, kill process now")
                    pykd.breakin()
                    self._wait_break()
                    # TODO: need find a way to replace time.sleep
                    time.sleep(0.05)
                    pykd.killAllProcesses()
                    self._pid = None
                    self._process = None
            except:
                self.logger.error('failed to kill process [%s]' % traceback.format_exc())
                return False
            return True

    def teardown(self):
        self._stop_process()
        self._pid = None
        self._process = None
        super(PykdTarget, self).teardown()

    def pre_test(self, test_number):
        super(PykdTarget, self).pre_test(test_number)
        self.is_crash.clear()
        self.crash_dump_finished.clear()
        self._stop_process()

    def post_test(self, test_num):
        if self.is_crash.wait(self._timeout):
            self.crash_dump_finished.wait(self._timeout)
        self._stop_process()
        # for each test case we need deinitialize the pykd, other wise event handle will not work.
        pykd.deinitialize()
        super(PykdTarget, self).post_test(test_num)

    def _send_to_target(self, payload):
        '''

        :param payload: payload to send (might be file path)
        '''
        self.logger.debug('send called')
        self.process_data = [payload]
        self._start_server_thread()

    def _stop_process(self):
        '''
        Stop the process (if running)
        '''
        return self._kill_all_processes()

    def _restart(self):
        '''
        restart the process
        '''
        self._stop_process()
        self._server_thread.join(1)
        self._start_server_thread()
示例#4
0
class WinAppDbgController(BaseController):
    '''
    WinAppDbgController controls a server process
    by starting it on setup making sure it stays up.
    It uses winappdbg to attach to the target processes.
    '''

    def __init__(self, name, process_path, process_args=[], sql_crash_db='sqlite:///crashes.sqlite', logger=None):
        '''
        :param name: name of the object
        :param process_path: path to the target executable
        :param process_args: arguments to pass to the process
        :param attach: try to attach if process path
        :param sql_crash_db: sql alchemy connection string to crash db (default:sqlite:///crashes.sqlite)
        :param logger: logger for this object (default: None)
        '''
        super(WinAppDbgController, self).__init__(name, logger)
        assert(process_path)
        assert(os.path.exists(process_path))
        self._process_path = process_path
        self._process_name = os.path.basename(process_path)
        self._process_args = process_args
        self._process = None
        self._sql_crash_db = sql_crash_db
        self._crash_event_complete = threading.Event()
        self._server_is_up = threading.Event()
        self._crash_event_complete.set()
        self._debug = Debug(lambda x: _my_event_handler(self, x), bKillOnExit=True)

    def _debug_server(self):
        '''
        debugger thread
        '''
        try:
            self._process = None
            # Start a new process for debugging.
            argv = [self._process_path] + self._process_args
            self.logger.debug('debugger starting server: %s' % argv)
            try:
                self._process = self._debug.execv(argv, bFollow=True)
            except WindowsError:
                self.logger.error('debug_server received exception', traceback.fmt_exc())
            self._pid = self._process.get_pid()
            self.logger.info('process started. pid=%d' % self._pid)

            # Wait for the debugee to finish.
            self._server_is_up.set()
            self._debug.loop()
        except:
            self.logger.error('Got an exception in _debug_server')
            self.logger.error(traceback.format_exc())
        # Stop the debugger.
        finally:
            self._debug.stop()
            self._process = None
            self._pid = -1
            self._crash_event_complete.set()

    def _start_server_thread(self):
        '''
        start the server thread
        '''
        self._server_is_up.clear()
        self.server_thread = FuncThread(self._debug_server)
        self.server_thread.start()
        self.logger.info('waiting for server to be up')
        self._server_is_up.wait()
        self.logger.info('server should be up')

    def _kill_all_processes(self):
        '''
        kill all processes with the same name
        :return: True if all matching processes were killed properly, False otherwise
        '''
        res = True
        # Lookup the currently running processes.
        self._debug.system.scan_processes()
        # For all processes that match the requested filename...
        for (process, name) in self._debug.system.find_processes_by_filename(self._process_name):
            process_pid = process.get_pid()
            self.logger.info('found process %s (%d) - trying to kill it' % (name, process_pid))
            try:
                process.kill()
                self.logger.info('successfully killed %s (%d)' % (name, process_pid))
            except:
                self.logger.error('failed to kill %s (%d) [%s]' % (name, process_pid, traceback.format_exc()))
                res = False
        return res

    def setup(self):
        '''
        Called at the beginning of a fuzzing session.
        Will start the server up.
        '''
        self._stop_process()
        self._start_server_thread()

    def teardown(self):
        self._stop_process()
        self._process = None
        super(WinAppDbgController, self).teardown()

    def pre_test(self, test_number):
        super(WinAppDbgController, self).pre_test(test_number)
        if not self._is_victim_alive():
            self.logger.error('victim is dead, restarting...')
            # self.report.failed('server is down during pre_test - failure it probably from previous test (%d)' % (test_number-1))
            self._restart()
            self._crash_event_complete.set()
        else:
            self.logger.debug('victim is alive (pid=%d)' % self._pid)

    def post_test(self):
        self.logger.debug('in')
        time.sleep(1)
        self.logger.debug('after sleep')
        res = self._crash_event_complete.wait()
        self.logger.debug('after wait')
        if not res:
            self.report.failed('incomplete crash detected')
        super(WinAppDbgController, self).post_test()
        self.logger.debug('out')

    def _stop_process(self):
        '''
        Stop the process (if running)
        '''
        return self._kill_all_processes()

    def _stop_process_old(self):
        '''
        :return: True if process was killed, False otherwise
        '''
        if self._is_victim_alive():
            self._process.kill()
            time.sleep(0.5)
            if self._is_victim_alive():
                self._process.kill()
                time.sleep(0.5)
                if self._is_victim_alive():
                    raise Exception('Failed to kill client process')
            self._debug.stop()
            return True
        else:
            self._debug.stop()
            return False

    def _restart(self):
        '''
        restart the process
        '''
        self._stop_process()
        self.server_thread.join(1)
        time.sleep(3)
        self._server_is_up.clear()
        self._start_server_thread()

    def _is_victim_alive(self):
        '''
        check if process running
        '''
        if self._process:
            self.logger.debug('process pid: %d' % self._pid)
            is_alive = self._process.is_alive()
            is_debugee_started = self._debug.is_debugee_started(self._pid)
            self.logger.debug('is_alive = %s' % is_alive)
            self.logger.debug('is_debugee_started = %s' % is_debugee_started)
            return (is_alive and is_debugee_started)
        else:
            self.logger.debug('_process is None')
        return False