コード例 #1
0
class ProtobufRunner(FuzzObject):

    def __init__(self, pb2_api, name='ProtobufRunner', logger=None):
        super(ProtobufRunner, self).__init__(name, logger)
        self.pb2_api = pb2_api
        self.config = ConfigParser()
        self.target_host = self.config.get_target_host_name()
        self.target_port = self.config.get_target_port()
        self.frmu = FrameworkUtils()

    def run_proto(self) -> None:
        """
        kitty low level field model
        https://kitty.readthedocs.io/en/latest/kitty.model.low_level.field.html
        """

        js = ext_json.dict_to_JsonObject(dict(self.pb2_api[0]['Messages']), 'api')

        template_a = Template(name='Api', fields=js)

        self.logger.info(f"[{time.strftime('%H:%M:%S')}] Prepare ProtobufTarget ")
        target = ProtobufTarget('ProtobufTarget',
                                host=self.target_host,
                                port=self.target_port,
                                max_retries=10,
                                timeout=None,
                                pb2_module=self.pb2_api[1])

        self.logger.info(f"[{time.strftime('%H:%M:%S')}] Prepare ProtobufController ")
        controller = ProtobufController('ProtobufController', host=self.target_host, port=self.target_port)
        target.set_controller(controller)
        #target.set_expect_response('true')
        self.logger.info(f"[{time.strftime('%H:%M:%S')}] Defining GraphModel")
        model = GraphModel()
        model.connect(template_a)

        self.logger.info(f"[{time.strftime('%H:%M:%S')}] Prepare Server Fuzzer ")
        fuzzer = ServerFuzzer()
        fuzzer.set_interface(WebInterface(port=26001))
        fuzzer.set_model(model)
        fuzzer.set_target(target)
        fuzzer.start()
        self.logger.info(f"[{time.strftime('%H:%M:%S')}] Start Fuzzer")
        self.logger.info(f"[Further info are in the related Kitty log output!]")
        six.moves.input('press enter to exit')
        self.logger.info(f"[{time.strftime('%H:%M:%S')}] End Fuzzer Session")
        fuzzer.stop()
コード例 #2
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
コード例 #3
0
class HttpRunner(FuzzObject):
    """
       HttpRunner class created according to the Kitty/Katnip API documentations.
       The class is a part of the Fuzzer Framework.

    """
    def __init__(self, name='HttpRunner', logger=None) -> object:
        """
        HttpRunner constructor
        :param name: the name of the class
        :param logger: logger from the framework
        """
        super(HttpRunner, self).__init__(name, logger)
        self.config = ConfigParser()
        self.target_host = self.config.get_target_host_name()
        self.target_port = self.config.get_target_port()
        self.http_get = self.config.get_http_get_method()
        self.http_post_put = self.config.get_http_post_put_method()
        self.http_post_update = self.config.get_http_post_update_method()
        self.http_delete = self.config.get_http_delete_method()
        self.http_fuzz_protocol = self.config.get_http_fuzz_protocol()
        self.http_path = self.config.get_http_path()
        self.http_content_type = self.config.get_http_content_type()
        self.http_payload = self.config.get_http_payload()
        self.gen_uuid = GenerateUUID.generate_uuid()

    def run_http(self) -> None:
        """
        This method provides the HTTP GET, POST, ... , templating for the HTTP header
        as fields, data provided by the config, explained in the User Documentation.
        kitty low level field model
        https://kitty.readthedocs.io/en/latest/kitty.model.low_level.field.html

        :returns: None
        :rtype: None

        """
        http_template = None
        # HTTP GET TEMPLATE
        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Initiate template for HTTP GET ..."
        )
        if self.http_get:
            http_template = Template(
                name='HTTP_GET',
                fields=[
                    # GET / HTTP/1.1
                    String('GET', name='method', fuzzable=False),
                    Delimiter(' ', name='delimiter-1', fuzzable=False),
                    String(self.http_path, name='path'),
                    Delimiter(' ',
                              name='delimiter-2',
                              fuzzable=self.http_fuzz_protocol),
                    String('HTTP',
                           name='protocol name',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter('/',
                              name='fws-1',
                              fuzzable=self.http_fuzz_protocol),
                    Dword(1,
                          name='major version',
                          encoder=ENC_INT_DEC,
                          fuzzable=self.http_fuzz_protocol),
                    Delimiter('.',
                              name='dot-1',
                              fuzzable=self.http_fuzz_protocol),
                    Dword(1,
                          name='minor version',
                          encoder=ENC_INT_DEC,
                          fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-1'),

                    # User agent
                    String('User-Agent:',
                           name='user_agent_field',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-3',
                              fuzzable=self.http_fuzz_protocol),
                    String('Fuzzer',
                           name='user-agent_name',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-2'),

                    # Token generated by framework to support following the session if necessary.
                    String('Fuzzer-Token:',
                           name='fuzzer_token',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-4',
                              fuzzable=self.http_fuzz_protocol),
                    String(str(self.gen_uuid),
                           name='fuzzer_token_type',
                           fuzzable=False),  # do not fuzz token
                    Static('\r\n', name='EOL-3'),

                    # Accept
                    String('Accept:',
                           name='accept',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-5',
                              fuzzable=self.http_fuzz_protocol),
                    String('*/*',
                           name='accept_type_',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-4'),

                    # Cache-control no-cache by default
                    String('Cache-Control:',
                           name='cache-control',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-6',
                              fuzzable=self.http_fuzz_protocol),
                    String('no-cache',
                           name='cache_control_type',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-5'),

                    # Host, the target host
                    String('Host:',
                           name='host_name',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-7',
                              fuzzable=self.http_fuzz_protocol),
                    String(self.target_host,
                           name='target_host',
                           fuzzable=False),  # do not fuzz target host address!
                    Static('\r\n', name='EOL-6'),

                    # Connection close, do not use keep-alive it results only one mutation, than the
                    # fuzzer will hang.
                    String('Connection:',
                           name='accept_encoding',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-8',
                              fuzzable=self.http_fuzz_protocol),
                    String('close',
                           name='accept_encoding_types',
                           fuzzable=False),  # do not fuzz this field!
                    Static('\r\n', name='EOM-7'),

                    # Content-type from config.
                    String('Content-Type:',
                           name='Content-Type',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-9',
                              fuzzable=self.http_fuzz_protocol),
                    String(self.http_content_type,
                           name='content_type_',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n\r\n', name='EOM-8')
                ])

        if self.http_post_put:
            self.logger.info(
                f"[{time.strftime('%H:%M:%S')}] Initiate template for HTTP POST ..."
            )
            http_template = Template(
                name='HTTP_POST',
                fields=[
                    # POST / HTTP/1.1
                    String('POST', name='method', fuzzable=False),
                    Delimiter(' ', name='delimiter-1', fuzzable=False),
                    String(self.http_path, name='path'),
                    Delimiter(' ',
                              name='delimiter-2',
                              fuzzable=self.http_fuzz_protocol),
                    String('HTTP',
                           name='protocol name',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter('/',
                              name='fws-1',
                              fuzzable=self.http_fuzz_protocol),
                    Dword(1,
                          name='major version',
                          encoder=ENC_INT_DEC,
                          fuzzable=self.http_fuzz_protocol),
                    Delimiter('.',
                              name='dot-1',
                              fuzzable=self.http_fuzz_protocol),
                    Dword(1,
                          name='minor version',
                          encoder=ENC_INT_DEC,
                          fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-1'),

                    # User agent
                    String('User-Agent:',
                           name='user_agent_field',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-3',
                              fuzzable=self.http_fuzz_protocol),
                    String('Fuzzer',
                           name='user-agent_name',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-2'),

                    # Token generated by framework to support following the session if necessary.
                    String('Fuzzer-Token:',
                           name='fuzzer_token',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-4',
                              fuzzable=self.http_fuzz_protocol),
                    String(str(self.gen_uuid),
                           name='fuzzer_token_type',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-3'),

                    # Accept
                    String('Accept:',
                           name='accept',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-5',
                              fuzzable=self.http_fuzz_protocol),
                    String('*/*',
                           name='accept_type_',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-4'),

                    # Cache-control no-cache by default
                    String('Cache-Control:',
                           name='cache-control',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-6',
                              fuzzable=self.http_fuzz_protocol),
                    String('no-cache',
                           name='cache_control_type',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\r\n', name='EOL-5'),

                    # Host, the target host
                    String('Host:',
                           name='host_name',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-7',
                              fuzzable=self.http_fuzz_protocol),
                    String(self.target_host,
                           name='target_host',
                           fuzzable=False),  # do not fuzz target host address!
                    Static('\r\n', name='EOL-6'),

                    # Content length: obvious payload lenght.
                    String('Content-Length:',
                           name='content_length',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-9',
                              fuzzable=self.http_fuzz_protocol),
                    String(str(len(self.http_payload)),
                           name='content_length_len',
                           fuzzable=False),
                    Static('\r\n', name='EOM-8'),

                    # Connection close, do not use keep-alive it results only one mutation, than the
                    # fuzzer will hang.
                    String('Connection:',
                           name='accept_encoding',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-8',
                              fuzzable=self.http_fuzz_protocol),
                    String('close',
                           name='accept_encoding_types',
                           fuzzable=False),  # do not fuzz this field!
                    Static('\r\n', name='EOM-7'),

                    # Content type
                    String('Content-Type:',
                           name='Content-Type',
                           fuzzable=self.http_fuzz_protocol),
                    Delimiter(' ',
                              name='delimiter-10',
                              fuzzable=self.http_fuzz_protocol),
                    String(self.http_content_type,
                           name='content_type_',
                           fuzzable=self.http_fuzz_protocol),
                    Static('\n\r\n', name='EOM-9'),

                    # Payload
                    String(self.http_payload, name='payload'),
                    Static('\r\n\r\n', name='EOM-10')
                ])

        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Prepare HttpTarget ...")
        target = HttpTarget(name='HttpTarget',
                            host=self.target_host,
                            port=self.target_port,
                            max_retries=10,
                            timeout=None)
        target.set_expect_response('true')
        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Prepare HttpController ...")
        controller = HttpGetController('HttpGetController',
                                       host=self.target_host,
                                       port=self.target_port)
        target.set_controller(controller)
        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Defining GraphModel...")
        model = GraphModel()
        model.connect(http_template)
        fuzzer = ServerFuzzer()
        fuzzer.set_interface(WebInterface(port=26001))
        fuzzer.set_model(model)
        fuzzer.set_target(target)
        fuzzer.set_delay_between_tests(1)
        self.logger.info(f"[{time.strftime('%H:%M:%S')}] Start Fuzzer...")
        self.logger.info(
            f"[Further info are in the related Kitty log output!]")
        fuzzer.start()
        self.logger.info(f"[{time.strftime('%H:%M:%S')}] End Fuzzer Session")
        fuzzer.stop()
コード例 #4
0
class DnsRunner(FuzzObject):
    def __init__(self, name='DnsRunner', logger=None):
        super(DnsRunner, self).__init__(name, logger)
        self.config = ConfigParser()
        self.target_host = self.config.get_target_host_name()
        self.target_port = self.config.get_target_port()
        self.timeout = self.config.get_dns_timout()
        self.tld = self.config.get_dns_tld()
        self.default_labels = self.config.get_dns_default_labels()

    def run_dns(self):
        """
        kitty low level field model
        https://kitty.readthedocs.io/en/latest/kitty.model.low_level.field.html
        """
        fields = []
        counter = 0
        dns_label_length = len(self.default_labels.split('.'))
        dns_label_list = self.default_labels.split('.')

        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Initiate template for DNS ...")
        while counter < dns_label_length:
            fields.append(
                String(dns_label_list[counter],
                       name='sub_domain_' + str(counter),
                       max_size=10))
            fields.append(Delimiter('.', name='delimiter_' + str(counter)))
            counter += 1

        fields.append(String(self.tld, name='tld', fuzzable=False))

        dns_query = Template(name='DNS_QUERY', fields=fields)
        """
        dns_query = Template(name='DNS_QUERY', fields=[
            String('r', name='sub_domain', max_size=10),
            Delimiter('.', name='space1"),
            String('rf', name='sub_domain2', max_size=10),
            Delimiter('.', name='space2"),
            String(self.tld, name='tld', fuzzable=False),
        ])
        """
        # define target, in this case this is SslTarget because of HTTPS
        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Prepare DnsTarget ...")
        target = DnsTarget(name='DnsTarget',
                           host=self.target_host,
                           port=self.target_port,
                           timeout=self.timeout)
        target.set_expect_response('true')
        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Prepare DnsController ...")
        controller = DnsController('DnsController',
                                   host=self.target_host,
                                   port=self.target_port)
        target.set_controller(controller)

        # Define model
        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Defining GraphModel...")
        model = GraphModel()
        model.connect(dns_query)

        self.logger.info(
            f"[{time.strftime('%H:%M:%S')}] Prepare Server Fuzzer ...")
        fuzzer = ServerFuzzer()
        fuzzer.set_interface(WebInterface(port=26001))
        fuzzer.set_model(model)
        fuzzer.set_target(target)
        fuzzer.set_delay_between_tests(1)
        self.logger.info(f"[{time.strftime('%H:%M:%S')}] Start Fuzzer...")
        self.logger.info(
            f"[Further info are in the related Kitty log output!]")
        fuzzer.start()
        self.logger.info(f"[{time.strftime('%H:%M:%S')}] End Fuzzer Session")
        fuzzer.stop()