def _analyze_result(self, mutant, response): """ Analyze results of the _send_mutant method. """ if self._has_bug(mutant): return if self._header_was_injected(mutant, response): desc = "Response splitting was found at: %s" % mutant.found_at() v = Vuln.from_mutant( "Response splitting vulnerability", desc, severity.MEDIUM, response.id, self.get_name(), mutant ) self.kb_append_uniq(self, "response_splitting", v) # When trying to send a response splitting to php 5.1.2 I get : # Header may not contain more than a single header, new line detected for error in self.HEADER_ERRORS: if error in response: desc = ( 'The variable "%s" at URL "%s" modifies the HTTP' " response headers, but this error was sent while" ' testing for response splitting: "%s".' ) args = (mutant.get_token_name(), mutant.get_url(), error) desc = desc % args i = Info.from_mutant("Parameter modifies response headers", desc, response.id, self.get_name(), mutant) self.kb_append_uniq(self, "response_splitting", i) return
def _analyze_result(self, mutant, response): """ Analyze results of the _send_mutant method. """ # # I will only report the vulnerability once. # if self._has_no_bug(mutant): if self._header_was_injected(mutant, response): desc = 'Response splitting was found at: %s' % mutant.found_at() v = Vuln.from_mutant('Response splitting vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'response_splitting', v) # When trying to send a response splitting to php 5.1.2 I get : # Header may not contain more than a single header, new line detected for error in self.HEADER_ERRORS: if error in response: desc = 'The variable "%s" at URL "%s" modifies the HTTP'\ ' response headers, but this error was sent while'\ ' testing for response splitting: "%s".' desc = desc % (mutant.get_var(), mutant.get_url(), error) i = Info.from_mutant('Parameter modifies response headers', desc, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'response_splitting', i) return
def _header_was_injected(self, mutant, response): """ This method verifies if a header was successfully injected :param mutant: The mutant that was sent to generate the response :param response: The HTTP response where I want to find the injected header. :return: True / False """ # Get the lower case headers headers = response.get_lower_case_headers() # Analyze injection for header, value in headers.items(): if HEADER_NAME in header and value.lower() == HEADER_VALUE: return True elif HEADER_NAME in header and value.lower() != HEADER_VALUE: msg = ('The vulnerable header was added to the HTTP response,' ' but the value is not what w3af expected (%s: %s).' ' Please verify manually.') msg = msg % (HEADER_NAME, HEADER_VALUE) om.out.information(msg) i = Info.from_mutant('Parameter modifies response headers', msg, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'response_splitting', i) return False return False
def _analyze_result(self, mutant, response): """ Analyze results of the _send_mutant method. """ if self._has_bug(mutant): return if self._header_was_injected(mutant, response): desc = 'Response splitting was found at: %s' % mutant.found_at() v = Vuln.from_mutant('Response splitting vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'response_splitting', v) # When trying to send a response splitting to php 5.1.2 I get : # Header may not contain more than a single header, new line detected for error in self.HEADER_ERRORS: if error in response: desc = ('The variable "%s" at URL "%s" modifies the HTTP' ' response headers, but this error was sent while' ' testing for response splitting: "%s".') args = (mutant.get_token_name(), mutant.get_url(), error) desc = desc % args i = Info.from_mutant('Parameter modifies response headers', desc, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'response_splitting', i) return
def _header_was_injected(self, mutant, response): """ This method verifies if a header was successfully injected :param mutant: The mutant that was sent to generate the response :param response: The HTTP response where I want to find the injected header. :return: True / False """ # Get the lower case headers headers = response.get_lower_case_headers() # Analyze injection for header, value in headers.items(): if HEADER_NAME in header and value.lower() == HEADER_VALUE: return True elif HEADER_NAME in header and value.lower() != HEADER_VALUE: msg = ( "The vulnerable header was added to the HTTP response," " but the value is not what w3af expected (%s: %s)." " Please verify manually." ) msg = msg % (HEADER_NAME, HEADER_VALUE) om.out.information(msg) i = Info.from_mutant("Parameter modifies response headers", msg, response.id, self.get_name(), mutant) self.kb_append_uniq(self, "response_splitting", i) return False return False
def test_from_mutant(self): dc = DataContainer() url = URL('http://moth/') payloads = ['abc', 'def'] dc['a'] = [ '1', ] dc['b'] = [ '2', ] freq = FuzzableRequest(url, dc=dc) fuzzer_config = {} created_mutants = Mutant.create_mutants(freq, payloads, [], False, fuzzer_config) mutant = created_mutants[0] inst = Info.from_mutant('TestCase', 'desc' * 30, 1, 'plugin_name', mutant) self.assertIsInstance(inst, Info) self.assertEqual(inst.get_uri(), mutant.get_uri()) self.assertEqual(inst.get_url(), mutant.get_url()) self.assertEqual(inst.get_method(), mutant.get_method()) self.assertEqual(inst.get_dc(), mutant.get_dc()) self.assertEqual(inst.get_var(), mutant.get_var())
def _report_php_errors(self, mutant, response): # When trying to send a response splitting to PHP 5.1.2 I get: # Header may not contain more than a single header, new line detected for error in self.HEADER_ERRORS: if error not in response: continue desc = ('The variable "%s" at URL "%s" modifies the HTTP' ' response headers, but this error was sent while' ' testing for response splitting: "%s".') args = (mutant.get_token_name(), mutant.get_url(), error) desc %= args i = Info.from_mutant('Parameter modifies response headers', desc, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'response_splitting', i) break
def _send_request(self, mutant): """ Sends a mutant to the remote web server. I wrap urllib's _send_mutant just to handle errors in a different way. """ try: response = self._uri_opener.send_mutant(mutant) except (BaseFrameworkException, ScanMustStopException): desc = 'A potential (most probably a false positive than a bug)' \ ' buffer-overflow was found when requesting: "%s", using' \ ' HTTP method %s. The data sent was: "%s".' desc = desc % (mutant.get_url(), mutant.get_method(), mutant.get_dc()) i = Info.from_mutant('Potential buffer overflow vulnerability', desc, [], self.get_name(), mutant) self.kb_append_uniq(self, 'buffer_overflow', i) else: self._analyze_result(mutant, response)
def test_from_mutant(self): url = URL("http://moth/?a=1&b=2") payloads = ["abc", "def"] freq = FuzzableRequest(url) fuzzer_config = {} created_mutants = QSMutant.create_mutants(freq, payloads, [], False, fuzzer_config) mutant = created_mutants[0] inst = Info.from_mutant("TestCase", "desc" * 30, 1, "plugin_name", mutant) self.assertIsInstance(inst, Info) self.assertEqual(inst.get_uri(), mutant.get_uri()) self.assertEqual(inst.get_url(), mutant.get_url()) self.assertEqual(inst.get_method(), mutant.get_method()) self.assertEqual(inst.get_dc(), mutant.get_dc()) self.assertIsInstance(inst.get_dc(), QueryString)
def test_from_mutant(self): url = URL('http://moth/?a=1&b=2') payloads = ['abc', 'def'] freq = FuzzableRequest(url) fuzzer_config = {} created_mutants = QSMutant.create_mutants(freq, payloads, [], False, fuzzer_config) mutant = created_mutants[0] inst = Info.from_mutant('TestCase', 'desc' * 30, 1, 'plugin_name', mutant) self.assertIsInstance(inst, Info) self.assertEqual(inst.get_uri(), mutant.get_uri()) self.assertEqual(inst.get_url(), mutant.get_url()) self.assertEqual(inst.get_method(), mutant.get_method()) self.assertEqual(inst.get_dc(), mutant.get_dc()) self.assertIsInstance(inst.get_dc(), QueryString)
def _send_request(self, mutant, debugging_id): """ Sends a mutant to the remote web server. I wrap urllib's _send_mutant just to handle errors in a different way. """ # Only grep the request which sends the larger payload grep = mutant.get_token_value() == self.BUFFER_TESTS[-1] try: response = self._uri_opener.send_mutant(mutant, debugging_id=debugging_id, grep=grep) except (BaseFrameworkException, ScanMustStopException): desc = ('A potential (most probably a false positive than a bug)' ' buffer-overflow was found when requesting: "%s", using' ' HTTP method %s. The data sent was: "%s".') desc %= (mutant.get_url(), mutant.get_method(), mutant.get_dc()) i = Info.from_mutant('Potential buffer overflow vulnerability', desc, [], self.get_name(), mutant) self.kb_append_uniq(self, 'buffer_overflow', i) else: self._analyze_result(mutant, response)
def test_from_mutant(self): dc = DataContainer() url = URL('http://moth/') payloads = ['abc', 'def'] dc['a'] = ['1', ] dc['b'] = ['2', ] freq = FuzzableRequest(url, dc=dc) fuzzer_config = {} created_mutants = Mutant.create_mutants(freq, payloads, [], False, fuzzer_config) mutant = created_mutants[0] inst = Info.from_mutant('TestCase', 'desc' * 30, 1, 'plugin_name', mutant) self.assertIsInstance(inst, Info) self.assertEqual(inst.get_uri(), mutant.get_uri()) self.assertEqual(inst.get_url(), mutant.get_url()) self.assertEqual(inst.get_method(), mutant.get_method()) self.assertEqual(inst.get_dc(), mutant.get_dc()) self.assertEqual(inst.get_var(), mutant.get_var())
def _analyze_result(self, mutant, response): """ Analyze results of the _send_mutant method. Try to find the local file inclusions. """ # # I will only report the vulnerability once. # if self._has_bug(mutant): return # # Identify the vulnerability # for file_pattern_match in self._find_common_file_fragments(response): if file_pattern_match not in mutant.get_original_response_body(): desc = 'Local File Inclusion was found at: %s' desc %= mutant.found_at() v = Vuln.from_mutant('Local file inclusion vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) v['file_pattern'] = file_pattern_match v.add_to_highlight(file_pattern_match) self.kb_append_uniq(self, 'lfi', v) return # # If the vulnerability could not be identified by matching strings that # commonly appear in "/etc/passwd", then I'll check one more thing... # (note that this is run if no vulns were identified) # # http://host.tld/show_user.php?id=show_user.php # # The calls to smart_str_ignore fix a UnicodeDecoreError which appears when # the token value is a binary string which can't be converted to unicode. # This happens, for example, when trying to upload JPG files to a multipart form # # >>> u'' in '\x80' # ... # UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0: ordinal not in range(128) # filename = smart_str_ignore(mutant.get_url().get_file_name()) token_value = smart_str_ignore(mutant.get_token_value()) if filename in token_value: match, lang = contains_source_code(response) if match: # We were able to read the source code of the file that is # vulnerable to local file read desc = ('An arbitrary local file read vulnerability was' ' found at: %s') desc %= mutant.found_at() v = Vuln.from_mutant('Local file inclusion vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) # # Set which part of the source code to match # match_source_code = match.group(0) v['file_pattern'] = match_source_code self.kb_append_uniq(self, 'lfi', v) return # # Check for interesting errors (note that this is run if no vulns were # identified) # body = response.get_body() for _, error_str, _ in self.file_read_error_multi_re.query(body): if error_str not in mutant.get_original_response_body(): desc = 'A file read error was found at: %s' desc %= mutant.found_at() i = Info.from_mutant('File read error', desc, response.id, self.get_name(), mutant) i.add_to_highlight(error_str) self.kb_append_uniq(self, 'error', i)
def _analyze_result(self, mutant, response): """ Analyze results of the _send_mutant method. Try to find the local file inclusions. """ # # I will only report the vulnerability once. # if self._has_bug(mutant): return # # Identify the vulnerability # for file_pattern_match in self._find_common_file_fragments(response): if file_pattern_match not in mutant.get_original_response_body(): desc = 'Local File Inclusion was found at: %s' desc %= mutant.found_at() v = Vuln.from_mutant('Local file inclusion vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) v['file_pattern'] = file_pattern_match v.add_to_highlight(file_pattern_match) self.kb_append_uniq(self, 'lfi', v) return # # If the vulnerability could not be identified by matching strings that # commonly appear in "/etc/passwd", then I'll check one more thing... # (note that this is run if no vulns were identified) # # http://host.tld/show_user.php?id=show_user.php if mutant.get_url().get_file_name() in mutant.get_token_value(): match, lang = contains_source_code(response) if match: # We were able to read the source code of the file that is # vulnerable to local file read desc = ('An arbitrary local file read vulnerability was' ' found at: %s') desc %= mutant.found_at() v = Vuln.from_mutant('Local file inclusion vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) # # Set which part of the source code to match # match_source_code = match.group(0) v['file_pattern'] = match_source_code self.kb_append_uniq(self, 'lfi', v) return # # Check for interesting errors (note that this is run if no vulns were # identified) # body = response.get_body() for _, error_str, _ in self.file_read_error_multi_re.query(body): if error_str not in mutant.get_original_response_body(): desc = 'A file read error was found at: %s' desc %= mutant.found_at() i = Info.from_mutant('File read error', desc, response.id, self.get_name(), mutant) i.add_to_highlight(error_str) self.kb_append_uniq(self, 'error', i)
def _analyze_result(self, mutant, response): """ Analyze results of the _send_mutant method. Try to find the local file inclusions. """ # I analyze the response searching for a specific PHP error string # that tells me that open_basedir is enabled, and our request triggered # the restriction. If open_basedir is in use, it makes no sense to keep # trying to read "/etc/passwd", that is why this variable is used to # determine which tests to send if it was possible to detect the usage # of this security feature. if not self._open_basedir: basedir_warning = "open_basedir restriction in effect" if basedir_warning in response and basedir_warning not in mutant.get_original_response_body(): self._open_basedir = True # # I will only report the vulnerability once. # if self._has_bug(mutant): return # # Identify the vulnerability # file_content_list = self._find_file(response) for file_pattern_match in file_content_list: if file_pattern_match not in mutant.get_original_response_body(): desc = "Local File Inclusion was found at: %s" desc = desc % mutant.found_at() v = Vuln.from_mutant( "Local file inclusion vulnerability", desc, severity.MEDIUM, response.id, self.get_name(), mutant ) v["file_pattern"] = file_pattern_match v.add_to_highlight(file_pattern_match) self.kb_append_uniq(self, "lfi", v) return # # If the vulnerability could not be identified by matching strings that # commonly appear in "/etc/passwd", then I'll check one more thing... # (note that this is run if no vulns were identified) # # http://host.tld/show_user.php?id=show_user.php if mutant.get_token_value() == mutant.get_url().get_file_name(): match, lang = is_source_file(response.get_body()) if match: # We were able to read the source code of the file that is # vulnerable to local file read desc = "An arbitrary local file read vulnerability was" " found at: %s" % mutant.found_at() v = Vuln.from_mutant( "Local file inclusion vulnerability", desc, severity.MEDIUM, response.id, self.get_name(), mutant ) # # Set which part of the source code to match # match_source_code = match.group(0) v["file_pattern"] = match_source_code self.kb_append_uniq(self, "lfi", v) return # # Check for interesting errors (note that this is run if no vulns were # identified) # for regex in self.get_include_errors(): match = regex.search(response.get_body()) if match and not regex.search(mutant.get_original_response_body()): desc = "A file read error was found at: %s" desc = desc % mutant.found_at() i = Info.from_mutant("File read error", desc, response.id, self.get_name(), mutant) self.kb_append_uniq(self, "error", i)
def _analyze_result(self, mutant, response): """ Analyze results of the _send_mutant method. Try to find the local file inclusions. """ # I analyze the response searching for a specific PHP error string # that tells me that open_basedir is enabled, and our request triggered # the restriction. If open_basedir is in use, it makes no sense to keep # trying to read "/etc/passwd", that is why this variable is used to # determine which tests to send if it was possible to detect the usage # of this security feature. if not self._open_basedir: basedir_warning = 'open_basedir restriction in effect' if basedir_warning in response and \ basedir_warning not in mutant.get_original_response_body(): self._open_basedir = True # # I will only report the vulnerability once. # if self._has_bug(mutant): return # # Identify the vulnerability # file_content_list = self._find_file(response) for file_pattern_match in file_content_list: if file_pattern_match not in mutant.get_original_response_body(): desc = 'Local File Inclusion was found at: %s' desc = desc % mutant.found_at() v = Vuln.from_mutant('Local file inclusion vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) v['file_pattern'] = file_pattern_match v.add_to_highlight(file_pattern_match) self.kb_append_uniq(self, 'lfi', v) return # # If the vulnerability could not be identified by matching strings that # commonly appear in "/etc/passwd", then I'll check one more thing... # (note that this is run if no vulns were identified) # # http://host.tld/show_user.php?id=show_user.php if mutant.get_token_value() == mutant.get_url().get_file_name(): match, lang = is_source_file(response.get_body()) if match: # We were able to read the source code of the file that is # vulnerable to local file read desc = 'An arbitrary local file read vulnerability was' \ ' found at: %s' % mutant.found_at() v = Vuln.from_mutant('Local file inclusion vulnerability', desc, severity.MEDIUM, response.id, self.get_name(), mutant) # # Set which part of the source code to match # match_source_code = match.group(0) v['file_pattern'] = match_source_code self.kb_append_uniq(self, 'lfi', v) return # # Check for interesting errors (note that this is run if no vulns were # identified) # for regex in self.get_include_errors(): match = regex.search(response.get_body()) if match and not regex.search(mutant.get_original_response_body()): desc = 'A file read error was found at: %s' desc = desc % mutant.found_at() i = Info.from_mutant('File read error', desc, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'error', i)