def delay_for(self, delay, original_wait_time): """ Sends a request to the remote end that "should" delay the response in `delay` seconds. :param original_wait_time: The time that it takes to perform the request without adding any delays. :return: (True, response) if there was a delay. In order to make things right we first send some requests to measure the original wait time. """ delay_str = self.delay_obj.get_string_for_delay(delay) mutant = self.mutant.copy() mutant.set_token_value(delay_str) # Set the upper and lower bounds delta = original_wait_time * self.DELTA_PERCENT upper_bound = (delay * 2) + original_wait_time + delta lower_bound = original_wait_time + delay - delta # Send, it is important to notice that we don't use the cache # to avoid any interference try: response = self.uri_opener.send_mutant(mutant, cache=False, timeout=upper_bound * 10) except HTTPRequestException, hre: # NOTE: In some cases where the remote web server timeouts we reach # this code section. The handling of that situation is done # with the new_no_content_resp() below. return False, new_no_content_resp(self.mutant.get_uri())
def default_open(self, req): """ If blacklisted return an empty response, else return None, which means that this handler was unable to handle the request and the next one needs to be called. With this we want to indicate that the keepalive handler will be called. """ if self._blacklist_urls is None: # This happens only during the first HTTP request self._read_configuration_settings() uri = req.url_object if not self._is_blacklisted(uri): # This means: I don't know how to handle this, call the next opener return None msg = ('%s was included in the HTTP request blacklist, the scan' ' engine is NOT sending the HTTP request and is instead' ' returning an empty response to the plugin.') om.out.debug(msg % uri) # Return a 204 response no_content = new_no_content_resp(req.url_object) no_content = http_response_to_httplib(no_content) return no_content
def delay_for(self, delay, original_wait_time): """ Sends a request to the remote end that "should" delay the response in `delay` seconds. :param original_wait_time: The time that it takes to perform the request without adding any delays. :return: (True, response) if there was a delay. In order to make things right we first send some requests to measure the original wait time. """ delay_str = self.delay_obj.get_string_for_delay(delay) mutant = self.mutant.copy() mutant.set_token_value(delay_str) # Set the upper and lower bounds delta = original_wait_time * self.DELTA_PERCENT upper_bound = (delay * 2) + original_wait_time + delta lower_bound = original_wait_time + delay - delta # Send, it is important to notice that we don't use the cache # to avoid any interference try: response = self.uri_opener.send_mutant(mutant, cache=False, timeout=upper_bound * 10) except HTTPRequestException, hre: # NOTE: In some cases where the remote web server timeouts we reach # this code section. The handling of that situation is done # with the new_no_content_resp() below. return False, new_no_content_resp(self.mutant.get_uri())
def handle_url_error(self, uri, http_exception): """ Handle UrlError exceptions raised when requests are made. Subclasses should redefine this method for a more refined behavior and must respect the return value format. :param http_exception: HTTPRequestException exception instance :return: A tuple containing: * re_raise: Boolean value that indicates the caller if the original exception should be re-raised after this error handling method. * result: The result to be returned to the caller. This only makes sense if re_raise is False. """ no_content_resp = new_no_content_resp(uri, add_id=True) msg = ('The %s plugin got an error while requesting "%s".' ' Exception: "%s".' ' Generated 204 "No Content" response (id:%s)') args = (self.get_name(), uri, http_exception, no_content_resp.id) om.out.error(msg % args) return False, no_content_resp
def handle_url_error(self, uri, http_exception): """ Handle UrlError exceptions raised when requests are made. Subclasses should redefine this method for a more refined behavior and must respect the return value format. :param http_exception: HTTPRequestException exception instance :return: A tuple containing: * re_raise: Boolean value that indicates the caller if the original exception should be re-raised after this error handling method. * result: The result to be returned to the caller. This only makes sense if re_raise is False. """ no_content_resp = new_no_content_resp(uri, add_id=True) msg = ('The %s plugin got an error while requesting "%s".' ' Exception: "%s".' ' Generated 204 "No Content" response (id:%s)') args = (self.get_name(), uri, http_exception, no_content_resp.id) om.out.error(msg % args) return False, no_content_resp
def default_open(self, req): """ If blacklisted return an empty response, else return None, which means that this handler was unable to handle the request and the next one needs to be called. With this we want to indicate that the keepalive handler will be called. """ if self._is_blacklisted(req.url_object): nncr = new_no_content_resp(req.url_object) addinfo_inst = http_response_to_httplib(nncr) return addinfo_inst # This means: I don't know how to handle this, call the next opener return None
def default_open(self, req): """ If blacklisted return an empty response, else return None, which means that this handler was unable to handle the request and the next one needs to be called. With this we want to indicate that the keepalive handler will be called. """ if self._is_blacklisted(req.url_object): nncr = new_no_content_resp(req.url_object) addinfo_inst = http_response_to_httplib(nncr) return addinfo_inst # This means: I don't know how to handle this, call the next opener return None
def delay_for(self, delay, original_wait_time, grep, reverse=False): """ Sends a request to the remote end that "should" delay the response in `delay` seconds. :param delay: The delay object :param original_wait_time: The time that it takes to perform the request without adding any delays. :param grep: Should the framework grep the HTTP response sent for testing? :param reverse: Should we reverse the delay_str before sending it? :return: (True, response) if there was a delay. In order to make things right we first send some requests to measure the original wait time. """ delay_str = self.delay_obj.get_string_for_delay(delay) delay_str = delay_str if not reverse else delay_str[::-1] mutant = self.mutant.copy() mutant.set_token_value(delay_str) # Set the upper and lower bounds delta = original_wait_time * self.DELTA_PERCENT # Upper bound is the highest number we'll wait for a response, it # doesn't mean that it is the highest delay that might happen on # the application. # # So, for example if the application logic (for some reason) runs # our payload three times, and we send: # # sleep(10) # # The delay will be of 30 seconds, but we don't want to wait all # that time (using high timeouts for HTTP requests is *very* bad when # scanning slow apps). # # We just wait until `upper_bound` is reached upper_bound = original_wait_time + delta + delay * 2 # The lower_bound is the lowest number of seconds we require for this # HTTP response to be considered "delayed". # # I tried with different variations of this, the first one included # original_wait_time and delta, but that failed in this scenario: # # * RTT (which defines original_wait_time) is inaccurately high: 3 seconds # instead of 1 second which was the expected result. This could be # because of outliers in the measurement # # https://github.com/andresriancho/w3af/issues/16902 # # * lower_bound is then set to original_wait_time - delta + delay # # * The payload is sent and the response is delayed for 4 seconds # # * The delay_for method yields false because of the bad RTT lower_bound = delay # Send, it is important to notice that we don't use the cache # to avoid any interference try: response = self.uri_opener.send_mutant( mutant, grep=grep, cache=False, timeout=upper_bound, debugging_id=self.get_debugging_id()) except HTTPRequestException: # # We reach this part of the code when the server response times out # # Note that in the timeout parameter of send_mutant we're sending # the upper bound, so we reach this code if that upper bound is # exceeded. That can be because of: # # * Network delays unrelated to our payload # # * Our payload having an effect on the server, delaying the # response so much that it triggers the timeout # args = (id(self), upper_bound, lower_bound, delay, upper_bound) msg = (u'[id: %s] HTTP response delay was %.2f.' u' (lower, expected, upper): %.2f, %.2f, %.2f.') out.debug(msg % args) return True, new_no_content_resp(self.mutant.get_uri()) # We reach this code when the HTTP timeout wasn't reached, but we might still # have a working delay. This is most of the cases I've seen. current_response_wait_time = response.get_wait_time() args = (id(self), current_response_wait_time, lower_bound, delay, upper_bound) msg = (u'[id: %s] HTTP response delay was %.2f.' u' (lower, expected, upper): %.2f, %.2f, %.2f.') out.debug(msg % args) if current_response_wait_time > lower_bound: return True, response return False, response
def delay_for(self, delay, original_wait_time, grep, reverse=False): """ Sends a request to the remote end that "should" delay the response in `delay` seconds. :param delay: The delay object :param original_wait_time: The time that it takes to perform the request without adding any delays. :param grep: Should the framework grep the HTTP response sent for testing? :param reverse: Should we reverse the delay_str before sending it? :return: (True, response) if there was a delay. In order to make things right we first send some requests to measure the original wait time. """ delay_str = self.delay_obj.get_string_for_delay(delay) delay_str = delay_str if not reverse else delay_str[::-1] mutant = self.mutant.copy() mutant.set_token_value(delay_str) # Set the upper and lower bounds delta = original_wait_time * self.DELTA_PERCENT # Upper bound is the highest number we'll wait for a response, it # doesn't mean that it is the highest delay that might happen on # the application. # # So, for example if the application logic (for some reason) runs # our payload three times, and we send: # # sleep(10) # # The delay will be of 30 seconds, but we don't want to wait all # that time (using high timeouts for HTTP requests is *very* bad when # scanning slow apps). # # We just wait until `upper_bound` is reached upper_bound = original_wait_time + delta + delay * 2 # The lower_bound is the lowest number of seconds we require for this # HTTP response to be considered "delayed". # # I tried with different variations of this, the first one included # original_wait_time and delta, but that failed in this scenario: # # * RTT (which defines original_wait_time) is inaccurately high: 3 seconds # instead of 1 second which was the expected result. This could be # because of outliers in the measurement # # https://github.com/andresriancho/w3af/issues/16902 # # * lower_bound is then set to original_wait_time - delta + delay # # * The payload is sent and the response is delayed for 4 seconds # # * The delay_for method yields false because of the bad RTT lower_bound = delay # Send, it is important to notice that we don't use the cache # to avoid any interference try: response = self.uri_opener.send_mutant(mutant, grep=grep, cache=False, timeout=upper_bound, debugging_id=self.get_debugging_id()) except HTTPRequestException: # # We reach this part of the code when the server response times out # # Note that in the timeout parameter of send_mutant we're sending # the upper bound, so we reach this code if that upper bound is # exceeded. That can be because of: # # * Network delays unrelated to our payload # # * Our payload having an effect on the server, delaying the # response so much that it triggers the timeout # args = (id(self), upper_bound, lower_bound, delay, upper_bound) msg = (u'[id: %s] HTTP response delay was %.2f.' u' (lower, expected, upper): %.2f, %.2f, %.2f.') out.debug(msg % args) return True, new_no_content_resp(self.mutant.get_uri()) # We reach this code when the HTTP timeout wasn't reached, but we might still # have a working delay. This is most of the cases I've seen. current_response_wait_time = response.get_wait_time() args = (id(self), current_response_wait_time, lower_bound, delay, upper_bound) msg = (u'[id: %s] HTTP response delay was %.2f.' u' (lower, expected, upper): %.2f, %.2f, %.2f.') out.debug(msg % args) if current_response_wait_time > lower_bound: return True, response return False, response