def test_delay_controlled_random(self): for expected_result, delays in self.TEST_SUITE: print delays mock_uri_opener = Mock() side_effect = generate_delays(delays, rand_range=(0, 2)) mock_uri_opener.send_mutant = MagicMock(side_effect=side_effect) delay_obj = ExactDelay('sleep(%s)') url = URL('http://moth/?id=1') req = FuzzableRequest(url) mutant = QSMutant(req) mutant.set_dc(url.querystring) mutant.set_var('id', 0) ed = ExactDelayController(mutant, delay_obj, mock_uri_opener) controlled, responses = ed.delay_is_controlled() # This is where we change from test_delay_controlled, the basic # idea is that we'll allow false negatives but no false positives if expected_result == True: expected_result = [True, False] else: expected_result = [ False, ] self.assertIn(controlled, expected_result, delays)
def _with_time_delay(self, freq): """ Tests an URL for OS Commanding vulnerabilities using time delays. :param freq: A FuzzableRequest """ fake_mutants = create_mutants(freq, [ '', ]) for mutant in fake_mutants: if self._has_bug(mutant): continue for delay_obj in self._get_wait_commands(): ed = ExactDelayController(mutant, delay_obj, self._uri_opener) success, responses = ed.delay_is_controlled() if success: desc = 'OS Commanding was found at: %s' % mutant.found_at() v = Vuln.from_mutant('OS commanding vulnerability', desc, severity.HIGH, [r.id for r in responses], self.get_name(), mutant) v['os'] = delay_obj.get_OS() v['separator'] = delay_obj.get_separator() self.kb_append_uniq(self, 'os_commanding', v) break
def is_injectable(self, mutant): """ Check if this mutant is delay injectable or not. @mutant: The mutant object that I have to inject to :return: A vulnerability object or None if nothing is found """ for delay_obj in self._get_delays(): ed = ExactDelayController(mutant, delay_obj, self._uri_opener) success, responses = ed.delay_is_controlled() if success: # Now I can be sure that I found a vuln, we control the response # time with the delay desc = 'Blind SQL injection using time delays was found at: %s' desc = desc % mutant.found_at() response_ids = [r.id for r in responses] v = Vuln.from_mutant('Blind SQL injection vulnerability', desc, severity.HIGH, response_ids, 'blind_sqli', mutant) om.out.debug(v.get_desc()) return v return None
def test_delay_controlled(self): for expected_result, delays in self.TEST_SUITE: mock_uri_opener = Mock() side_effect = generate_delays(delays) mock_uri_opener.send_mutant = MagicMock(side_effect=side_effect) delay_obj = ExactDelay('sleep(%s)') url = URL('http://moth/?id=1') req = FuzzableRequest(url) mutant = QSMutant(req) mutant.set_dc(url.querystring) mutant.set_token(('id', 0)) ed = ExactDelayController(mutant, delay_obj, mock_uri_opener) controlled, responses = ed.delay_is_controlled() self.assertEqual(expected_result, controlled, delays)
def _with_time_delay(self, freq): """ Tests an URLs for shell shock vulnerabilities using time delays. :param freq: A FuzzableRequest :return: True if a vulnerability was found """ mutant = self.create_mutant(freq, TEST_HEADER) for delay_obj in self.DELAY_TESTS: ed = ExactDelayController(mutant, delay_obj, self._uri_opener) success, responses = ed.delay_is_controlled() if success: mutant.set_token_value(delay_obj.get_string_for_delay(3)) desc = u'Shell shock was found at: %s' % mutant.found_at() v = Vuln.from_mutant(u'Shell shock vulnerability', desc, severity.HIGH, [r.id for r in responses], self.get_name(), mutant) self.kb_append_uniq(self, 'shell_shock', v) return True
def _test_delay(self, mutant): """ Try to delay the response and save a vulnerability if successful """ if self._has_bug(mutant): return for delay_obj in self.WAIT_OBJ: ed_inst = ExactDelayController(mutant, delay_obj, self._uri_opener) success, responses = ed_inst.delay_is_controlled() if success: desc = 'eval() input injection was found at: %s' desc = desc % mutant.found_at() response_ids = [r.id for r in responses] v = Vuln.from_mutant('eval() input injection vulnerability', desc, severity.HIGH, response_ids, self.get_name(), mutant) self.kb_append_uniq(self, 'eval', v) break
class eval(AuditPlugin): """ Find insecure eval() usage. :author: Viktor Gazdag ( [email protected] ) :author: Andres Riancho ([email protected]) """ PRINT_REPEATS = 5 PRINT_STRINGS = ( # PHP http://php.net/eval "echo str_repeat('%%s',%s);" % PRINT_REPEATS, # Perl http://perldoc.perl.org/functions/eval.html "print '%%s'x%s" % PRINT_REPEATS, # Python # http://docs.python.org/reference/simple_stmts.html#the-exec-statement "print('%%s'*%s)" % PRINT_REPEATS, # ASP "Response.Write(new String(\"%%s\",%s))" % PRINT_REPEATS, ) WAIT_OBJ = ( # PHP http://php.net/sleep # Perl http://perldoc.perl.org/functions/sleep.html ExactDelay("sleep(%s);"), # Python http://docs.python.org/library/time.html#time.sleep ExactDelay("__import__('time').sleep(%s)"), # It seems that ASP doesn't support sleep! A language without sleep... # is not a language! # http://classicasp.aspfaq.com/general/how-do-i-make-my-asp-page-pause-or-sleep.html # JSP takes the amount in miliseconds # http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html#sleep(long) ExactDelay("Thread.sleep(%s);", mult=1000), # ASP.NET also uses miliseconds # http://msdn.microsoft.com/en-us/library/d00bd51t.aspx # Note: The Sleep in ASP.NET is uppercase ExactDelay("Thread.Sleep(%s);", mult=1000), # NodeJS eval ExactDelay("var cd;var d=new Date();do{cd=new Date();}while(cd-d<%s)", mult=1000) ) def __init__(self): AuditPlugin.__init__(self) # Create some random strings, which the plugin will use. # for the fuzz_with_echo self._rnd = rand_alpha(5) self._rnd = self._rnd.lower() self._expected_result = self._rnd * self.PRINT_REPEATS # User configured parameters self._use_time_delay = True self._use_echo = True def audit(self, freq, orig_response, debugging_id): """ Tests an URL for eval() user input injection vulnerabilities. :param freq: A FuzzableRequest :param orig_response: The HTTP response associated with the fuzzable request :param debugging_id: A unique identifier for this call to audit() """ if self._use_echo: self._fuzz_with_echo(freq, orig_response, debugging_id) if self._use_time_delay: self._fuzz_with_time_delay(freq, debugging_id) def _fuzz_with_echo(self, freq, orig_response, debugging_id): """ Tests an URL for eval() usage vulnerabilities using echo strings. :param freq: A FuzzableRequest """ print_strings = [pstr % (self._rnd,) for pstr in self.PRINT_STRINGS] mutants = create_mutants(freq, print_strings, orig_resp=orig_response) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_echo, debugging_id=debugging_id) def _fuzz_with_time_delay(self, freq, debugging_id): """ Tests an URL for eval() usage vulnerabilities using time delays. :param freq: A FuzzableRequest """ self._send_mutants_in_threads(func=self._find_delay_in_mutant, iterable=self._generate_delay_tests(freq, debugging_id), callback=lambda x, y: None) def _generate_delay_tests(self, freq, debugging_id): for mutant in create_mutants(freq, ['', ]): # # Don't try to find an eval() using a time delay method if we already found # it via echo # if self._has_bug(mutant): return for delay_obj in self.WAIT_OBJ: yield mutant, delay_obj, debugging_id def _find_delay_in_mutant(self, (mutant, delay_obj, debugging_id)): """ Try to delay the response and save a vulnerability if successful :param mutant: The mutant to modify and test :param delay_obj: The delay to use :param debugging_id: The debugging ID for logging """ if self._has_bug(mutant): return ed_inst = ExactDelayController(mutant, delay_obj, self._uri_opener) ed_inst.set_debugging_id(debugging_id) success, responses = ed_inst.delay_is_controlled() if success: desc = 'eval() input injection was found at: %s' desc %= mutant.found_at() response_ids = [r.id for r in responses] v = Vuln.from_mutant('eval() input injection vulnerability', desc, severity.HIGH, response_ids, self.get_name(), mutant) self.kb_append_uniq(self, 'eval', v)
class shell_shock(AuditPlugin): """ Find shell shock vulnerabilities. :author: Andres Riancho ([email protected]) """ DELAY_TESTS = [PingDelay('() { test; }; ping -c %s 127.0.0.1'), ExactDelay('() { test; }; sleep %s')] def __init__(self): super(shell_shock, self).__init__() self.already_tested_urls = ScalableBloomFilter() def audit(self, freq, orig_response, debugging_id): """ Tests an URL for shell shock vulnerabilities. :param freq: A FuzzableRequest :param orig_response: The HTTP response associated with the fuzzable request :param debugging_id: A unique identifier for this call to audit() """ url = freq.get_url() # Here the script is vulnerable, not a specific parameter, so we # run unique tests per URL if url not in self.already_tested_urls: self.already_tested_urls.add(url) # We are implementing these methods for detecting shell-shock vulns # if you know about other methods, or have improvements on these # please let us know. Pull-requests are also welcome. for detection_method in [self._with_header_echo_injection, #self._with_body_echo_injection, self._with_time_delay]: if detection_method(freq, debugging_id): break def _with_header_echo_injection(self, freq, debugging_id): """ We're sending a payload that will trigger the injection of various headers in the HTTP response body. :param freq: A FuzzableRequest :return: True if a vulnerability was found """ injected_header = 'shellshock' injected_value = 'check' payload = '() { :;}; echo "%s: %s"' % (injected_header, injected_value) mutant = self.create_mutant(freq, TEST_HEADER) mutant.set_token_value(payload) response = self._uri_opener.send_mutant(mutant, debugging_id=debugging_id) header_value, header_name = response.get_headers().iget(injected_header) if header_value is not None and injected_value in header_value.lower(): desc = u'Shell shock was found at: %s' % mutant.found_at() v = Vuln.from_mutant(u'Shell shock vulnerability', desc, severity.HIGH, [response.id], self.get_name(), mutant) self.kb_append_uniq(self, 'shell_shock', v) return True def _with_body_echo_injection(self, freq, debugging_id): """ We're sending a payload that will trigger the injection of new lines that will make the response transition from "headers" to "body". :param freq: A FuzzableRequest :return: True if a vulnerability was found """ raise NotImplementedError def create_mutant(self, freq, header_name): headers = freq.get_headers() headers[header_name] = '' freq.set_headers(headers) fuzzer_config = {'fuzzable_headers': [TEST_HEADER]} mutant = HeadersMutant.create_mutants(freq, [''], [TEST_HEADER], False, fuzzer_config)[0] return mutant def _with_time_delay(self, freq, debugging_id): """ Tests an URLs for shell shock vulnerabilities using time delays. :param freq: A FuzzableRequest :return: True if a vulnerability was found """ self._send_mutants_in_threads(func=self._find_delay_in_mutant, iterable=self._generate_delay_tests(freq, debugging_id), callback=lambda x, y: None) def _generate_delay_tests(self, freq, debugging_id): for delay_obj in self.DELAY_TESTS: mutant = self.create_mutant(freq, TEST_HEADER) yield mutant, delay_obj, debugging_id def _find_delay_in_mutant(self, (mutant, delay_obj, debugging_id)): """ Try to delay the response and save a vulnerability if successful :param mutant: The mutant to modify and test :param delay_obj: The delay to use :param debugging_id: The debugging ID for logging """ ed = ExactDelayController(mutant, delay_obj, self._uri_opener) ed.set_debugging_id(debugging_id) success, responses = ed.delay_is_controlled() if not success: return False mutant.set_token_value(delay_obj.get_string_for_delay(3)) desc = u'Shell shock was found at: %s' % mutant.found_at() v = Vuln.from_mutant(u'Shell shock vulnerability', desc, severity.HIGH, [r.id for r in responses], self.get_name(), mutant) self.kb_append_uniq(self, 'shell_shock', v) return True
if file_name.endswith(self.PAYLOAD_EXTENSION): json_str = file(os.path.join(root, file_name)).read() yield language, json.loads(json_str) def _find_delay_in_mutant(self, (mutant, delay_obj), debugging_id=None): """ Try to delay the response and save a vulnerability if successful :param mutant: The mutant to modify and test :param delay_obj: The delay to use :param debugging_id: The debugging ID for logging """ if self._has_bug(mutant): return ed = ExactDelayController(mutant, delay_obj, self._uri_opener) ed.set_debugging_id(debugging_id) success, responses = ed.delay_is_controlled() if not success: return desc = 'Insecure deserialization vulnerability was found at: %s' desc %= mutant.found_at() v = Vuln.from_mutant('Insecure deserialization', desc, severity.HIGH, [r.id for r in responses], self.get_name(), mutant) self.kb_append_uniq(self, 'deserialization', v)
class os_commanding(AuditPlugin): """ Find OS Commanding vulnerabilities. :author: Andres Riancho ([email protected]) """ FILE_PATTERNS = FILE_PATTERNS _multi_in = MultiIn(FILE_PATTERNS) def __init__(self): AuditPlugin.__init__(self) # # Some internal variables # self._special_chars = ['', '&&', '|', ';', '\n', '\r\n'] self._file_compiled_regex = [] def audit(self, freq, orig_response, debugging_id): """ Tests an URL for OS Commanding vulnerabilities. :param freq: A FuzzableRequest :param orig_response: The HTTP response associated with the fuzzable request :param debugging_id: A unique identifier for this call to audit() """ # We are implementing two different ways of detecting OS Commanding # vulnerabilities: # - Time delays # - Writing a known file to the HTML output # The basic idea is to be able to detect ANY vulnerability, so we use # ALL of the known techniques # # Please note that I'm running the echo ones first in order to get them # into the KB before the ones with time delays so that the os_commanding # exploit can (with a higher degree of confidence) exploit the # vulnerability # # This also speeds-up the detection process a little bit in the cases # where there IS a vulnerability present and can be found with both # methods. self._with_echo(freq, orig_response, debugging_id) self._with_time_delay(freq, debugging_id) def _with_echo(self, freq, orig_response, debugging_id): """ Tests an URL for OS Commanding vulnerabilities using cat/type to write the content of a known file (i.e. /etc/passwd) to the HTML. :param freq: A FuzzableRequest """ # Prepare the strings to create the mutants command_list = self._get_echo_commands() only_command_strings = [v.get_command() for v in command_list] # Create the mutants, notice that we use append=False (default) and # True to have better coverage. mutants = create_mutants(freq, only_command_strings, orig_resp=orig_response) mutants.extend( create_mutants(freq, only_command_strings, orig_resp=orig_response, append=True)) self._send_mutants_in_threads(self._uri_opener.send_mutant, mutants, self._analyze_echo, debugging_id=debugging_id) def _analyze_echo(self, mutant, response): """ Analyze results of the _send_mutant method that was sent in the _with_echo method. """ # # I will only report the vulnerability once. # if self._has_bug(mutant): return for file_pattern_match in self._multi_in.query(response.get_body()): if file_pattern_match in mutant.get_original_response_body(): continue # Search for the correct command and separator sent_os, sent_separator = self._get_os_separator(mutant) desc = 'OS Commanding was found at: %s' % mutant.found_at() # Create the vuln obj v = Vuln.from_mutant('OS commanding vulnerability', desc, severity.HIGH, response.id, self.get_name(), mutant) v['os'] = sent_os v['separator'] = sent_separator v.add_to_highlight(file_pattern_match) self.kb_append_uniq(self, 'os_commanding', v) break def _get_os_separator(self, mutant): """ :param mutant: The mutant that is being analyzed. :return: A tuple with the OS and the command separator that was used to generate the mutant. """ os = separator = None # Retrieve the data I need to create the vuln and the info objects command_list = self._get_echo_commands() # TODO: Are you sure that this works as expected ?! for comm in command_list: if comm.get_command() in mutant.get_token_value(): os = comm.get_OS() separator = comm.get_separator() return os, separator def _with_time_delay(self, freq, debugging_id): """ Tests an URL for OS Commanding vulnerabilities using time delays. :param freq: A FuzzableRequest """ self._send_mutants_in_threads(func=self._find_delay_in_mutant, iterable=self._generate_delay_tests( freq, debugging_id), callback=lambda x, y: None) def _generate_delay_tests(self, freq, debugging_id): fake_mutants = create_mutants(freq, [ '', ]) fake_mutants.extend(create_mutants(freq, [ '', ], append=True)) for mutant in fake_mutants: # # Don't try to find an OS commanding using a time delay method # if we already found it via echo # if self._has_bug(mutant): return for delay_obj in self._get_wait_commands(): yield mutant, delay_obj, debugging_id def _find_delay_in_mutant(self, (mutant, delay_obj, debugging_id)): """ Try to delay the response and save a vulnerability if successful :param mutant: The mutant to modify and test :param delay_obj: The delay to use :param debugging_id: The debugging ID for logging """ if self._has_bug(mutant): return ed = ExactDelayController(mutant, delay_obj, self._uri_opener) ed.set_debugging_id(debugging_id) success, responses = ed.delay_is_controlled() if not success: return desc = 'OS Commanding was found at: %s' % mutant.found_at() v = Vuln.from_mutant('OS commanding vulnerability', desc, severity.HIGH, [r.id for r in responses], self.get_name(), mutant) v['os'] = delay_obj.get_OS() v['separator'] = delay_obj.get_separator() self.kb_append_uniq(self, 'os_commanding', v)