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 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)
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): """ Tests an URL for shell shock vulnerabilities. :param freq: A FuzzableRequest """ 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): break def _with_header_echo_injection(self, freq): """ 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) 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): """ 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): """ 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 get_long_desc(self): """ :return: A DETAILED description of the plugin functions and features. """ return """
def __init__(self, delay_fmt): Command.__init__(self, delay_fmt, 'unix', '') ExactDelay.__init__(self, delay_fmt)
def __init__(self, delay_fmt, os, sep): Command.__init__(self, delay_fmt, os, sep) ExactDelay.__init__(self, delay_fmt) self._delay_delta = 1
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): """ Tests an URL for eval() user input injection vulnerabilities. :param freq: A FuzzableRequest """ if self._use_echo: self._fuzz_with_echo(freq, orig_response) if self._use_time_delay: self._fuzz_with_time_delay(freq) def _fuzz_with_echo(self, freq, orig_response): """ 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) def _fuzz_with_time_delay(self, freq): """ Tests an URL for eval() usage vulnerabilities using time delays. :param freq: A FuzzableRequest """ fake_mutants = create_mutants(freq, [ '', ]) self.worker_pool.map(self._test_delay, fake_mutants) 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 def _analyze_echo(self, mutant, response): """ Analyze results of the _send_mutant method that was sent in the _fuzz_with_echo method. """ eval_error_list = self._find_eval_result(response) for eval_error in eval_error_list: if not re.search(eval_error, mutant.get_original_response_body(), re.I): desc = 'eval() input injection was found at: %s' desc = desc % mutant.found_at() v = Vuln.from_mutant('eval() input injection vulnerability', desc, severity.HIGH, response.id, self.get_name(), mutant) self.kb_append_uniq(self, 'eval', v) def _find_eval_result(self, response): """ This method searches for the randomized self._rnd string in HTMLs. :param response: The HTTP response object :return: A list of error found on the page """ res = [] if self._expected_result in response.body.lower(): msg = 'Verified eval() input injection, found the concatenated'\ ' random string: "%s" in the response body. The'\ ' vulnerability was found on response with id %s.' om.out.debug(msg % (self._expected_result, response.id)) res.append(self._expected_result) return res def get_options(self): """ :return: A list of option objects for this plugin. """ opt_list = OptionList() desc = 'Use time delay (sleep() technique)' _help = 'If set to True, w3af will checks insecure eval() usage by' \ ' analyzing of time delay result of script execution.' opt = opt_factory('use_time_delay', self._use_time_delay, desc, 'boolean', help=_help) opt_list.add(opt) desc = 'Use echo technique' _help = 'If set to True, w3af will checks insecure eval() usage by' \ ' grepping result of script execution for test strings.' opt = opt_factory('use_echo', self._use_echo, desc, 'boolean', help=_help) opt_list.add(opt) return opt_list def set_options(self, options_list): """ This method sets all the options that are configured using the user interface generated by the framework using the result of get_options(). :param options_list: A dictionary with the options for the plugin. :return: No value is returned. """ self._use_time_delay = options_list['use_time_delay'].get_value() self._use_echo = options_list['use_echo'].get_value() def get_long_desc(self): """ :return: A DETAILED description of the plugin functions and features. """ return """
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)
def _get_delays(self): """ :return: A list of statements that are going to be used to test for blind SQL injections. The statements are objects. IMPORTANT: Note that I need this function that generates unique instances of the delay objects! Adding this to a list that's defined at the class level will bring threading issues """ res = [] # MSSQL res.append(ExactDelay("1;waitfor delay '0:0:%s'--")) res.append(ExactDelay("1);waitfor delay '0:0:%s'--")) res.append(ExactDelay("1));waitfor delay '0:0:%s'--")) res.append(ExactDelay("1';waitfor delay '0:0:%s'--")) res.append(ExactDelay("1');waitfor delay '0:0:%s'--")) res.append(ExactDelay("1'));waitfor delay '0:0:%s'--")) # MySQL 5 # # Thank you guys for adding sleep(seconds) ! # res.append(ExactDelay("1 or SLEEP(%s)")) res.append(ExactDelay("1' or SLEEP(%s) and '1'='1")) res.append(ExactDelay('1" or SLEEP(%s) and "1"="1')) # MySQL 4 # # MySQL 4 doesn't have a sleep function, so I have to use BENCHMARK(1000000000,MD5(1)) # but the benchmarking will delay the response a different amount of time in each computer # which sucks because I use the time delay to check! # # In my test environment 3500000 delays 10 seconds # This is why I selected 2500000 which is guaranteed to (at least) delay 8 # seconds; and I only check the delay like this: # response.get_wait_time() > (original_wait_time + self._wait_time-2): # # With a small wait time of 5 seconds, this should work without problems... # and without hitting the ExtendedUrllib timeout ! # # TODO: Need to implement variable_delay.py (modification of ExactDelay) # and use the following there: # #res.append( delay("1 or BENCHMARK(2500000,MD5(1))") ) #res.append( delay("1' or BENCHMARK(2500000,MD5(1)) or '1'='1") ) #res.append( delay('1" or BENCHMARK(2500000,MD5(1)) or "1"="1') ) # PostgreSQL res.append(ExactDelay("1 or pg_sleep(%s)")) res.append(ExactDelay("1' or pg_sleep(%s) and '1'='1")) res.append(ExactDelay('1" or pg_sleep(%s) and "1"="1')) # TODO: Add Oracle support # TODO: Add XXXXX support return res
def _get_delays(self): """ :return: A list of statements that are going to be used to test for blind SQL injections. The statements are objects. IMPORTANT: Note that I need this function that generates unique instances of the delay objects! Adding this to a list that's defined at the class level will bring threading issues """ res = [] # MSSQL res.append(ExactDelay("1;waitfor delay '0:0:%s'--")) res.append(ExactDelay("1);waitfor delay '0:0:%s'--")) res.append(ExactDelay("1));waitfor delay '0:0:%s'--")) res.append(ExactDelay("1';waitfor delay '0:0:%s'--")) res.append(ExactDelay("1');waitfor delay '0:0:%s'--")) res.append(ExactDelay("1'));waitfor delay '0:0:%s'--")) # MySQL 5 # # Note: These payloads are better than "1 or SLEEP(%s)" since they # do not call SLEEP for each row in the table # # Payloads are heavily based on the ones from SQLMap which can be found # at xml/payloads/05_time_blind.xml # res.append(ExactDelay("1 AND (SELECT * FROM (SELECT(SLEEP(%s)))foo)")) res.append(ExactDelay("1 OR (SELECT * FROM (SELECT(SLEEP(%s)))foo)")) # Single and double quote string concat res.append(ExactDelay("'+(SELECT * FROM (SELECT(SLEEP(%s)))foo)+'")) res.append(ExactDelay('"+(SELECT * FROM (SELECT(SLEEP(%s)))foo)+"')) # These are required, they don't cover the same case than the previous # ones (string concat). res.append( ExactDelay( "' AND (SELECT * FROM (SELECT(SLEEP(%s)))foo) AND '1'='1")) res.append( ExactDelay( '" AND (SELECT * FROM (SELECT(SLEEP(%s)))foo) AND "1"="1')) res.append( ExactDelay( "' OR (SELECT * FROM (SELECT(SLEEP(%s)))foo) OR '1'='2")) res.append( ExactDelay( '" OR (SELECT * FROM (SELECT(SLEEP(%s)))foo) OR "1"="2')) # MySQL 4 # # MySQL 4 doesn't have a sleep function, so I have to use # BENCHMARK(1000000000,MD5(1)) but the benchmarking will delay the # response a different amount of time in each computer which sucks # because I use the time delay to check! # # In my test environment 3500000 delays 10 seconds # This is why I selected 2500000 which is guaranteed to (at least) delay # 8 seconds; and I only check the delay like this: # # response.get_wait_time() > (original_wait_time + self._wait_time-2) # # With a small wait time of 5 seconds, this should work without # problems... and without hitting the ExtendedUrllib timeout ! # # TODO: Need to implement variable_delay.py (modification of ExactDelay) # and use the following there: # #res.append( delay("1 or BENCHMARK(2500000,MD5(1))") ) #res.append( delay("1' or BENCHMARK(2500000,MD5(1)) or '1'='1") ) #res.append( delay('1" or BENCHMARK(2500000,MD5(1)) or "1"="1') ) # PostgreSQL res.append(ExactDelay("1 or pg_sleep(%s)")) res.append(ExactDelay("1' or pg_sleep(%s) and '1'='1")) res.append(ExactDelay('1" or pg_sleep(%s) and "1"="1')) # TODO: Add Oracle support # TODO: Add XXXXX support # TODO: https://github.com/andresriancho/w3af/issues/12385 return res
def __init__(self, delay_fmt): Command.__init__(self, delay_fmt, "unix", "") ExactDelay.__init__(self, delay_fmt)
class BlindSQLTimeDelay(object): """ This class tests for blind SQL injection bugs using time delays, the logic is here and not as an audit plugin because this logic is also used in attack plugins. :author: Andres Riancho ([email protected]) """ DELAYS = [ # MSSQL ExactDelay("1;waitfor delay '0:0:%s'--"), ExactDelay("1);waitfor delay '0:0:%s'--"), ExactDelay("1));waitfor delay '0:0:%s'--"), ExactDelay("1';waitfor delay '0:0:%s'--"), ExactDelay("1');waitfor delay '0:0:%s'--"), ExactDelay("1'));waitfor delay '0:0:%s'--"), # MySQL 5 # # Note: These payloads are better than "1 or SLEEP(%s)" since they # do not call SLEEP for each row in the table # # Payloads are heavily based on the ones from SQLMap which can be found # at xml/payloads/05_time_blind.xml # ExactDelay("1 AND (SELECT * FROM (SELECT(SLEEP(%s)))foo)"), ExactDelay("1 OR (SELECT * FROM (SELECT(SLEEP(%s)))foo)"), # Single and double quote string concat ExactDelay("'+(SELECT * FROM (SELECT(SLEEP(%s)))foo)+'"), ExactDelay('"+(SELECT * FROM (SELECT(SLEEP(%s)))foo)+"'), # These are required, they don't cover the same case than the previous # ones (string concat). ExactDelay("' AND (SELECT * FROM (SELECT(SLEEP(%s)))foo) AND '1'='1"), ExactDelay('" AND (SELECT * FROM (SELECT(SLEEP(%s)))foo) AND "1"="1'), ExactDelay("' OR (SELECT * FROM (SELECT(SLEEP(%s)))foo) OR '1'='2"), ExactDelay('" OR (SELECT * FROM (SELECT(SLEEP(%s)))foo) OR "1"="2'), # MySQL 4 # # MySQL 4 doesn't have a sleep function, so I have to use # BENCHMARK(1000000000,MD5(1)) but the benchmarking will delay the # response a different amount of time in each computer which sucks # because I use the time delay to check! # # In my test environment 3500000 delays 10 seconds # This is why I selected 2500000 which is guaranteed to (at least) delay # 8 seconds; and I only check the delay like this: # # response.get_wait_time() > (original_wait_time + self._wait_time-2) # # With a small wait time of 5 seconds, this should work without # problems... and without hitting the ExtendedUrllib timeout ! # # TODO: Need to implement variable_delay.py (modification of ExactDelay) # and use the following there: # # ExactDelay("1 or BENCHMARK(2500000,MD5(1))") ) # ExactDelay("1' or BENCHMARK(2500000,MD5(1)) or '1'='1") ) # ExactDelay('1" or BENCHMARK(2500000,MD5(1)) or "1"="1') ) # PostgreSQL ExactDelay("1 or pg_sleep(%s)"), ExactDelay("1' or pg_sleep(%s) and '1'='1"), ExactDelay('1" or pg_sleep(%s) and "1"="1'), # TODO: Add Oracle support # TODO: Add XXXXX support # TODO: https://github.com/andresriancho/w3af/issues/12385 ] def __init__(self, uri_opener): self._uri_opener = uri_opener self._debugging_id = None def set_debugging_id(self, debugging_id): self._debugging_id = debugging_id def get_debugging_id(self): return self._debugging_id def is_injectable(self, mutant, delay_obj): """ 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 """ ed = ExactDelayController(mutant, delay_obj, self._uri_opener) ed.set_debugging_id(self.get_debugging_id()) 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 def get_delays(self): """ :return: A list of statements that are going to be used to test for blind SQL injections. The statements are objects. IMPORTANT: Note that I need this function that generates unique instances of the delay objects! Adding this to a list that's defined at the class level will bring threading issues """ return self.DELAYS