def audit(self, freq, orig_response): """ Test URLs for CSRF vulnerabilities. :param freq: A FuzzableRequest """ if not self._is_suitable(freq): return # Referer/Origin check # # IMPORTANT NOTE: I'm aware that checking for the referer header does # NOT protect the application against all cases of CSRF, but it's a # very good first step. In order to exploit a CSRF in an application # that protects using this method an intruder would have to identify # other vulnerabilities such as XSS or open redirects. # # TODO: This algorithm has lots of room for improvement if self._is_origin_checked(freq, orig_response): om.out.debug('Origin for %s is checked' % freq.get_url()) return # Does the request have CSRF token in query string or POST payload? if self._find_csrf_token(freq): return # Ok, we have found vulnerable to CSRF attack request msg = 'Cross Site Request Forgery has been found at: ' + freq.get_url() v = Vuln.from_fr('CSRF vulnerability', msg, severity.MEDIUM, orig_response.id, self.get_name(), freq) self.kb_append_uniq(self, 'csrf', v)
def _analyze_ips(self, ip_address_list, fuzzable_request): """ Search all IP addresses in Bing and determine if they have more than one domain hosted on it. Store findings in KB. """ bing_wrapper = bing(self._uri_opener) # This is the best way to search, one by one! for ip_address in ip_address_list: results = bing_wrapper.get_n_results("ip:" + ip_address, self._result_limit) results = [r.URL.base_url() for r in results] results = list(set(results)) # not vuln by default is_vulnerable = False if len(results) > 1: # We may have something... is_vulnerable = True if len(results) == 2: # Maybe we have this case: # [Mon 09 Jun 2008 01:08:26 PM ART] - http://216.244.147.14/ # [Mon 09 Jun 2008 01:08:26 PM ART] - http://www.business.com/ # Where www.business.com resolves to 216.244.147.14; so we don't really # have more than one domain in the same server. try: res0 = socket.gethostbyname(results[0].get_domain()) res1 = socket.gethostbyname(results[1].get_domain()) except: pass else: if res0 == res1: is_vulnerable = False if is_vulnerable: desc = ( "The web application under test seems to be in a shared" " hosting. This list of domains, and the domain of the " " web application under test, all point to the same IP" " address (%s):\n" % ip_address ) domain_list = kb.kb.raw_read(self, "domains") for url in results: domain = url.get_domain() desc += "- %s\n" % domain domain_list.append(domain) kb.kb.raw_write(self, "domains", domain_list) v = Vuln.from_fr("Shared hosting", desc, severity.MEDIUM, 1, self.get_name(), fuzzable_request) v["also_in_hosting"] = results om.out.vulnerability(desc, severity=severity.MEDIUM) kb.kb.append(self, "shared_hosting", v)
def _report_vuln(self, debug_msg, freq, rid): debug_msg = debug_msg % (freq.get_uri(), rid) om.out.debug(debug_msg) desc = u"Reflected File Download has been " u"found at: %s" desc = desc % freq.get_url() v = Vuln.from_fr(u"Reflected File Download vulnerability", desc, severity.HIGH, rid, self.get_name(), freq) self.kb_append_uniq(self, "rfd", v)
def check_is_open_web_socket(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket is open: * Any Origin can connect * No cookies required for authentication * No basic auth required for authentication """ upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=self.W3AF_ORIGIN) upgrade_response = self._uri_opener.send_mutant(upgrade_request, cookies=False, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): return False msg = ('An HTML5 WebSocket which allows connections from any origin' ' without authentication was found at "%s"') msg %= web_socket_url v = Vuln.from_fr('Open WebSocket', msg, severity.LOW, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True
def _analyze_ips(self, ip_address_list, fuzzable_request): """ Search all IP addresses in Bing and determine if they have more than one domain hosted on it. Store findings in KB. """ bing_wrapper = bing(self._uri_opener) # This is the best way to search, one by one! for ip_address in ip_address_list: results = bing_wrapper.get_n_results('ip:' + ip_address, self._result_limit) results = [r.URL.base_url() for r in results] results = list(set(results)) # not vuln by default is_vulnerable = False if len(results) > 1: # We may have something... is_vulnerable = True if len(results) == 2: # Maybe we have this case: # [Mon 09 Jun 2008 01:08:26 PM ART] - http://216.244.147.14/ # [Mon 09 Jun 2008 01:08:26 PM ART] - http://www.business.com/ # Where www.business.com resolves to 216.244.147.14; so we don't really # have more than one domain in the same server. try: res0 = socket.gethostbyname(results[0].get_domain()) res1 = socket.gethostbyname(results[1].get_domain()) except: pass else: if res0 == res1: is_vulnerable = False if is_vulnerable: desc = 'The web application under test seems to be in a shared' \ ' hosting. This list of domains, and the domain of the ' \ ' web application under test, all point to the same IP' \ ' address (%s):\n' % ip_address domain_list = kb.kb.raw_read(self, 'domains') for url in results: domain = url.get_domain() desc += '- %s\n' % domain domain_list.append(domain) kb.kb.raw_write(self, 'domains', domain_list) v = Vuln.from_fr('Shared hosting', desc, severity.MEDIUM, 1, self.get_name(), fuzzable_request) v['also_in_hosting'] = results om.out.vulnerability(desc, severity=severity.MEDIUM) kb.kb.append(self, 'shared_hosting', v)
def _report_results(self, fuzzable_request, analysis_result): """ Report our findings """ reported = set() for vhost, request_id in analysis_result: if vhost in reported: continue reported.add(vhost) domain = fuzzable_request.get_url().get_domain() desc = ( u"Found a new virtual host at the target web server, the" u' virtual host name is: "%s". To access this site' u" you might need to change your DNS resolution settings" u' in order to point "%s" to the IP address of "%s".' ) desc %= (vhost, vhost, domain) v = Vuln.from_fr( "Virtual host identified", desc, severity.LOW, request_id, self.get_name(), fuzzable_request ) kb.kb.append(self, "find_vhosts", v) om.out.information(v.get_desc())
def _html_in_comment(self, comment, request, response): """ Find HTML code in HTML comments """ html_in_comment = self.HTML_RE.search(comment) if html_in_comment is None: return if (comment, response.get_url()) in self._already_reported: return # There is HTML code in the comment. comment = comment.strip() comment = comment.replace('\n', '') comment = comment.replace('\r', '') comment = comment[:40] desc = ('A comment with the string "%s" was found in: "%s".' ' This could be interesting.') desc %= (comment, response.get_url()) v = Vuln.from_fr('HTML comment contains HTML code', desc, severity.INFORMATION, response.id, self.get_name(), request) v.set_uri(response.get_uri()) v.add_to_highlight(html_in_comment.group(0)) om.out.vulnerability(v.get_desc(), severity=severity.INFORMATION) kb.kb.append(self, 'html_comment_hides_html', v) self._already_reported.add((comment, response.get_url()))
def _check_potential_vhosts(self, fuzzable_request, vhosts): """ Send the HTTP requests to check for potential findings :param fuzzable_request: The fuzzable request as received by the plugin :param vhosts: A generator yielding potential vhosts to check :return: None, vulnerabilities (if any) are written to the KB """ # Get some responses to compare later base_url = fuzzable_request.get_url().base_url() original_response = self._uri_opener.GET(base_url, cache=True) orig_resp_body = original_response.get_body() non_existent_responses = self._get_non_exist(fuzzable_request) for vhost, vhost_response in self._send_in_threads(base_url, vhosts): if not self._response_is_different(vhost_response, orig_resp_body, non_existent_responses): continue domain = fuzzable_request.get_url().get_domain() desc = (u'Found a new virtual host at the target web server, the' u' virtual host name is: "%s". To access this site' u' you might need to change your DNS resolution settings' u' in order to point "%s" to the IP address of "%s".') desc %= (vhost, vhost, domain) ids = [vhost_response.id, original_response.id] ids.extend([r.id for r in non_existent_responses]) v = Vuln.from_fr('Virtual host identified', desc, severity.LOW, ids, self.get_name(), fuzzable_request) kb.kb.append(self, 'find_vhosts', v) om.out.information(v.get_desc())
def _report_vuln(self, debug_msg, freq, rid): debug_msg = debug_msg % (freq.get_uri(), rid) om.out.debug(debug_msg) desc = u'Reflected File Download has been ' \ u'found at: %s' desc = desc % freq.get_url() v = Vuln.from_fr(u'Reflected File Download vulnerability', desc, severity.HIGH, rid, self.get_name(), freq) self.kb_append_uniq(self, 'rfd', v)
def audit(self, freq, orig_response): url = URL(freq.get_url() + self._PAYLOAD) print 'the url is %s', url freq_new = FuzzableRequest(url, method='GET') response = self._uri_opener.send_mutant(freq_new) print response.get_body() if 'root:/root' in response.get_body(): msg = 'Directory Traversal Vulnerbility found at ' + freq.get_url() v = Vuln.from_fr('Directory Traversal vulnerability', msg, severity.MEDIUM, orig_response.id, self.get_name(), freq) print 'hello there' self.kb_append_uniq(self, 'directory_traversal', v)
def check_is_restricted_by_origin_with_match_bug(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket checks the origin for connections but there is a bug in the matching process """ # # Keep in mind that we get here only if the websocket is NOT an open # (accepts any origin) socket. So we're in a situation where the socket # is either verifying by Origin+Cookies, Origin+Basic Auth or just # Origin. # # We want to check for the "just Origin" now, with a twist, we're # checking if there is a mistake in the Origin domain match process # # This is the trick: origin_domain = web_socket_url.get_domain() origin_domain += '.%s' % self.W3AF_DOMAIN for scheme in {'http', 'https'}: origin = '%s://%s' % (scheme, origin_domain) upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=origin) upgrade_response = self._uri_opener.send_mutant(upgrade_request, cookies=False, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): continue msg = ('An HTML5 WebSocket which restricts connections based on the' ' Origin header was found to be vulnerable because of an' ' incorrect matching algorithm. The "%s" Origin was allowed' ' to connect to "%s".') msg %= (origin_domain, web_socket_url) v = Vuln.from_fr('Insecure WebSocket Origin filter', msg, severity.MEDIUM, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True return False
def _extract_urls(self, fuzzable_request, response): """ Extract information from the server-status page and return fuzzable requests to the caller. """ res = self._create_fuzzable_requests(response) # Now really parse the file and create custom made fuzzable requests regex = '<td>.*?<td nowrap>(.*?)</td><td nowrap>.*? (.*?) HTTP/1' for domain, path in re.findall(regex, response.get_body()): if 'unavailable' in domain: domain = response.get_url().get_domain() # Check if the requested domain and the found one are equal. if domain == response.get_url().get_domain(): proto = response.get_url().get_protocol() found_url = proto + '://' + domain + path found_url = URL(found_url) # They are equal, request the URL and create the fuzzable # requests tmp_res = self._uri_opener.GET(found_url, cache=True) if not is_404(tmp_res): res.extend(self._create_fuzzable_requests(tmp_res)) else: # This is a shared hosting server self._shared_hosting_hosts.append(domain) # Now that we are outsite the for loop, we can report the possible vulns if len(self._shared_hosting_hosts): desc = 'The web application under test seems to be in a shared'\ ' hosting.' v = Vuln.from_fr('Shared hosting', desc, severity.MEDIUM, response.id, self.get_name(), fuzzable_request) self._shared_hosting_hosts = list(set(self._shared_hosting_hosts)) v['also_in_hosting'] = self._shared_hosting_hosts kb.kb.append(self, 'shared_hosting', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) msg = 'This list of domains, and the domain of the web application'\ ' under test, all point to the same server:' om.out.vulnerability(msg, severity=severity.MEDIUM) for url in self._shared_hosting_hosts: om.out.vulnerability('- ' + url, severity=severity.MEDIUM) return res
def _extract_urls(self, fuzzable_request, response): """ Extract information from the server-status page and return fuzzable requests to the caller. """ res = self._create_fuzzable_requests(response) # Now really parse the file and create custom made fuzzable requests regex = "<td>.*?<td nowrap>(.*?)</td><td nowrap>.*? (.*?) HTTP/1" for domain, path in re.findall(regex, response.get_body()): if "unavailable" in domain: domain = response.get_url().get_domain() # Check if the requested domain and the found one are equal. if domain == response.get_url().get_domain(): proto = response.get_url().get_protocol() found_url = proto + "://" + domain + path found_url = URL(found_url) # They are equal, request the URL and create the fuzzable # requests tmp_res = self._uri_opener.GET(found_url, cache=True) if not is_404(tmp_res): res.extend(self._create_fuzzable_requests(tmp_res)) else: # This is a shared hosting server self._shared_hosting_hosts.append(domain) # Now that we are outsite the for loop, we can report the possible vulns if len(self._shared_hosting_hosts): desc = "The web application under test seems to be in a shared" " hosting." v = Vuln.from_fr("Shared hosting", desc, severity.MEDIUM, response.id, self.get_name(), fuzzable_request) self._shared_hosting_hosts = list(set(self._shared_hosting_hosts)) v["also_in_hosting"] = self._shared_hosting_hosts kb.kb.append(self, "shared_hosting", v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) msg = ( "This list of domains, and the domain of the web application" " under test, all point to the same server:" ) om.out.vulnerability(msg, severity=severity.MEDIUM) for url in self._shared_hosting_hosts: om.out.vulnerability("- " + url, severity=severity.MEDIUM) return res
def check_is_restricted_by_origin(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket checks the origin for connections: * Only the same origin can connect * Send any cookie/basic auth known to the scanner """ # # Keep in mind that we get here only if the websocket is NOT an open # (accepts any origin) socket. So we're in a situation where the socket # is either verifying by Origin+Cookies, Origin+Basic Auth or just # Origin. # # We want to check for the "just Origin" now # origin_domain = web_socket_url.get_domain() for scheme in {'http', 'https'}: origin = '%s://%s' % (scheme, origin_domain) upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=origin) upgrade_response = self._uri_opener.send_mutant(upgrade_request, cookies=False, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): continue msg = ('An HTML5 WebSocket which allows connections only when the' ' origin is set to "%s" was found at "%s"') msg %= (origin_domain, web_socket_url) v = Vuln.from_fr('Origin restricted WebSocket', msg, severity.LOW, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True return False
def _report_shared_hosting(self, fuzzable_request, response): # Now that we are outsite the for loop, we can report the possible vulns if len(self._shared_hosting_hosts): desc = 'The web application under test seems to be in a shared'\ ' hosting.' v = Vuln.from_fr('Shared hosting', desc, severity.MEDIUM, response.id, self.get_name(), fuzzable_request) self._shared_hosting_hosts = list(set(self._shared_hosting_hosts)) v['also_in_hosting'] = self._shared_hosting_hosts kb.kb.append(self, 'shared_hosting', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) msg = 'This list of domains, and the domain of the web application'\ ' under test, all point to the same server:' om.out.vulnerability(msg, severity=v.get_severity()) for url in self._shared_hosting_hosts: om.out.vulnerability('- ' + url, severity=severity.MEDIUM)
def _report_shared_hosting(self, fuzzable_request, response): # Now that we are outsite the for loop, we can report the possible vulns if len(self._shared_hosting_hosts): desc = "The web application under test seems to be in a shared" " hosting." v = Vuln.from_fr("Shared hosting", desc, severity.MEDIUM, response.id, self.get_name(), fuzzable_request) self._shared_hosting_hosts = list(set(self._shared_hosting_hosts)) v["also_in_hosting"] = self._shared_hosting_hosts kb.kb.append(self, "shared_hosting", v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) msg = ( "This list of domains, and the domain of the web application" " under test, all point to the same server:" ) om.out.vulnerability(msg, severity=v.get_severity()) for url in self._shared_hosting_hosts: om.out.vulnerability("- " + url, severity=severity.MEDIUM)
def check_need_cookie_origin_not_restricted(self, web_socket_url, web_socket_version): """ Note that this method only makes sense if called in a loop with the other check_* methods. :param web_socket_url: The URL of the web socket :param web_socket_version: The protocol version :return: True if the web socket does NOT check the origin for connections but DOES require cookies to connect """ # # Keep in mind that we get here only if: # * The websocket is NOT an open (accepts any origin) socket # * The websocket is NOT verifying by Origin # # So we're in one of these cases: # * The websocket authenticates by cookie # * The websocket authenticates by basic auth # # We want to check for the "authenticates by cookie" # upgrade_request = build_ws_upgrade_request(web_socket_url, web_socket_version=web_socket_version, origin=self.W3AF_ORIGIN) upgrade_response = self._uri_opener.send_mutant(upgrade_request, # Note the True here! cookies=True, use_basic_auth=False) if not is_successful_upgrade(upgrade_response): return False msg = 'Cross-Site WebSocket Hijacking has been found at "%s"' msg %= web_socket_url v = Vuln.from_fr('Websockets CSRF vulnerability', msg, severity.HIGH, upgrade_response.id, self.get_name(), upgrade_request) self.kb_append_uniq(self, 'websocket_hijacking', v) return True
def audit(self, freq, orig_response, debugging_id): """ Verify xst vulns by sending a TRACE request and analyzing the response. :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 not self._exec: return # Only run once self._exec = False uri = freq.get_url().get_domain_path() method = 'TRACE' headers = Headers() headers['FakeHeader'] = 'XST' fr = FuzzableRequest(uri, method=method, headers=headers ) # send the request to the server and receive the response response = self._uri_opener.send_mutant(fr) # create a regex to test the response. regex = re.compile("FakeHeader: *?XST", re.IGNORECASE) if regex.search(response.get_body()): # If vulnerable record it. This will now become visible on # the KB Browser desc = 'The web server at "%s" is vulnerable to Cross Site'\ ' Tracing.' desc = desc % response.get_url() v = Vuln.from_fr('Cross site tracing vulnerability', desc, severity.LOW, response.id, self.get_name(), freq) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) self.kb_append(self, 'xst', v)
def _report_results(self, fuzzable_request, analysis_result): """ Report our findings """ reported = set() for vhost, request_id in analysis_result: if vhost not in reported: reported.add(vhost) domain = fuzzable_request.get_url().get_domain() desc = 'Found a new virtual host at the target web server, the ' \ 'virtual host name is: "%s". To access this site' \ ' you might need to change your DNS resolution settings in' \ ' order to point "%s" to the IP address of "%s".' desc = desc % (vhost, vhost, domain) v = Vuln.from_fr('Virtual host identified', desc, severity.LOW, request_id, self.get_name(), fuzzable_request) kb.kb.append(self, 'find_vhosts', v) om.out.information(v.get_desc())
def _interesting_word(self, comment, request, response): """ Find interesting words in HTML comments """ comment = comment.lower() for word in self._multi_in.query(comment): if (word, response.get_url()) in self._already_reported: continue desc = ('A comment with the string "%s" was found in: "%s".' ' This could be interesting.') desc %= (word, response.get_url()) v = Vuln.from_fr('Interesting HTML comment', desc, severity.INFORMATION, response.id, self.get_name(), request) v.add_to_highlight(word) kb.kb.append(self, 'interesting_comments', v) self._already_reported.add((word, response.get_url()))
def audit(self, freq, orig_response, debugging_id): """ Verify xst vulns by sending a TRACE request and analyzing the response. :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 not self._exec: return # Only run once self._exec = False uri = freq.get_url().get_domain_path() method = 'TRACE' headers = Headers() headers['FakeHeader'] = 'XST' fr = FuzzableRequest(uri, method=method, headers=headers) # send the request to the server and receive the response response = self._uri_opener.send_mutant(fr) # create a regex to test the response. regex = re.compile("FakeHeader: *?XST", re.IGNORECASE) if regex.search(response.get_body()): # If vulnerable record it. This will now become visible on # the KB Browser desc = 'The web server at "%s" is vulnerable to Cross Site'\ ' Tracing.' desc = desc % response.get_url() v = Vuln.from_fr('Cross site tracing vulnerability', desc, severity.LOW, response.id, self.get_name(), freq) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) self.kb_append(self, 'xst', v)
def audit(self, freq, orig_response, debugging_id): """ Check if the protocol specified in freq is https and fetch the same URL using http. ie: - input: https://w3af.org/ - check: http://w3af.org/ :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 not self._should_run: return initial_uri = freq.get_uri() if initial_uri.get_port() not in {80, 443}: # We get here then the original URL looks like http://foo:3921/ # # It's really strange (maybe not even possible?) to find a server # that listens for HTTP and HTTPS connections on the same port, # since we don't want to guess the port, nor generate errors such # as #8871 we just ignore this case self._should_run = False return # Define some variables insecure_uri = initial_uri.copy() secure_uri = initial_uri.copy() insecure_uri.set_protocol('http') insecure_fr = copy.deepcopy(freq) insecure_fr.set_url(insecure_uri) secure_uri.set_protocol('https') secure_fr = copy.deepcopy(freq) secure_fr.set_url(secure_uri) # Make sure that we disable error handling during these tests, we want # the requests to fail quickly and without affecting the library's error # rate send_mutant = self._uri_opener.send_mutant kwargs = {'grep': False, 'error_handling': False} try: insecure_response = send_mutant(insecure_fr, **kwargs) secure_response = send_mutant(secure_fr, **kwargs) except (HTTPRequestException, ScanMustStopException): # No vulnerability to report since one of these threw an error # (because there is nothing listening on that port). It makes # no sense to keep running since we already got an error self._should_run = False else: if insecure_response is None or secure_response is None: # No vulnerability to report since one of these threw an # error (because there is nothing listening on that port). # It makes no sense to keep running since we already got an # error self._should_run = False return if self._redirects_to_secure(insecure_response, secure_response): return if insecure_response.get_code() == secure_response.get_code()\ and fuzzy_equal(insecure_response.get_body(), secure_response.get_body(), 0.95): desc = ('Secure content can be accessed using the insecure' ' HTTP protocol. The vulnerable URLs used to verify' ' this vulnerability are:\n' ' - %s\n' ' - %s\n') desc %= (secure_uri, insecure_uri) response_ids = [insecure_response.id, secure_response.id] v = Vuln.from_fr('Secure content over insecure channel', desc, severity.MEDIUM, response_ids, self.get_name(), freq) self.kb_append(self, 'un_ssl', v) # In most cases, when one resource is available, all are # so we just stop searching for this vulnerability self._should_run = False
def _brute_worker(self, freq, user_field, passwd_field, login_failed_result_list, combination): """ :param freq: A FuzzableRequest :param combination: A tuple with (user, pass) or a pass if this is a password only form. """ if freq.get_url() not in self._found or not self._stop_on_first: freq = freq.copy() data_container = freq.get_dc() data_container = self._true_extra_fields( data_container, user_field, passwd_field) # Handle password-only forms! if user_field is not None: user, pwd = combination data_container[user_field][0] = user data_container[passwd_field][0] = pwd else: user = '******' pwd = combination data_container[passwd_field][0] = pwd freq.set_dc(data_container) try: resp = self._uri_opener.send_mutant(freq, cookies=False, grep=False) except ScanMustStopOnUrlError: return else: body = resp.get_body() body = body.replace(user, '').replace(pwd, '') if self._matches_failed_login(body, login_failed_result_list): return # Ok, this might be a valid combination. # Now test with a new invalid password to ensure our # previous possible found credentials are valid data_container[passwd_field][0] = rand_alnum(8) freq.set_dc(data_container) verif_resp = self._uri_opener.send_mutant(freq, cookies=False, grep=False) body = verif_resp.get_body() body = body.replace(user, '').replace(pwd, '') if self._matches_failed_login(body, login_failed_result_list): freq_url = freq.get_url() self._found.add(freq_url) if user_field is not None: desc = ('Found authentication credentials to: ' '"%s". A correct user and password combination' ' is: %s/%s' % (freq_url, user, pwd)) else: # There is no user field! desc = ('Found authentication credentials to: ' '"%s". The correct password is: "%s".' % (freq_url, pwd)) v = Vuln.from_fr('Guessable credentials', desc, severity.HIGH, resp.id, self.get_name(), freq) v['user'] = user v['pass'] = pwd v['response'] = resp kb.kb.append(self, 'auth', v) om.out.vulnerability(desc, severity=severity.HIGH) return
def audit(self, freq, orig_response): """ Check if the protocol specified in freq is https and fetch the same URL using http. ie: - input: https://w3af.org/ - check: http://w3af.org/ :param freq: A FuzzableRequest """ if not self._should_run: return initial_uri = freq.get_uri() if initial_uri.get_port() not in {80, 443}: # We get here then the original URL looks like http://foo:3921/ # # It's really strange (maybe not even possible?) to find a server # that listens for HTTP and HTTPS connections on the same port, # since we don't want to guess the port, nor generate errors such # as #8871 we just ignore this case self._should_run = False return # Define some variables insecure_uri = initial_uri.copy() secure_uri = initial_uri.copy() insecure_uri.set_protocol('http') insecure_fr = copy.deepcopy(freq) insecure_fr.set_url(insecure_uri) secure_uri.set_protocol('https') secure_fr = copy.deepcopy(freq) secure_fr.set_url(secure_uri) # Make sure that we disable error handling during these tests, we want # the requests to fail quickly and without affecting the library's error # rate send_mutant = self._uri_opener.send_mutant kwargs = {'grep': False, 'error_handling': False} try: insecure_response = send_mutant(insecure_fr, **kwargs) secure_response = send_mutant(secure_fr, **kwargs) except (HTTPRequestException, ScanMustStopException): # No vulnerability to report since one of these threw an error # (because there is nothing listening on that port). It makes # no sense to keep running since we already got an error self._should_run = False else: if insecure_response is None or secure_response is None: # No vulnerability to report since one of these threw an # error (because there is nothing listening on that port). # It makes no sense to keep running since we already got an # error self._should_run = False return if self._redirects_to_secure(insecure_response, secure_response): return if insecure_response.get_code() == secure_response.get_code()\ and relative_distance_boolean(insecure_response.get_body(), secure_response.get_body(), 0.95): desc = 'Secure content can be accessed using the insecure'\ ' protocol HTTP. The vulnerable URLs are:'\ ' "%s" - "%s" .' desc = desc % (secure_uri, insecure_uri) response_ids = [insecure_response.id, secure_response.id] v = Vuln.from_fr('Secure content over insecure channel', desc, severity.MEDIUM, response_ids, self.get_name(), freq) self.kb_append(self, 'un_ssl', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) # In most cases, when one resource is available, all are # so we just stop searching for this vulnerability self._should_run = False
def audit(self, freq, orig_response): """ Check if the protocol specified in freq is https and fetch the same URL using http. ie: - input: https://w3af.org/ - check: http://w3af.org/ :param freq: A FuzzableRequest """ if not self._run: return else: # Define some variables initial_uri = freq.get_uri() insecure_uri = initial_uri.copy() secure_uri = initial_uri.copy() insecure_uri.set_protocol('http') insecure_fr = freq.copy() insecure_fr.set_url(insecure_uri) secure_uri.set_protocol('https') secure_fr = freq.copy() secure_fr.set_url(secure_uri) # Make sure that we ignore errors during this test send_mutant = self._uri_opener.send_mutant kwargs = {'grep': False, 'ignore_errors': True} try: insecure_response = send_mutant(insecure_fr, **kwargs) secure_response = send_mutant(secure_fr, **kwargs) except (BaseFrameworkException, ScanMustStopException): # No vulnerability to report since one of these threw an error # (because there is nothing listening on that port). It makes # no sense to keep running since we already got an error self._run = False else: if insecure_response is None or secure_response is None: # No vulnerability to report since one of these threw an # error (because there is nothing listening on that port). # It makes no sense to keep running since we already got an # error self._run = False return if self._redirects_to_secure(insecure_response, secure_response): return if insecure_response.get_code() == secure_response.get_code()\ and relative_distance_boolean(insecure_response.get_body(), secure_response.get_body(), 0.95): desc = 'Secure content can be accessed using the insecure'\ ' protocol HTTP. The vulnerable URLs are:'\ ' "%s" - "%s" .' desc = desc % (secure_uri, insecure_uri) response_ids = [insecure_response.id, secure_response.id] v = Vuln.from_fr('Secure content over insecure channel', desc, severity.MEDIUM, response_ids, self.get_name(), freq) self.kb_append(self, 'un_ssl', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) # In most cases, when one resource is available, all are # so we just stop searching for this vulnerability self._run = False
def audit(self, freq, orig_response): """ Check if the protocol specified in freq is https and fetch the same URL using http. ie: - input: https://w3af.org/ - check: http://w3af.org/ :param freq: A FuzzableRequest """ if not self._run: return else: # Define some variables initial_uri = freq.get_uri() insecure_uri = initial_uri.copy() secure_uri = initial_uri.copy() insecure_uri.set_protocol('http') insecure_fr = copy.deepcopy(freq) insecure_fr.set_url(insecure_uri) secure_uri.set_protocol('https') secure_fr = copy.deepcopy(freq) secure_fr.set_url(secure_uri) # Make sure that we ignore errors during this test send_mutant = self._uri_opener.send_mutant kwargs = {'grep': False, 'ignore_errors': True} try: insecure_response = send_mutant(insecure_fr, **kwargs) secure_response = send_mutant(secure_fr, **kwargs) except (HTTPRequestException, ScanMustStopException): # No vulnerability to report since one of these threw an error # (because there is nothing listening on that port). It makes # no sense to keep running since we already got an error self._run = False else: if insecure_response is None or secure_response is None: # No vulnerability to report since one of these threw an # error (because there is nothing listening on that port). # It makes no sense to keep running since we already got an # error self._run = False return if self._redirects_to_secure(insecure_response, secure_response): return if insecure_response.get_code() == secure_response.get_code()\ and relative_distance_boolean(insecure_response.get_body(), secure_response.get_body(), 0.95): desc = 'Secure content can be accessed using the insecure'\ ' protocol HTTP. The vulnerable URLs are:'\ ' "%s" - "%s" .' desc = desc % (secure_uri, insecure_uri) response_ids = [insecure_response.id, secure_response.id] v = Vuln.from_fr('Secure content over insecure channel', desc, severity.MEDIUM, response_ids, self.get_name(), freq) self.kb_append(self, 'un_ssl', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) # In most cases, when one resource is available, all are # so we just stop searching for this vulnerability self._run = False
def _brute_worker(self, freq, user_field, passwd_field, login_failed_result_list, combination): """ :param freq: A FuzzableRequest :param combination: A tuple with (user, pass) or a pass if this is a password only form. """ if freq.get_url() not in self._found or not self._stop_on_first: freq = freq.copy() data_container = freq.get_dc() data_container = self._true_extra_fields(data_container, user_field, passwd_field) # Handle password-only forms! if user_field is not None: user, pwd = combination data_container[user_field][0] = user data_container[passwd_field][0] = pwd else: user = '******' pwd = combination data_container[passwd_field][0] = pwd freq.set_dc(data_container) try: resp = self._uri_opener.send_mutant(freq, cookies=False, grep=False) except ScanMustStopOnUrlError: return else: body = resp.get_body() body = body.replace(user, '').replace(pwd, '') if self._matches_failed_login(body, login_failed_result_list): return # Ok, this might be a valid combination. # Now test with a new invalid password to ensure our # previous possible found credentials are valid data_container[passwd_field][0] = rand_alnum(8) freq.set_dc(data_container) verif_resp = self._uri_opener.send_mutant(freq, cookies=False, grep=False) body = verif_resp.get_body() body = body.replace(user, '').replace(pwd, '') if self._matches_failed_login(body, login_failed_result_list): freq_url = freq.get_url() self._found.add(freq_url) if user_field is not None: desc = ('Found authentication credentials to: ' '"%s". A correct user and password combination' ' is: %s/%s' % (freq_url, user, pwd)) else: # There is no user field! desc = ('Found authentication credentials to: ' '"%s". The correct password is: "%s".' % (freq_url, pwd)) v = Vuln.from_fr('Guessable credentials', desc, severity.HIGH, resp.id, self.get_name(), freq) v['user'] = user v['pass'] = pwd v['response'] = resp kb.kb.append(self, 'auth', v) om.out.vulnerability(desc, severity=severity.HIGH) return