示例#1
0
 def testDataEntry(self):
     report = Report(self.report_name)
     entry_name = 'my entry'
     entry_data = 'some data'
     report.add(entry_name, entry_data)
     self.assertEqual(report.get(entry_name), entry_data)
     self.assertEqual(report.get(entry_name), entry_data)
示例#2
0
 def testFailureInSubReportEntry(self):
     entry_name = 'sub report'
     report = Report(self.report_name)
     subreport = Report(entry_name)
     report.add(entry_name, subreport)
     subreport.failed(self.failure_reason)
     self.assertEqual(report.get_status(), Report.FAILED)
     self.assertEqual(report.get('reason'), self.failure_reason)
示例#3
0
 def testErrorInSubReportEntry(self):
     entry_name = 'sub report'
     report = Report(self.report_name)
     subreport = Report(entry_name)
     report.add(entry_name, subreport)
     subreport.error(self.error_reason)
     self.assertEqual(report.get_status(), Report.ERROR)
     self.assertEqual(report.get('reason'), self.error_reason)
示例#4
0
 def testClearSubReportEntry(self):
     entry_name = 'sub report'
     report = Report(self.report_name)
     subreport = Report(entry_name)
     report.add(entry_name, subreport)
     self.assertEqual(report.get(entry_name), subreport)
     report.clear()
     self.assertEqual(report.get(entry_name), None)
示例#5
0
 def testDataEntryReplaced(self):
     report = Report(self.report_name)
     entry_name = 'my entry'
     entry_data = 'some data'
     report.add(entry_name, entry_data)
     self.assertEqual(report.get(entry_name), entry_data)
     new_data = 'some other data'
     report.add(entry_name, new_data)
     self.assertEqual(report.get(entry_name), new_data)
示例#6
0
class BaseController(KittyObject):
    '''
    Base class for controllers. Defines basic variables and implements basic behavior.
    '''

    def __init__(self, name, logger=None):
        '''
        :param name: name of the object
        :param logger: logger for the object (default: None)
        '''
        super(BaseController, self).__init__(name, logger)
        self.report = None
        self.test_number = 0

    def setup(self):
        '''
        Called at the beginning of the fuzzing session.
        You should override it with the actual implementation of victim setup.
        '''
        pass

    def teardown(self):
        '''
        Called at the end of the fuzzing session.
        You should override it with the actual implementation of victim teardown.
        '''
        pass

    def pre_test(self, test_number):
        '''
        Called before a test is started. Call super if overriden.

        :param test_number: current test number
        '''
        self.report = Report(self.name)
        self.report.add('start_time', time.time())
        self.test_number = test_number

    def post_test(self):
        '''
        Called when test is done. Call super if overriden.
        '''
        self.report.add('stop_time', time.time())

    def get_report(self):
        '''
        :rtype: :class:`~kitty.data.report.Report`
        :return: a report about the victim since last call to pre_test
        '''
        return self.report
示例#7
0
    def _store_report(self, report):
        self.logger.debug('<in>')
        report.add('test_number', self.model.current_index())
        report.add('fuzz_path', self.model.get_sequence_str())
        test_info = self.model.get_test_info()
        data_model_report = Report(name='Data Model')
        for k, v in test_info.items():
            new_entries = _flatten_dict_entry(k, v)
            for (k_, v_) in new_entries:
                data_model_report.add(k_, v_)
        report.add(data_model_report.get_name(), data_model_report)
        payload = self._last_payload
        if payload is not None:
            data_report = Report('payload')
            data_report.add('raw', payload)
            try:
                data_report.add('hex', json.dumps(str(payload)).encode('hex'))
            except UnicodeDecodeError:
                print('cant serialize payload: %', payload)
            data_report.add('length', len(payload))
            report.add('payload', data_report)
        else:
            report.add('payload', None)

        self.dataman.store_report(report, self.model.current_index())
        self.dataman.get_report_by_id(self.model.current_index())
示例#8
0
 def testErrorInOneOfSubReportEntries(self):
     entry_name1 = 'sub report 1'
     entry_name2 = 'sub report 2'
     entry_name3 = 'sub report 3'
     report = Report(self.report_name)
     subreport1 = Report(entry_name1)
     subreport2 = Report(entry_name2)
     subreport3 = Report(entry_name3)
     report.add(entry_name1, subreport1)
     report.add(entry_name2, subreport2)
     report.add(entry_name3, subreport3)
     subreport2.error(self.error_reason)
     self.assertEqual(report.get_status(), Report.ERROR)
     self.assertEqual(report.get('reason'), self.error_reason)
示例#9
0
class BaseController(KittyObject):
    '''
    Base class for controllers. Defines basic variables and implements basic behavior.
    '''
    def __init__(self, name, logger=None):
        '''
        :param name: name of the object
        :param logger: logger for the object (default: None)
        '''
        super(BaseController, self).__init__(name, logger)
        self.report = None
        self.test_number = 0

    def setup(self):
        '''
        Called at the beginning of the fuzzing session.
        You should override it with the actual implementation of victim setup.
        '''
        pass

    def teardown(self):
        '''
        Called at the end of the fuzzing session.
        You should override it with the actual implementation of victim teardown.
        '''
        pass

    def pre_test(self, test_number):
        '''
        Called before a test is started. Call super if overriden.

        :param test_number: current test number
        '''
        self.report = Report(self.name)
        self.report.add('start_time', time.time())
        self.test_number = test_number

    def post_test(self):
        '''
        Called when test is done. Call super if overriden.
        '''
        self.report.add('stop_time', time.time())

    def get_report(self):
        '''
        :rtype: :class:`~kitty.data.report.Report`
        :return: a report about the victim since last call to pre_test
        '''
        return self.report
示例#10
0
    def _store_report(self, report):
        """
        Enrich fuzz report
        :param report: report to extend
        """
        self.logger.debug('<in>')
        report.add('test_number', self.model.current_index())
        report.add('fuzz_path', self.model.get_sequence_str())
        test_info = self.model.get_test_info()
        data_model_report = Report(name='Data Model')
        for k, v in test_info.items():
            new_entries = _flatten_dict_entry(k, v)
            for (k_, v_) in new_entries:
                data_model_report.add(k_, v_)
        report.add(data_model_report.get_name(), data_model_report)
        payload = self._last_payload
        if payload is not None:
            data_report = Report('payload')
            data_report.add('raw', payload)
            data_report.add('length', len(payload))
            report.add('payload', data_report)
        else:
            report.add('payload', None)

        self.dataman.store_report(report, self.model.current_index())
示例#11
0
    def _store_report(self, report):
        self.logger.debug('<in>')
        report.add('test_number', self.model.current_index())
        report.add('fuzz_path', self.model.get_sequence_str())
        test_info = self.model.get_test_info()
        data_model_report = Report(name='Data Model')
        for k, v in test_info.items():
            data_model_report.add(k, v)
        report.add(data_model_report.get_name(), data_model_report)
        payload = self._last_payload
        if payload is not None:
            data_report = Report('payload')
            data_report.add('raw', payload)
            data_report.add('hex', payload.encode('hex'))
            data_report.add('length', len(payload))
            report.add('payload', data_report)
        else:
            report.add('payload', None)

        self.dataman.store_report(report, self.model.current_index())
        self.dataman.get_report_by_id(self.model.current_index())
示例#12
0
文件: base.py 项目: kevinhsu/kitty
    def _store_report(self, report):
        self.logger.debug('<in>')
        report.add('test_number', self.model.current_index())
        report.add('fuzz_path', self.model.get_sequence_str())
        test_info = self.model.get_test_info()
        data_model_report = Report(name='Data Model')
        for k, v in test_info.items():
            data_model_report.add(k, v)
        report.add(data_model_report.get_name(), data_model_report)
        payload = self._last_payload
        if payload is not None:
            data_report = Report('payload')
            data_report.add('raw', payload)
            data_report.add('hex', payload.encode('hex'))
            data_report.add('length', len(payload))
            report.add('payload', data_report)
        else:
            report.add('payload', None)

        self.dataman.store_report(report, self.model.current_index())
        self.dataman.get_report_by_id(self.model.current_index())
示例#13
0
文件: client.py 项目: unix1010/isf-1
class ClientFuzzer(BaseFuzzer):
    '''
    ClientFuzzer is designed for fuzzing clients.
    It does not preform an active fuzzing, but rather returns a mutation of a
    response when in the right state.
    It is designed to be a module that is integrated into different stacks.

    You can see its usahe examples in the following places:

        - examples/02_client_fuzzer_browser_remote
        - examples/03_client_fuzzer_browser
    '''

    #  Wild card for matching any stage
    STAGE_ANY = '******************'

    def __init__(self, name='ClientFuzzer', logger=None, option_line=None):
        '''
        :param name: name of the object
        :param logger: logger for the object (default: None)
        :param option_line: cmd line options to the fuzzer
        '''
        super(ClientFuzzer, self).__init__(name, logger, option_line)
        self._target_control_thread = LoopFuncThread(self._do_trigger)
        self._trigger_stop_evt = Event()
        self._target_control_thread.set_func_stop_event(self._trigger_stop_evt)
        self._index_in_path = 0
        self._requested_stages = []
        self._report = None
        self._done_evt = Event()

    def _pre_test(self):
        self._requested_stages = []
        self._report = Report(self.get_name())
        super(ClientFuzzer, self)._pre_test()

    def is_done(self):
        '''
        check if fuzzer is done fuzzing

        :return: True if done
        '''
        return self._done_evt.is_set()

    def wait_until_done(self):
        '''
        wait until fuzzer is done
        '''
        self._done_evt.wait()

    def stop(self):
        '''
        Stop the fuzzing session
        '''
        self.logger.info('Stopping client fuzzer')
        self._target_control_thread.stop()
        self.target.signal_mutated()
        super(ClientFuzzer, self).stop()

    def _do_trigger(self):
        self.logger.debug('_do_trigger called')
        self._check_pause()
        if self._next_mutation():
            self._fuzz_path = self.model.get_sequence()
            self._index_in_path = 0
            self._pre_test()
            self._test_info()
            self.target.trigger()
            self._post_test()
        else:
            self._end_message()
            self._done_evt.set()
            self._trigger_stop_evt.wait()

    def _start(self):
        self._target_control_thread.start()

    def _test_environment(self):
        '''
        .. todo:: can we do that here somehow?
        '''
        pass

    def _should_fuzz_node(self, fuzz_node, stage):
        '''
        The matching stage is either the name of the last node, or ClientFuzzer.STAGE_ANY.

        :return: True if we are in the correct model node
        '''
        if stage == ClientFuzzer.STAGE_ANY:
            return True
        if fuzz_node.name.lower() == stage.lower():
            if self._index_in_path == len(self._fuzz_path) - 1:
                return True
        else:
            return False

    def _update_path_index(self, stage):
        last_index_in_path = len(self._fuzz_path) - 1
        if self._index_in_path < last_index_in_path:
            node = self._fuzz_path[self._index_in_path].dst
            if node.name.lower() == stage.lower():
                self._index_in_path += 1

    def get_mutation(self, stage, data):
        '''
        Get the next mutation, if in the correct stage

        :param stage: current stage of the stack
        :param data: a dictionary of items to pass to the model
        :return: mutated payload if in apropriate stage, None otherwise
        '''
        payload = None
        # Commented out for now: we want to return the same
        # payload - while inside the same test
        # if self._keep_running() and self._do_fuzz.is_set():
        if self._keep_running():
            fuzz_node = self._fuzz_path[self._index_in_path].dst
            if self._should_fuzz_node(fuzz_node, stage):
                fuzz_node.set_session_data(data)
                payload = fuzz_node.render().tobytes()
                self._last_payload = payload
            else:
                self._update_path_index(stage)
        if payload:
            self._notify_mutated()
        self._requested_stages.append((stage, payload))
        return payload

    def _notify_mutated(self):
        self.target.signal_mutated()

    def _get_report(self):
        base_report = super(ClientFuzzer, self)._get_report()
        if len(self._requested_stages):
            stages, payloads = zip(*self._requested_stages)
        else:
            stages = []
            payloads = []
        self._report.add('stages', stages)
        self._report.add('payloads', [
            None if payload is None else hexlify(payload)
            for payload in payloads
        ])
        base_report.add('fuzzer', self._report)
        return base_report
示例#14
0
文件: base.py 项目: wflk/kitty
class BaseMonitor(KittyObject):
    '''
    Base (abstract) monitor class
    '''
    def __init__(self, name, logger=None):
        '''
        :param name: name of the monitor
        :param logger: logger for the monitor (default: None)
        '''
        super(BaseMonitor, self).__init__(name, logger)
        self.report = Report(name)
        self.monitor_thread = None
        self.test_number = None

    def setup(self):
        '''
        Make sure the monitor is ready for fuzzing
        '''
        self._cleanup()
        self.monitor_thread = LoopFuncThread(self._monitor_func)
        self.monitor_thread.start()

    def teardown(self):
        '''
        cleanup the monitor data and
        '''
        self.monitor_thread.stop()
        self.monitor_thread = None

    def pre_test(self, test_number):
        '''
        Called when a test is started

        :param test_number: current test number
        '''
        if not self._is_alive():
            self.setup()
        self._cleanup()
        self.test_number = test_number
        self.report.add('state', 'STARTED')
        self.report.add('start_time', time.time())
        self.report.add('name', self.name)

    def post_test(self):
        '''
        Called when a test is completed, prepare the report etc.
        '''
        self.report.add('state', 'STOPPED')
        self.report.add('stop_time', time.time())

    def _is_alive(self):
        '''
        Check if victim/monitor alive
        '''
        if self.monitor_thread is not None:
            if self.monitor_thread.is_alive():
                return True
        return False

    def _cleanup(self):
        '''
        perform a monitor cleanup
        '''
        self.report = Report(self.name)

    def get_report(self):
        '''
        :return: the monitor's report
        '''
        return self.report

    def _monitor_func(self):
        '''
        Called in a loop in a separate thread (self.monitor_thread).
        '''
        self.not_implemented('_monitor_func')
示例#15
0
文件: base.py 项目: unix1010/isf-1
class BaseTarget(KittyObject):
    '''
    BaseTarget contains the common logic and behaviour of all target.
    '''
    def __init__(self, name='BaseTarget', logger=None):
        super(BaseTarget, self).__init__(name, logger)
        self.controller = None
        self.monitors = []
        self.report = Report(name)
        self.test_number = None
        self.fuzzer = None
        self.session_data = {}

    def set_fuzzer(self, fuzzer):
        self.fuzzer = fuzzer

    def set_controller(self, controller):
        '''
        Set a controller
        '''
        self.controller = controller

    def add_monitor(self, monitor):
        '''
        Add a monitor
        '''
        self.monitors.append(monitor)

    def setup(self):
        '''
        Make sure the target is ready for fuzzing, including monitors and
        controllers
        '''
        if self.controller:
            self.controller.setup()
        for monitor in self.monitors:
            monitor.setup()

    def teardown(self):
        '''
        Clean up the target once all tests are completed
        '''
        if self.controller:
            self.controller.teardown()
        for monitor in self.monitors:
            monitor.teardown()

    def pre_test(self, test_num):
        '''
        Called when a test is started
        '''
        self.test_number = test_num
        self.report = Report(self.name)
        if self.controller:
            self.controller.pre_test(test_number=self.test_number)
        for monitor in self.monitors:
            monitor.pre_test(test_number=self.test_number)
        self.report.add('test_number', test_num)
        self.report.add('state', 'STARTED')

    def post_test(self, test_num):
        '''
        Called when test is completed, a report should be prepared now
        '''
        if self.controller:
            self.controller.post_test()
        for monitor in self.monitors:
            monitor.post_test()
        self.report.add('state', 'COMPLETED')
        if self.controller:
            controller_report = self.controller.get_report()
            self.report.add('controller', controller_report)
        for monitor in self.monitors:
            current_report = monitor.get_report()
            self.report.add(current_report.get('name'), current_report)
        status = self.report.get_status()
        reason = self.report.get('reason')
        if status != Report.PASSED:
            self.logger.warning('Test %d status: %s' % (test_num, status))
            self.logger.warning('Reason: %s' % (reason))

    def get_report(self):
        return self.report

    def get_session_data(self):
        '''
        Session related data dictionary to be used by data model.

        :return: dictionary (str, bytes)
        '''
        return self.session_data
示例#16
0
文件: server.py 项目: cisco-sas/kitty
    def transmit(self, payload):
        '''
        Transmit single payload, and receive response, if expected.
        The actual implementation of the send/receive should be in
        ``_send_to_target`` and ``_receive_from_target``.

        :type payload: str
        :param payload: payload to send
        :rtype: str
        :return: the response (if received)
        '''
        response = None
        trans_report_name = 'transmission_0x%04x' % self.transmission_count
        trans_report = Report(trans_report_name)
        self.transmission_report = trans_report
        self.report.add(trans_report_name, trans_report)
        try:
            trans_report.add('request (hex)', payload.encode('hex'))
            trans_report.add('request (raw)', '%s' % payload)
            trans_report.add('request length', len(payload))
            trans_report.add('request time', time.time())

            request = payload.encode('hex')
            request = request if len(request) < 100 else (request[:100] + ' ...')
            self.logger.info('request(%d): %s' % (len(payload), request))
            self._send_to_target(payload)
            trans_report.success()

            if self.expect_response:
                try:
                    response = self._receive_from_target()
                    trans_report.add('response time', time.time())
                    trans_report.add('response (hex)', response.encode('hex'))
                    trans_report.add('response (raw)', '%s' % response)
                    trans_report.add('response length', len(response))
                    printed_response = response.encode('hex')
                    printed_response = printed_response if len(printed_response) < 100 else (printed_response[:100] + ' ...')
                    self.logger.info('response(%d): %s' % (len(response), printed_response))
                except Exception as ex2:
                    trans_report.failed('failed to receive response: %s' % ex2)
                    trans_report.add('traceback', traceback.format_exc())
                    self.logger.error('target.transmit - failure in receive (exception: %s)' % ex2)
                    self.logger.error(traceback.format_exc())
                    self.receive_failure = True
            else:
                response = ''
        except Exception as ex1:
            trans_report.failed('failed to send payload: %s' % ex1)
            trans_report.add('traceback', traceback.format_exc())
            self.logger.error('target.transmit - failure in send (exception: %s)' % ex1)
            self.logger.error(traceback.format_exc())
            self.send_failure = True
        self.transmission_count += 1
        return response
示例#17
0
class ClientFuzzer(BaseFuzzer):
    '''
    ClientFuzzer is designed for fuzzing clients.
    It does not preform an active fuzzing, but rather returns a mutation of a
    response when in the right state.
    It is designed to be a module that is integrated into different stacks.
    See its usage example in the file examples/client_fuzzer_example.py which
    designed to fuzz a browser.
    '''

    #  Wild card for matching any stage
    STAGE_ANY = '******************'

    def __init__(self, name='ClientFuzzer', logger=None, option_line=None):
        '''
        :param name: name of the object
        :param logger: logger for the object (default: None)
        :param option_line: cmd line options to the fuzzer
        '''
        super(ClientFuzzer, self).__init__(name, logger, option_line)
        self._target_control_thread = LoopFuncThread(self._do_trigger)
        self._trigger_stop_evt = Event()
        self._target_control_thread.set_func_stop_event(self._trigger_stop_evt)
        self._index_in_path = 0
        self._requested_stages = []
        self._report = None
        self._done_evt = Event()

    def _pre_test(self):
        self._requested_stages = []
        self._report = Report(self.get_name())
        super(ClientFuzzer, self)._pre_test()

    def is_done(self):
        '''
        check if fuzzer is done fuzzing

        :return: True if done
        '''
        return self._done_evt.is_set()

    def wait_until_done(self):
        '''
        wait until fuzzer is done
        '''
        self._done_evt.wait()

    def stop(self):
        '''
        Stop the fuzzing session
        '''
        self.logger.info('Stopping client fuzzer')
        self._target_control_thread.stop()
        self.target.signal_mutated()
        super(ClientFuzzer, self).stop()

    def _do_trigger(self):
        self.logger.debug('_do_trigger called')
        self._check_pause()
        if self._keep_running() and self.model.mutate():
            self._fuzz_path = self.model.get_sequence()
            self._index_in_path = 0
            self._pre_test()
            self._test_info()
            self.target.trigger()
            self._post_test()
        else:
            self._end_message()
            self._done_evt.set()
            self._trigger_stop_evt.wait()

    def _start(self):
        self._start_message()
        self.target.setup()
        self._target_control_thread.start()

    def _should_fuzz_node(self, fuzz_node, stage):
        '''
        The matching stage is either the name of the last node, or ClientFuzzer.STAGE_ANY.

        :return: True if we are in the correct model node
        '''
        if stage == ClientFuzzer.STAGE_ANY:
            return True
        if fuzz_node.name.lower() == stage.lower():
            if self._index_in_path == len(self._fuzz_path) - 1:
                return True
        else:
            return False

    def get_mutation(self, stage, data):
        '''
        Get the next mutation, if in the correct stage

        :param stage: current stage of the stack
        :param data: a dictionary of items to pass to the model
        :return: mutated payload if in apropriate stage, None otherwise
        '''
        payload = None
        # Commented out for now: we want to return the same
        # payload - while inside the same test
        # if self._keep_running() and self._do_fuzz.is_set():
        if self._keep_running():
            fuzz_node = self._fuzz_path[self._index_in_path].dst
            if self._should_fuzz_node(fuzz_node, stage):
                fuzz_node.set_session_data(data)
                payload = fuzz_node.render().tobytes()
                self._last_payload = payload
            else:
                self._index_in_path += 1
                if self._index_in_path >= len(self._fuzz_path):
                    self._index_in_path = 0
        if payload:
            self._notify_mutated()
        self._requested_stages.append((stage, payload))
        return payload

    def _notify_mutated(self):
        self.target.signal_mutated()

    def _get_report(self):
        base_report = super(ClientFuzzer, self)._get_report()
        stages, payloads = zip(*self._requested_stages)
        self._report.add('stages', stages)
        self._report.add('payloads', [None if payload is None else hexlify(payload) for payload in payloads])
        base_report.add('fuzzer', self._report)
        return base_report
示例#18
0
class HttpTarget(ServerTarget):
    """
    HttpTarget is implementation of a TCP target for the ServerFuzzer
    """
    def __init__(self,
                 name,
                 host,
                 port,
                 max_retries=10,
                 timeout=None,
                 logger=None) -> object:
        """
        :param name: name of the target
        :param host: host ip (to send data to) currently unused
        :param port: port to send to
        :param max_retries: maximum connection retries (default: 10)
        :param timeout: socket timeout (default: None)
        :param logger: logger for the object (default: None)
        """
        super(HttpTarget, self).__init__(name, logger)
        self.host = host
        self.port = port
        if (host is None) or (port is None):
            raise ValueError('host and port may not be None')
        self.timeout = timeout
        self.socket = None
        self.max_retries = max_retries
        self.config = ConfigParser()
        self.use_tls = self.config.get_tls()
        self.target_host = self.config.get_target_host_name()
        self.report = Report('report')
        self._uuid = GenerateUUID.generate_uuid()

    def pre_test(self, test_num=str) -> None:
        """Katnip original method."""
        super(HttpTarget, self).pre_test(test_num)
        retry_count = 0
        while self.socket is None and retry_count < self.max_retries:
            sock = self._get_socket()
            if self.timeout is not None:
                sock.settimeout(self.timeout)
            try:
                retry_count += 1
                sock.connect((self.host, self.port))
                self.socket = sock
            except Exception:
                sock.close()
                self.logger.error(f"Error: {traceback.format_exc()}")
                self.logger.error(
                    f"Failed to connect to target server, retrying...")
                time.sleep(1)
        if self.socket is None:
            raise (KittyException(
                'TCPTarget: (pre_test) cannot connect to server (retries = %d'
                % retry_count))

    def _get_socket(self) -> socket:
        """
        Katnip original method. Get a Socket object.
        Extended with Python3.x TLS socket wrapper
        """

        if self.use_tls:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(10)
            socket_handler = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            socket_wraped = ssl.create_default_context().wrap_socket(
                socket_handler, server_hostname=self.target_host)
            return socket_wraped
        elif not self.use_tls:
            return socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def post_test(self, test_num=str) -> None:
        """Katnip original method."""
        super(HttpTarget, self).post_test(test_num)
        if self.socket is not None:
            self.socket.close()
            self.socket = None
        if self.report.get('status') != Report.PASSED:
            if not os.path.exists(os.getcwd() + '/results/'):
                os.makedirs(os.getcwd() + '/results/')
            test_num = self.report.get('test_number')
            with open(os.getcwd() + '/results/{}-result-http-{}.json'.format(
                    test_num, str(self._uuid)),
                      'w',
                      encoding='utf-8') as file:
                json.dump(self.report.to_dict(),
                          file,
                          ensure_ascii=False,
                          indent=4)
            file.close()

    def _raw_data(self, data=bytes) -> None:
        """Convert bytes data to UTF-8"""
        try:
            self.logger.info(f"Data sent: {data.decode('utf-8')}")
        except UnicodeDecodeError as err:
            """
            UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfe in position 14: invalid start byte
            Fuzzer transform data so I have to log the bytes format.
            """
            self.logger.info(f"Data sent: {data}, error {err}")

    def _send_to_target(self, data=bytes) -> None:
        self._raw_data(data)
        self.socket.send(data)

    def _receive_from_target(self) -> bytes:
        return self.socket.recv(10000)

    def transmit(self, payload):
        """
        This is the original transmit method from ServerTarget overwritten with
        special cases such as 40X or 50X according to the aim of the test.

        According to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500
        500 Internal Server Error
        501 Not Implemented
        502 Bad Gateway
        503 Service Unavailable
        504 Gateway Timeout
        505 HTTP Version Not Supported
        506 Variant Also Negotiates
        507 Insufficient Storage
        508 Loop Detected
        510 Not Extended
        511 Network Authentication Required

        Original method docstring:
        Transmit single payload, and receive response, if expected.
        The actual implementation of the send/receive should be in
        ``_send_to_target`` and ``_receive_from_target``.

        :type payload: str
        :param payload: payload to send
        :rtype: str
        :return: the response (if received)
        """

        SERVER_50x_CODES = [
            '500 Internal Server Error', '501 Not Implemented',
            '502 Bad Gateway', '503 Service Unavailable',
            '504 Gateway Timeout', '505 HTTP Version Not Supported',
            '506 Variant Also Negotiates', '507 Insufficient Storage',
            '508 Loop Detected', '510 Not Extended',
            '511 Network Authentication Required'
        ]

        SERVER_40xCODES = [
            '400 Bad Request', '401 Unauthorized', '402 Payment Required',
            '403 Forbidden', '404 Not Found', '405 Method Not Allowed',
            '406 Not Acceptable', '407 Proxy Authentication Required',
            '408 Request Timeout', '409 Conflict', '410 Gone',
            '411 Length Required', '412 Precondition Failed',
            '413 Payload Too Large', '414 URI Too Long',
            '415 Unsupported Media Type', '416 Range Not Satisfiable',
            '417 Expectation Failed', '422 Unprocessable Entity',
            '425 Too Early', '426 Upgrade Required',
            '428 Precondition Required', '429 Too Many Requests',
            '431 Request Header Fields Too Large',
            '451 Unavailable For Legal Reasons'
        ]

        response = None
        trans_report_name = 'transmission_0x%04x' % self.transmission_count
        trans_report = Report(trans_report_name)
        self.transmission_report = trans_report
        self.report.add(trans_report_name, trans_report)
        try:
            trans_report.add('request (hex)', hexlify(payload).decode())
            trans_report.add('request (raw)', '%s' % payload)
            trans_report.add('request length', len(payload))
            trans_report.add('request time', time.time())

            request = hexlify(payload).decode()
            request = request if len(request) < 100 else (request[:100] +
                                                          ' ...')
            self.logger.info('request(%d): %s' % (len(payload), request))
            self._send_to_target(payload)
            trans_report.success()

            if self.expect_response:
                try:
                    response = self._receive_from_target()
                    trans_report.add('response time', time.time())
                    trans_report.add('response (hex)',
                                     hexlify(response).decode())
                    trans_report.add('response (raw)', '%s' % response)
                    trans_report.add('response length', len(response))
                    trans_report.add('Session ID', str(self._uuid))
                    printed_response = hexlify(response).decode()
                    printed_response = printed_response if len(
                        printed_response) < 100 else (printed_response[:100] +
                                                      ' ...')
                    self.logger.info(
                        f"response{len(response)}: {printed_response}")
                    self.logger.debug(response.decode('utf-8'))
                    string_response = response.decode('utf-8')
                    response_code_string = string_response.splitlines()[0]
                    response_code = response_code_string.replace(
                        'HTTP/1.1 ', '')

                    if response_code in SERVER_40xCODES or response_code in SERVER_50x_CODES:
                        self.logger.info(
                            f"response failure {response.decode('utf-8')}")
                        trans_report.failed('Failure in HTTP response.')
                        trans_report.add('Response', response.decode('utf-8'))
                        self.report.set_status('failed')
                        self.receive_failure = True

                except Exception as ex2:
                    trans_report.failed('failed to receive response: %s' % ex2)
                    trans_report.add('traceback', traceback.format_exc())
                    self.logger.error(
                        f"target.transmit - failure in receive (exception: {ex2})"
                    )
                    self.logger.error(traceback.format_exc())
                    self.receive_failure = True
            else:
                response = ''
        except Exception as ex1:
            trans_report.failed(f"failed to send payload: {ex1}")
            trans_report.add('traceback', traceback.format_exc())
            self.logger.error(
                f"target.transmit - failure in send (exception: {ex1})")
            self.logger.error(traceback.format_exc())
            self.send_failure = True
        self.transmission_count += 1
        return response
示例#19
0
文件: actor.py 项目: kevinhsu/kitty
class KittyActorInterface(KittyObject):
    '''
    Base class for monitors and controllers,
    its defines (and partially implements) the Kitty Actor API:

    - :func:`~kitty.core.actor.KittyActorInterface.setup`
    - :func:`~kitty.core.actor.KittyActorInterface.teardown`
    - :func:`~kitty.core.actor.KittyActorInterface.pre_test`
    - :func:`~kitty.core.actor.KittyActorInterface.post_test`
    - :func:`~kitty.core.actor.KittyActorInterface.is_victim_alive`
    - :func:`~kitty.core.actor.KittyActorInterface.get_report`
    '''

    def __init__(self, name, logger=None, victim_alive_check_delay=0.3):
        '''
        :param name: name of the actor
        :param logger: logger for the actor (default: None)
        :param victim_alive_check_delay: delay between checks if alive (default: 0.3)
        '''
        super(KittyActorInterface, self).__init__(name, logger)
        self.victim_alive_check_delay = victim_alive_check_delay
        self.report = None
        self.test_number = 0

    def setup(self):
        '''
        Called at the beginning of the fuzzing session.
        You should override it with the actual implementation of victim setup.
        '''
        pass

    def teardown(self):
        '''
        Called at the end of the fuzzing session.
        You should override it with the actual implementation of victim teardown.
        '''
        pass

    def pre_test(self, test_number):
        '''
        Called before a test is started. Call super if overriden.

        :param test_number: current test number
        '''
        self.test_number = test_number
        self.report = Report(self.name)
        self.report.add('start_time', time.time())
        self.report.add('test_number', self.test_number)
        self.report.add('state', 'pre_test')
        last_log = 0
        while not self.is_victim_alive():
            if time.time() - last_log >= 10:
                last_log = time.time()
                self.logger.warn('waiting for target to be alive')
            time.sleep(self.victim_alive_check_delay)
        if last_log > 0: # only if we logged that we're waiting, should we log that we're now alive
            self.logger.warn('target is now alive')

    def post_test(self):
        '''
        Called when test is done. Call super if overriden.
        '''
        self.report.add('state', 'post_test')
        self.report.add('stop_time', time.time())

    def get_report(self):
        '''
        :rtype: :class:`~kitty.data.report.Report`
        :return: a report about the victim since last call to pre_test
        '''
        return self.report

    def is_victim_alive(self):
        '''
        Called during pre_test in loop until the target becomes alive

        :return: whether target is alive (ready for test) or not

        .. note::

            by default, it returns true,
            override if you have a way to check in your actor
        '''
        return True
示例#20
0
    def transmit(self, payload):
        """
        This is the original transmit method from ServerTarget overwritten with
        special cases such as 40X or 50X according to the aim of the test.

        Accordin to https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500
        500 Internal Server Error
        501 Not Implemented
        502 Bad Gateway
        503 Service Unavailable
        504 Gateway Timeout
        505 HTTP Version Not Supported
        506 Variant Also Negotiates
        507 Insufficient Storage
        508 Loop Detected
        510 Not Extended
        511 Network Authentication Required

        Original method docstring:
        Transmit single payload, and receive response, if expected.
        The actual implementation of the send/receive should be in
        ``_send_to_target`` and ``_receive_from_target``.

        :type payload: str
        :param payload: payload to send
        :rtype: str
        :return: the response (if received)
        """

        SERVER_50x_CODES = [
            '500 Internal Server Error', '501 Not Implemented',
            '502 Bad Gateway', '503 Service Unavailable',
            '504 Gateway Timeout', '505 HTTP Version Not Supported',
            '506 Variant Also Negotiates', '507 Insufficient Storage',
            '508 Loop Detected', '510 Not Extended',
            '511 Network Authentication Required'
        ]

        SERVER_40xCODES = [
            '400 Bad Request', '401 Unauthorized', '402 Payment Required',
            '403 Forbidden', '404 Not Found', '405 Method Not Allowed',
            '406 Not Acceptable', '407 Proxy Authentication Required',
            '408 Request Timeout', '409 Conflict', '410 Gone',
            '411 Length Required', '412 Precondition Failed',
            '413 Payload Too Large', '414 URI Too Long',
            '415 Unsupported Media Type', '416 Range Not Satisfiable',
            '417 Expectation Failed', '422 Unprocessable Entity',
            '425 Too Early', '426 Upgrade Required',
            '428 Precondition Required', '429 Too Many Requests',
            '431 Request Header Fields Too Large',
            '451 Unavailable For Legal Reasons'
        ]

        response = None
        trans_report_name = 'transmission_0x%04x' % self.transmission_count
        trans_report = Report(trans_report_name)
        self.transmission_report = trans_report
        self.report.add(trans_report_name, trans_report)
        try:
            trans_report.add('request (hex)', hexlify(payload).decode())
            trans_report.add('request (raw)', '%s' % payload)
            trans_report.add('request length', len(payload))
            trans_report.add('request time', time.time())

            request = hexlify(payload).decode()
            request = request if len(request) < 100 else (request[:100] +
                                                          ' ...')
            self.logger.info(f"request({len(payload)}): {request}")
            self.logger.debug(f"payload {payload}")
            self._send_to_target(payload)
            trans_report.success()

            if self.expect_response:
                try:
                    response = self._receive_from_target()
                    trans_report.add('response time', time.time())
                    trans_report.add('response (hex)',
                                     hexlify(response).decode())
                    trans_report.add('response (raw)', '%s' % response)
                    trans_report.add('response length', len(response))
                    trans_report.add('Session ID', str(self._uuid))
                    printed_response = hexlify(response).decode()
                    printed_response = printed_response if len(
                        printed_response) < 100 else (printed_response[:100] +
                                                      ' ...')
                    self.logger.info(
                        f"response({len(response)}): {printed_response}")

                    string_response = response.decode('utf-8')
                    response_code_string = string_response.splitlines()[0]
                    response_code = response_code_string.replace(
                        'HTTP/1.1 ', '')

                    if response_code in SERVER_40xCODES or response_code in SERVER_50x_CODES:
                        self.logger.info(
                            f"response failure {response.decode('utf-8')}")
                        trans_report.failed('Failure in HTTP-PROTO response.')
                        trans_report.add('Response', response.decode('utf-8'))
                        self.report.set_status('failed')
                        self.receive_failure = True

                except Exception as ex2:
                    trans_report.failed('failed to receive response: %s' % ex2)
                    trans_report.add('traceback', traceback.format_exc())
                    self.logger.error(
                        f"target.transmit - failure in receive (exception: {ex2})"
                    )
                    self.logger.error(traceback.format_exc())
                    self.receive_failure = True
            else:
                response = ''
        except Exception as ex1:
            #trans_report.failed('failed to send payload: %s' % ex1)
            #trans_report.add('traceback', traceback.format_exc())
            self.logger.error(
                f"target.transmit - failure in send (exception: {ex1})")
            self.logger.error(traceback.format_exc())
            #self.send_failure = True
        self.transmission_count += 1
        return response
示例#21
0
class BaseTarget(KittyObject):
    '''
    BaseTarget contains the common logic and behaviour of all target.
    '''

    def __init__(self, name='BaseTarget', logger=None):
        super(BaseTarget, self).__init__(name, logger)
        self.controller = None
        self.monitors = []
        self.report = Report(name)
        self.test_number = None
        self.fuzzer = None

    def set_fuzzer(self, fuzzer):
        self.fuzzer = fuzzer

    def set_controller(self, controller):
        '''
        Set a controller
        '''
        self.controller = controller

    def add_monitor(self, monitor):
        '''
        Add a monitor
        '''
        self.monitors.append(monitor)

    def setup(self):
        '''
        Make sure the target is ready for fuzzing, including monitors and
        controllers
        '''
        if self.controller:
            self.controller.setup()
        for monitor in self.monitors:
            monitor.setup()

    def teardown(self):
        '''
        Clean up the target once all tests are completed
        '''
        if self.controller:
            self.controller.teardown()
        for monitor in self.monitors:
            monitor.teardown()

    def pre_test(self, test_num):
        '''
        Called when a test is started
        '''
        self.test_number = test_num
        self.report = Report(self.name)
        if self.controller:
            self.controller.pre_test(test_number=self.test_number)
        for monitor in self.monitors:
            monitor.pre_test(test_number=self.test_number)
        self.report.add('test_number', test_num)
        self.report.add('state', 'STARTED')

    def post_test(self, test_num):
        '''
        Called when test is completed, a report should be prepared now
        '''
        if self.controller:
            self.controller.post_test()
        for monitor in self.monitors:
            monitor.post_test()
        self.report.add('state', 'COMPLETED')
        if self.controller:
            controller_report = self.controller.get_report()
            self.report.add('controller', controller_report)
        for monitor in self.monitors:
            current_report = monitor.get_report()
            self.report.add(current_report.get('name'), current_report)
        if self.report.is_failed():
            self.report.failed()
        if self.report.is_failed():
            self.logger.warning('!!! Test %d failed !!!' % (test_num))

    def get_report(self):
        return self.report

    def get_session_data(self):
        '''
        Session related data dictionary to be used by data model.

        :return: dictionary (str, bytes)
        '''
        return {}
示例#22
0
文件: base.py 项目: cisco-sas/kitty
class BaseTarget(KittyObject):
    """
    BaseTarget contains the common logic and behaviour of all target.
    """

    def __init__(self, name="BaseTarget", logger=None):
        super(BaseTarget, self).__init__(name, logger)
        self.controller = None
        self.monitors = []
        self.report = Report(name)
        self.test_number = None
        self.fuzzer = None
        self.session_data = {}

    def set_fuzzer(self, fuzzer):
        self.fuzzer = fuzzer

    def set_controller(self, controller):
        """
        Set a controller
        """
        self.controller = controller

    def add_monitor(self, monitor):
        """
        Add a monitor
        """
        self.monitors.append(monitor)

    def setup(self):
        """
        Make sure the target is ready for fuzzing, including monitors and
        controllers
        """
        if self.controller:
            self.controller.setup()
        for monitor in self.monitors:
            monitor.setup()

    def teardown(self):
        """
        Clean up the target once all tests are completed
        """
        if self.controller:
            self.controller.teardown()
        for monitor in self.monitors:
            monitor.teardown()

    def pre_test(self, test_num):
        """
        Called when a test is started
        """
        self.test_number = test_num
        self.report = Report(self.name)
        if self.controller:
            self.controller.pre_test(test_number=self.test_number)
        for monitor in self.monitors:
            monitor.pre_test(test_number=self.test_number)
        self.report.add("test_number", test_num)
        self.report.add("state", "STARTED")

    def post_test(self, test_num):
        """
        Called when test is completed, a report should be prepared now
        """
        if self.controller:
            self.controller.post_test()
        for monitor in self.monitors:
            monitor.post_test()
        self.report.add("state", "COMPLETED")
        if self.controller:
            controller_report = self.controller.get_report()
            self.report.add("controller", controller_report)
        for monitor in self.monitors:
            current_report = monitor.get_report()
            self.report.add(current_report.get("name"), current_report)
        status = self.report.get_status()
        reason = self.report.get("reason")
        if status != Report.PASSED:
            self.logger.warning("Test %d status: %s" % (test_num, status))
            self.logger.warning("Reason: %s" % (reason))

    def get_report(self):
        return self.report

    def get_session_data(self):
        """
        Session related data dictionary to be used by data model.

        :return: dictionary (str, bytes)
        """
        return self.session_data
示例#23
0
 def testAddReservedKeywordRaisesException_failed(self):
     report = Report(self.report_name)
     with self.assertRaises(Exception):
         report.add('failed', 'some status')
示例#24
0
文件: base.py 项目: Fors3cDream/kitty
class BaseMonitor(KittyObject):
    '''
    Base (abstract) monitor class
    '''

    def __init__(self, name, logger=None):
        '''
        :param name: name of the monitor
        :param logger: logger for the monitor (default: None)
        '''
        super(BaseMonitor, self).__init__(name, logger)
        self.report = Report(name)
        self.monitor_thread = None
        self.test_number = None

    def setup(self):
        '''
        Make sure the monitor is ready for fuzzing
        '''
        self._cleanup()
        self.monitor_thread = LoopFuncThread(self._monitor_func)
        self.monitor_thread.start()

    def teardown(self):
        '''
        cleanup the monitor data and
        '''
        self.monitor_thread.stop()
        self.monitor_thread = None

    def pre_test(self, test_number):
        '''
        Called when a test is started

        :param test_number: current test number
        '''
        if not self._is_alive():
            self.setup()
        self._cleanup()
        self.test_number = test_number
        self.report.add('state', 'STARTED')
        self.report.add('start_time', time.time())
        self.report.add('name', self.name)

    def post_test(self):
        '''
        Called when a test is completed, prepare the report etc.
        '''
        self.report.add('state', 'STOPPED')
        self.report.add('stop_time', time.time())

    def _is_alive(self):
        '''
        Check if victim/monitor alive
        '''
        if self.monitor_thread is not None:
            if self.monitor_thread.is_alive():
                return True
        return False

    def _cleanup(self):
        '''
        perform a monitor cleanup
        '''
        self.report = Report(self.name)

    def get_report(self):
        '''
        :return: the monitor's report
        '''
        return self.report

    def _monitor_func(self):
        '''
        Called in a loop in a separate thread (self.monitor_thread).
        '''
        self.not_implemented('_monitor_func')
示例#25
0
    def transmit(self, payload):

        """
        This is the original transmit method from ServerTarget overwritten with
        DNS response code specific reporting

        Accordin to:
        https://support.umbrella.com/hc/en-us/articles/232254248-Common-DNS-return-codes-for-any-DNS-service-and-Umbrella-

        NOERROR     RCODE:0     DNS Query completed successfully
        FORMERR     RCODE:1     DNS Query Format Error
        SERVFAIL    RCODE:2     Server failed to complete the DNS request
        NXDOMAIN    RCODE:3     Domain name does not exist.
        NOTIMP      RCODE:4     Function not implemented
        REFUSED     RCODE:5     The server refused to answer for the query
        YXDOMAIN    RCODE:6     Name that should not exist, does exist
        XRRSET      RCODE:7     RRset that should not exist, does exist
        NOTAUTH     RCODE:8     Server not authoritative for the zone
        NOTZONE     RCODE:9     Name not in zone

        Original method docstring:
        Transmit single payload, and receive response, if expected.
        The actual implementation of the send/receive should be in
        ``_send_to_target`` and ``_receive_from_target``.

        :type payload: str
        :param payload: payload to send
        :rtype: str
        :return: the response (if received)
        """

        DNS_RETRUN_CODES = [
            'NOERROR',
            'FORMERR',
            'SERVFAIL',
            'NXDOMAIN',
            'NOTIMP',
            'REFUSED',
            'YXDOMAIN',
            'XRRSET',
            'NOTAUTH',
            'NOTZONE'
        ]

        DNS_EXCLUDE = [0, 3, 5]

        response = None
        trans_report_name = 'transmission_0x%04x' % self.transmission_count
        trans_report = Report(trans_report_name)
        self.transmission_report = trans_report
        self.report.add(trans_report_name, trans_report)
        try:
            trans_report.add('request (hex)', hexlify(payload).decode())
            trans_report.add('request (raw)', '%s' % payload)
            trans_report.add('request length', len(payload))
            trans_report.add('request time', time.time())

            request = hexlify(payload).decode()
            request = request if len(request) < 100 else (request[:100] + ' ...')
            self.logger.info(f"request({len(payload)}): {request}")

            self._send_to_target(payload)

            trans_report.success()

            if self.expect_response:
                try:
                    response = self._receive_from_target()
                    trans_report.add('response time', time.time())
                    trans_report.add('response (hex)', hexlify(response).decode())
                    trans_report.add('response (raw)', '%s' % response)
                    trans_report.add('response length', len(response))
                    trans_report.add('Session ID', str(self._uuid))
                    printed_response = hexlify(response).decode()
                    printed_response = printed_response if len(printed_response) < 100 else (printed_response[:100]
                                                                                             + ' ...')
                    self.logger.info(f"response({len(response)}): {printed_response}")

                    server_message = response.split(b',', 0)
                    reply = codecs.encode(server_message[0], 'hex')

                    # USE DNS LIB TO HELP DEBUGGING MALFORMED HEADER
                    debug_response_header = DNSBufferExt(unhexlify(reply))
                    parsed_dns_response_header = DNSHeader.parse(debug_response_header)
                    dns_response_message = DNSRecordExt.parse(server_message[0])

                    if int(parsed_dns_response_header.rcode) not in DNS_EXCLUDE:
                        self.logger.error(f"DNS response code: "
                                          f"{DNS_RETRUN_CODES[int(parsed_dns_response_header.rcode)]}")
                        self.logger.error(f"Debug header: {str(parsed_dns_response_header)}")
                        trans_report.failed(f"Failure in response, code: "
                                            f"{DNS_RETRUN_CODES[int(parsed_dns_response_header.rcode)]}")
                        trans_report.add('Response', str(dns_response_message))
                        trans_report.add('traceback', traceback.format_exc())
                        self.receive_failure = True
                        self.report.set_status('failed')

                except Exception as ex2:
                    trans_report.failed('failed to receive response: %s' % ex2)
                    trans_report.add('traceback', traceback.format_exc())
                    self.logger.error(f"target.transmit - failure in receive (exception: {ex2})")
                    self.logger.error(traceback.format_exc())
                    self.receive_failure = True
            else:
                response = ''
        except Exception as ex1:
            trans_report.failed('failed to send payload: %s' % ex1)
            trans_report.add('traceback', traceback.format_exc())
            self.logger.error(f"target.transmit - failure in send (exception: {ex1})")
            self.logger.error(traceback.format_exc())
            self.send_failure = True
        self.transmission_count += 1
        return response
示例#26
0
    def transmit(self, payload):
        '''
        Transmit single payload, and receive response, if expected.
        The actual implementation of the send/receive should be in
        ``_send_to_target`` and ``_receive_from_target``.

        :type payload: str
        :param payload: payload to send
        :rtype: str
        :return: the response (if received)
        '''
        response = None
        trans_report_name = 'transmission_0x%04x' % self.transmission_count
        trans_report = Report(trans_report_name)
        self.transmission_report = trans_report
        self.report.add(trans_report_name, trans_report)
        try:
            trans_report.add('request (hex)', hexlify(payload).decode())
            trans_report.add('request (raw)', '%s' % payload)
            trans_report.add('request length', len(payload))
            trans_report.add('request time', time.time())

            request = hexlify(payload).decode()
            request = request if len(request) < 100 else (request[:100] +
                                                          ' ...')
            self.logger.info('request(%d): %s' % (len(payload), request))
            self._send_to_target(payload)
            trans_report.success()

            if self.expect_response:
                try:
                    response = self._receive_from_target()
                    trans_report.add('response time', time.time())
                    trans_report.add('response (hex)',
                                     hexlify(response).decode())
                    trans_report.add('response (raw)', '%s' % response)
                    trans_report.add('response length', len(response))
                    printed_response = hexlify(response).decode()
                    printed_response = printed_response if len(
                        printed_response) < 100 else (printed_response[:100] +
                                                      ' ...')
                    self.logger.info('response(%d): %s' %
                                     (len(response), printed_response))
                except Exception as ex2:
                    trans_report.failed('failed to receive response: %s' % ex2)
                    trans_report.add('traceback', traceback.format_exc())
                    self.logger.error(
                        'target.transmit - failure in receive (exception: %s)'
                        % ex2)
                    self.logger.error(traceback.format_exc())
                    self.receive_failure = True
            else:
                response = ''
        except Exception as ex1:
            trans_report.failed('failed to send payload: %s' % ex1)
            trans_report.add('traceback', traceback.format_exc())
            self.logger.error(
                'target.transmit - failure in send (exception: %s)' % ex1)
            self.logger.error(traceback.format_exc())
            self.send_failure = True
        self.transmission_count += 1
        return response