def run(self, fuzz: bool = True, retry: bool = True) -> bool: """ Run the test case, transmitting the full path Args: fuzz: (default True) Send the fuzzed node. If it is false, it transmit the original (for tests) retry: (default True) Retry if connection fails Returns: True if the TestCase was run and data was transmitted (even if transmission was cut) False if there was a connection issue and the target was paused, so the TestCase was not run """ # First step is to open the target # TODO: Mechanism for not opening if want to keep an open connection between fuzzed packets # (e.g. CLI in Telnet Session) try: self.logger.open_test_case( f"{self.id}: {self.name} {'[SENDING ORIGINAL]' if not fuzz else ''}", name=self.name, index=self.id) self.logger.log_info( f"Type: {type(self.request.mutant).__name__}. " f"Default value: {repr(self.request.mutant.original_value)}. " f"Case {self.id} of {self.session.num_mutations} overall.") self.open_fuzzing_target(retry=retry) fuzzed_sent = False for idx, edge in enumerate( self.path, start=1 ): # Now we go through our path, sending each request request = edge.dst callback = edge.callback if request == self.request: # This is the node we are fuzzing if fuzz: self.logger.open_test_step( f'Fuzzing node {request.name}') callback_data = self._callback_current_node( node=request, edge=edge) self.transmit(request, callback_data=callback_data) fuzzed_sent = True else: self.logger.open_test_step( f'Transmit node {request.name}') callback_data = self._callback_current_node( node=request, edge=edge, original=True) self.transmit(request, callback_data=callback_data, original=True) else: # This is a node we are not fuzzing right now self.logger.open_test_step(f'Transmit node {request.name}') callback_data = self._callback_current_node(node=request, edge=edge, original=True) self.transmit(request, callback_data=callback_data, original=not fuzzed_sent) if self.session.opts.new_connection_between_requests and len( self.path) > idx: # Reopen connection try: self.session.target.close() self.open_fuzzing_target(retry=False) except (exception.FuzzowskiTargetConnectionFailedError, Exception) as e: self.add_error(e) self.session.add_suspect(self) raise exception.FuzzowskiTestCaseAborted(str(e)) self.session.target.close() self.session.add_latest_test(self) return True except exception.FuzzowskiPaused: return False # Returns False when the fuzzer got paused, as it did not run the TestCase except exception.FuzzowskiTestCaseAborted as e: # There was a transmission Error, we end the test case self.logger.log_info( f'Test case aborted due to transmission error: {str(e)}') self.session.add_latest_test(self) return True
def transmit(self, request: Request, callback_data: bytes = None, original: bool = False, receive=True): """ Render and transmit a fuzzed node, process callbacks accordingly. Args: request: Request that is being fuzzed callback_data: callback data from a previous callback original: if True, will send the original value and not render receive: if True, it will try to receive data after sending the request Returns: None Raises: FuzzowskiTestCaseAborted when a transmission error occurs """ if callback_data: data = callback_data else: if original: data = request.original_value else: data = request.render() # 1. SEND DATA try: self.last_send = data self.session.target.send(data) except exception.FuzzowskiTargetConnectionReset as e: # Connection was reset self.logger.log_info("Target connection reset.") condition = self.session.opts.ignore_transmission_errors if original \ else self.session.opts.ignore_connection_issues_after_fuzz if not condition: self.add_error(e) self.session.add_suspect(self) raise exception.FuzzowskiTestCaseAborted( str(e)) # Abort TestCase, Connection Reset except exception.FuzzowskiTargetConnectionAborted as e: msg = f"Target connection lost (socket error: {e.socket_errno} {e.socket_errmsg})" condition = self.session.opts.ignore_transmission_errors if original \ else self.session.opts.ignore_connection_issues_after_fuzz if condition: self.logger.log_info(msg) else: self.logger.log_fail(msg) self.add_error(e) self.session.add_suspect(self) raise exception.FuzzowskiTestCaseAborted( str(e)) # Abort TestCase, Connection Failed # 2. RECEIVE DATA if receive: try: receive_failed = False error = '' self.last_recv = self.session.target.recv_all(DEFAULT_MAX_RECV) if not self.last_recv: # Nothing received, probably conn reset receive_failed = True error = "Nothing received. Connection Reset?" # raise exception.FuzzowskiTestCaseAborted("Receive failed. Aborting Test Case") elif len(request.responses ) > 0: # Data received, Responses defined try: self.logger.log_check( "Parsing response with data received") response_str = request.parse_response(self.last_recv) self.logger.log_info(response_str) receive_failed = False except exception.FuzzowskiRuntimeError as e: # Data received, Response do not match self.logger.log_fail(str(e)) # Abort TestCase receive_failed = False raise exception.FuzzowskiTestCaseAborted(str(e)) except Exception as e: # Any other exception not controlled by the Restarter module self.logger.log_fail(str(e)) self.session.is_paused = True # Pause the session if an uncontrolled error occurs raise exception.FuzzowskiTestCaseAborted(str(e)) else: # Data received, no Responses defined receive_failed = False if self.session.opts.check_data_received_each_request: self.logger.log_check("Checking data received...") if receive_failed: # Assume a crash? self.logger.log_fail( f"Nothing received from target. {error}") self.session.add_suspect(self) raise exception.FuzzowskiTestCaseAborted( "Receive failed. Aborting Test Case") except exception.FuzzowskiTargetConnectionReset as e: # Connection reset self.logger.log_info("Target connection reset.") if self.session.opts.check_data_received_each_request: self.logger.log_fail("Target connection reset.") self.add_error(e) self.session.add_suspect(self) raise exception.FuzzowskiTestCaseAborted(str(e)) except exception.FuzzowskiTargetConnectionAborted as e: msg = f"Target connection lost (socket error: {e.socket_errno} {e.socket_errmsg})" if self.session.opts.check_data_received_each_request: self.logger.log_fail(msg) self.add_error(e) self.session.add_suspect(self) else: self.logger.log_info(msg) raise exception.FuzzowskiTestCaseAborted(str(e))