示例#1
0
class Runner(FuzzObject):
    """
    This is the main runner class of the Fuzzer Framework.
    It integrates the kitty/katnip components and the framework components.
    See documentation for the usage.
    """

    def __init__(self, name='Runner', logger=None):
        super(Runner, self).__init__(name, logger)
        self.config = ConfigParser()
        self.framework_utils = FrameworkUtils()
        self.put_archive_to_s3 = PutArchiveToS3()
        self.proto_path = self.config.get_generic_proto_path()
        self.pb2_path = self.config.get_generic_pb2_path()
        self.verbosity = self.config.get_generic_verbosity()
        self.module_path = self.config.get_module_path()
        self.proto_modules = self.config.get_protobuf_modules()
        self.proto_classes = self.config.get_protobuf_classes_to_send()
        self.protocol_proto = self.config.get_protocol_protobuf()
        self.protocol_http = self.config.get_protocol_http()
        self.protocol_dns = self.config.get_protocol_dns()
        self.set_verbosity(self.verbosity)
        self.use_s3_to_archive = self.config.get_archive_to_s3()

    def search_files(self) -> tuple:
        """
        Creates lists of raw and compiled proto files paired into tuple;
        :return: raw and compiled proto file lists in tuple;
        """
        search_raw_proto = SearchFile(file_type='.proto', file_path=self.proto_path)
        raw_proto_files = search_raw_proto.search_file
        self.logger.debug(f'RAW proto files {raw_proto_files}')
        search_api = SearchFile(file_type='_pb2.py', file_path=self.pb2_path)
        compiled_proto_files = search_api.search_file
        self.logger.debug(f'Compiled proto files {compiled_proto_files}')

        return raw_proto_files, compiled_proto_files

    def checksum_files(self, raw_proto: list) -> list:
        # TODO for further usage, function not in use.
        checksum_files = self.framework_utils.md5_checksum_for_proto_files(raw_proto)
        self.logger.debug(f'Checksum {checksum_files}')
        return checksum_files

    @property
    def process_pb2(self) -> object:
        """
        This function parses the compiled protobuf API and
        produces a Dictionary representation of it.
        :return: object
        """
        compiled_proto_files = self.search_files()[1]
        for item in compiled_proto_files:
            if len(item.split('_')) > 2:
                raise FileNameError  # raise if file name contains more then 2 underline.
            elif item.split('_')[0] in self.config.get_protobuf_modules():
                parse_file = ParseProtoApi(pb2_api_file=item,
                                           module_path=self.module_path)
                return parse_file.execute_api_parse()
            else:
                self.logger.info(f'File {item} was not part of the inspection.')

    def tear_down(self):
        """
        The common tear down sequence of a fuzzing session
        :return: None
        """
        self.framework_utils.results_dir()
        self.framework_utils.rename_log_file()
        self.framework_utils.archive_locally()
        self.report()
        self.framework_utils.archive_xml()
        if self.use_s3_to_archive:
            self.put_archive_to_s3.do_upload(self.framework_utils.dir_files_to_archive())

    def report(self):
        _report = CreateReport()
        _report.run_report()

    def run(self):

        if self.config.get_banner():
            file1 = open(os.getcwd() + '/banner', 'r')
            Lines = file1.readlines()
            for line in Lines:
                print(f'     {line.strip()}')
            for i in range(5, 0, -1):
                sys.stdout.write(str(i) + '.....')
                sys.stdout.flush()
                time.sleep(1)

        if self.protocol_proto:
            self.logger.info(f"[{time.strftime('%H:%M:%S')}] Starting PROTOBUF Fuzzer session")
            try:
                api = self.process_pb2
                do_proto = ProtobufRunner(pb2_api=api)
                do_proto.run_proto()

            except SystemError as e:
                self.logger.error(f"Interpreter found an internal error: {e}")

            except EnvironmentError as e:
                self.logger(f"Error occur outside the Python environment: {e}")

            finally:
                self.logger.info(f"[{time.strftime('%H:%M:%S')}] Flush cache...")
                self.logger.info(f"[{time.strftime('%H:%M:%S')}] Rename kitty related log files...")
                self.tear_down()

        elif self.protocol_dns:
            self.logger.info(f"[{time.strftime('%H:%M:%S')}] Starting DNS Fuzzer session")
            try:
                # query record type
                do_dns = DnsRunner()
                do_dns.run_dns()
                self.logger.info(f"[{time.strftime('%H:%M:%S')}] DNS runner started")

            except SystemError as e:
                self.logger.error(f"Interpreter finds an internal problem {e}")

            except EnvironmentError as e:
                self.logger(f"Error occur outside the Python environment: {e}")

            finally:
                self.logger.info(f"[{time.strftime('%H:%M:%S')}] Rename kitty related log files...")
                self.tear_down()

        elif self.protocol_http:
            self.logger.info(f"[{time.strftime('%H:%M:%S')}] Starting HTTP Fuzzer session")
            try:
                do_http = HttpRunner()
                do_http.run_http()
                self.logger.info(f"[{time.strftime('%H:%M:%S')}] HTTP runner started")

            except SystemError as e:
                self.logger.error(f"Interpreter finds an internal problem {e}")

            except EnvironmentError as e:
                self.logger(f"Error occur outside the Python environment: {e}")

            finally:
                self.logger.info(f"[{time.strftime('%H:%M:%S')}] Rename kitty related log files...")
                self.tear_down()
示例#2
0
class TestFramework(unittest.TestCase):
    def __init__(self, name='TestFramework'):
        super(TestFramework, self).__init__(name)
        self.framework_utils = FrameworkUtils()
        self.protobuf_target = ProtobufTarget()

    def test_01_convert_filepath_to_module_path(self):
        module_import_path = \
            self.framework_utils.convert_filepath_to_module_path(file_path='/framework/models/pb2/api/')
        self.assertEqual(module_import_path, 'framework.models.pb2.api.')

    def test_02_encode_message(self):
        with open(
                os.path.abspath(os.path.join(os.path.dirname(__file__))) +
                '/msg_encode_assert') as file_data:
            data = file_data.read().encode()
        file_data.close()
        msg = self.protobuf_target._construct_message(data, query_pb2)
        msg_encoded = self.framework_utils.encode_message(msg)
        self.assertEqual(msg_encoded, msg_encoded)

    def test_03_decode_message(self):
        result = self.framework_utils.decode_message(msg_encoded, query_pb2,
                                                     'Request')
        msg = json_format.MessageToDict(result[0])
        with open(
                os.path.join(os.path.dirname(__file__)) +
                '/msg_decoded_assert') as file_data:
            msg_decoded_assert = json.load(file_data)
        self.assertEqual(msg, msg_decoded_assert)

    def test_04_create_result_dir_if_not_exists(self):
        is_exits = False
        self.framework_utils.results_dir()
        if os.path.exists(os.getcwd() + '/results/'):
            is_exits = True
        self.assertTrue(is_exits, True)
        #shutil.rmtree(os.getcwd() + '/results/', ignore_errors=True)

    def test_05_rename_log_file(self):
        self._uuid = GenerateUUID.generate_uuid()
        self.framework_utils.rename_log_file()
        file_name = glob.glob(os.getcwd() + '/kittylogs/' +
                              '*.log')[0].split('/')[-1:]
        expected_file_name = str(
            self._uuid) + '-kitty_' + time.strftime("%Y%m%d-%H%M%S") + '.log'
        shutil.rmtree(os.getcwd() + '/kittylogs/', ignore_errors=True)
        self.assertAlmostEqual(file_name[0], expected_file_name)

    def test_06_rename_log_file_file_not_found(self):
        shutil.rmtree(os.getcwd() + '/kittylogs/', ignore_errors=True)
        with self.assertRaises(FileNotFoundError):
            self.framework_utils.rename_log_file()

    def test_07_rename_log_file_could_not_read_from_file(self):
        os.makedirs(os.getcwd() + '/kittylogs/')
        with open(os.getcwd() + '/kittylogs/foo.gld', 'w') as file:
            file.write('dummy')
        file.close()
        with self.assertLogs('fuzzer', level='WARNING') as cm:
            self.framework_utils.rename_log_file()
        self.assertIn(
            'WARNING:fuzzer:Could not read from file ' + os.getcwd() +
            '/kittylogs/', cm.output)
        shutil.rmtree(os.getcwd() + '/kittylogs/', ignore_errors=True)

    def test_08_archive_locally(self):
        dir_exists = False
        self.framework_utils.archive_locally()
        self._uuid = GenerateUUID.generate_uuid()
        dir_ = str(self._uuid) + '-log'
        if os.path.exists(os.getcwd() + '/' + dir_):
            dir_exists = True
        self.assertTrue(dir_exists, True)

    def test_09_extract_values(self):
        input_ = {
            "a": 1,
            "b": 2,
            "c": {
                "d": 3,
                "e": 4,
                "f": [{
                    "g": 5
                }],
                "h": {
                    "i": 6
                }
            }
        }
        key_ = 'i'
        self.assertEqual(self.framework_utils.extract_values(input_, key_),
                         [6])

    def test_10_update_nested_object(self):
        context_ = [("a", 1), ("c", 2), ("h", 1), ("j", 1)]
        appnd_ = {"m": 9}
        key_ = "j"
        input_ = {
            "a": 1,
            "b": 2,
            "c": {
                "d": 3,
                "e": 4,
                "f": [{
                    "g": 5
                }],
                "h": {
                    "i": 6,
                    "j": [{
                        "k": 7,
                        "l": 8
                    }]
                }
            }
        }

        result = self.framework_utils.update_nested(input_, key_, appnd_,
                                                    context_)
        expected = {
            'a': 1,
            'b': 2,
            'c': {
                'd': 3,
                'e': 4,
                'f': [{
                    'g': 5
                }],
                'h': {
                    'i': 6,
                    'j': [{
                        'k': 7,
                        'l': 8,
                        'm': 9
                    }]
                }
            }
        }
        self.assertEqual(result, expected)

    def test_11_update_nested_object_same_name(self):
        context_ = [("a", 1), ("c", 2), ("h", 1)]
        appnd_ = {"h": 9}
        key_ = "h"
        input_ = {
            "a": 1,
            "b": 2,
            "c": {
                "d": 3,
                "e": 4,
                "f": {
                    "g": 5
                },
                "h": {
                    "i": 6,
                    "j": [{
                        "k": 7,
                        "l": 8
                    }]
                }
            }
        }

        result = self.framework_utils.update_nested(input_, key_, appnd_,
                                                    context_)
        expected = {
            'a': 1,
            'b': 2,
            'c': {
                'd': 3,
                'e': 4,
                'f': {
                    'g': 5
                },
                'h': {
                    'i': 6,
                    'j': [{
                        'k': 7,
                        'l': 8
                    }],
                    'h': 9
                }
            }
        }
        self.assertEqual(result, expected)

    def test_12_update_nested_object_same_name_nested(self):
        context_ = [("a", 1), ("q", 2), ("q", 1)]
        appnd_ = {"q": 9}
        key_ = "q"
        input_ = {
            "a": 1,
            "b": 2,
            "q": {
                "q": {
                    "x": 1
                }
            },
            "c": {
                "d": 3,
                "e": 4,
                "f": {
                    "g": 5
                },
                "h": {
                    "i": 6,
                    "j": [{
                        "k": 7,
                        "l": 8
                    }]
                }
            }
        }

        result = self.framework_utils.update_nested(input_, key_, appnd_,
                                                    context_)
        expected = {
            'a': 1,
            'b': 2,
            'q': {
                'q': {
                    'x': 1,
                    'q': 9
                }
            },
            'c': {
                'd': 3,
                'e': 4,
                'f': {
                    'g': 5
                },
                'h': {
                    'i': 6,
                    'j': [{
                        'k': 7,
                        'l': 8
                    }]
                }
            }
        }
        self.assertEqual(result, expected)

    def test_13_field_randomizer_double(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(1,
                                                       default_value=None,
                                                       enum_list=None)
        if 1.0 <= result <= 1.9:
            is_close = True
        self.assertTrue(is_close, True)

    def test_13_field_randomizer_double_default_value(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(
            1, default_value=1.12345679, enum_list=None)
        if 1.0 <= result <= 1.9:
            is_close = True
        self.assertTrue(is_close, True)

    def test_14_field_randomizer_float(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(2,
                                                       default_value=None,
                                                       enum_list=None)
        if 1.0 <= result <= 1.9:
            is_close = True
        self.assertTrue(is_close, True)

    def test_15_field_randomizer_float_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(
            2, default_value=1.123456789, enum_list=None)
        if 1.0 <= result <= 1.9:
            is_close = True
        self.assertTrue(is_close, True)

    def test_14_field_randomizer_int64(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(3,
                                                       default_value=None,
                                                       enum_list=None)
        if -9223372036854775808 <= result <= 9223372036854775808:
            is_close = True
        self.assertTrue(is_close, True)

    def test_15_field_randomizer_int64_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(
            3, default_value=1234567899999999, enum_list=None)
        if -9223372036854775808 <= result <= 9223372036854775808:
            is_close = True
        self.assertTrue(is_close, True)

    def test_16_field_randomizer_uint64(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(4,
                                                       default_value=None,
                                                       enum_list=None)
        if -0 <= result <= 18446744073709551615:
            is_close = True
        self.assertTrue(is_close, True)

    def test_17_field_randomizer_uint64_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(
            4, default_value=1844674407370955161, enum_list=None)
        if -0 <= result <= 18446744073709551615:
            is_close = True
        self.assertTrue(is_close, True)

    def test_18_field_randomizer_int32(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(5,
                                                       default_value=None,
                                                       enum_list=None)
        if -2147483648 <= result <= 2147483648:
            is_close = True
        self.assertTrue(is_close, True)

    def test_19_field_randomizer_int32_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(5,
                                                       default_value=1234567,
                                                       enum_list=None)
        if -2147483648 <= result <= 2147483648:
            is_close = True
        self.assertTrue(is_close, True)

    def test_20_field_randomizer_fixed64(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(6,
                                                       default_value=None,
                                                       enum_list=None)
        if -9223372036854775808 <= result <= 9223372036854775808:
            is_close = True
        self.assertTrue(is_close, True)

    def test_21_field_randomizer_fixed64_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(
            6, default_value=92233720368547758, enum_list=None)
        if -9223372036854775808 <= result <= 9223372036854775808:
            is_close = True
        self.assertTrue(is_close, True)

    def test_22_field_randomizer_fixed32(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(7,
                                                       default_value=None,
                                                       enum_list=None)
        if -2147483648 <= result <= 2147483648:
            is_close = True
        self.assertTrue(is_close, True)

    def test_23_field_randomizer_fixed32_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(7,
                                                       default_value=214748364,
                                                       enum_list=None)
        if -2147483648 <= result <= 2147483648:
            is_close = True
        self.assertTrue(is_close, True)

    def test_22_field_randomizer_bool(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(8,
                                                       default_value=None,
                                                       enum_list=None)
        if result or not result:
            is_close = True
        self.assertTrue(is_close, True)

    def test_23_field_randomizer_bool_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(8,
                                                       default_value=True,
                                                       enum_list=None)
        if result or not result:
            is_close = True
        self.assertTrue(is_close, True)

    def test_24_field_randomizer_string(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        regex = re.compile('[a-zA-Z0-9]', re.I)
        result = self.framework_utils.field_randomizer(9,
                                                       default_value=None,
                                                       enum_list=None)
        res = bool(regex.match(str(result)))
        if isinstance(result, str) and res:
            is_close = True
        self.assertTrue(is_close, True)

    def test_25_field_randomizer_string_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        regex = re.compile('[a-zA-Z0-9]', re.I)
        result = self.framework_utils.field_randomizer(
            9, default_value='abcdefghijk', enum_list=None)
        res = bool(regex.match(str(result)))
        if isinstance(result, str) and res:
            is_close = True
        self.assertTrue(is_close, True)

    def test_26_field_randomizer_group(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        with self.assertRaises(NotImplementedError):
            self.framework_utils.field_randomizer(10,
                                                  default_value=None,
                                                  enum_list=None)

    def test_27_field_randomizer_bytes(self):
        #TODO finish
        self.framework_utils.field_randomizer(12,
                                              default_value=None,
                                              enum_list=None)

    def test_28_field_randomizer_bytes_default(self):
        #TODO
        self.framework_utils.field_randomizer(12,
                                              default_value=0x23,
                                              enum_list=None)

    def test_29_field_randomizer_uint32(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(13,
                                                       default_value=None,
                                                       enum_list=None)
        if 0 <= result <= 4294967295:
            is_close = True
        self.assertTrue(is_close, True)

    def test_30_field_randomizer_uint32_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        result = self.framework_utils.field_randomizer(13,
                                                       default_value=429496729,
                                                       enum_list=None)
        if 0 <= result <= 4294967295:
            is_close = True
        self.assertTrue(is_close, True)

    def test_31_field_randomizer_enum(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        enum_list = ['foo', 'bar', 'baz']
        result = self.framework_utils.field_randomizer(14,
                                                       default_value=None,
                                                       enum_list=enum_list)
        if result.split('+')[0] in enum_list:
            is_close = True
        self.assertTrue(is_close, True)

    def test_32_field_randomizer_enum_default(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        is_close = False
        enum_list = ['foo', 'bar', 'baz']
        result = self.framework_utils.field_randomizer(14,
                                                       default_value='bar',
                                                       enum_list=enum_list)
        if result.split('+')[0] in enum_list:
            is_close = True
        self.assertTrue(is_close, True)

    def test_33_field_randomizer_sfixed32(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        with self.assertRaises(NotImplementedError):
            self.framework_utils.field_randomizer(15,
                                                  default_value=None,
                                                  enum_list=None)

    def test_34_field_randomizer_sfixed64(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        with self.assertRaises(NotImplementedError):
            self.framework_utils.field_randomizer(16,
                                                  default_value=None,
                                                  enum_list=None)

    def test_35_field_randomizer_sint32(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        with self.assertRaises(NotImplementedError):
            self.framework_utils.field_randomizer(17,
                                                  default_value=None,
                                                  enum_list=None)

    def test_36_field_randomizer_sint64(self):
        # field_randomizer(self, field_type, default_value=None, enum_list=None)
        with self.assertRaises(NotImplementedError):
            self.framework_utils.field_randomizer(18,
                                                  default_value=None,
                                                  enum_list=None)

    def test_37_str_to_bool_true(self):
        result = self.framework_utils.str_to_bool('true')
        self.assertTrue(result, True)

    def test_38_str_to_bool_false(self):
        result = self.framework_utils.str_to_bool('false')
        self.assertFalse(result, False)

    def test_39_dir_files_to_archive(self):
        result = self.framework_utils.dir_files_to_archive()
        self.assertEqual(len(result), 1)

    def test_40_md5_checksum_for_proto_files(self):
        pass