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 audit(self, freq, orig_response): ''' Tests a URL 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): om.out.debug('Token for %s exists and was checked' % freq.get_url()) 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.HIGH, orig_response.id, self.get_name(), freq) self.kb_append_uniq(self, 'csrf', v)
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(): found_url = response.get_url( ).get_protocol() + '://' + 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(): found_url = response.get_url().get_protocol( ) + '://' + 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 _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 audit(self, freq, orig_response): ''' Verify xst vulns by sending a TRACE request and analyzing the response. ''' if not self._exec: # Do nothing return else: # 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 re.search(regex, 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): ''' Verify xst vulns by sending a TRACE request and analyzing the response. ''' if not self._exec: # Do nothing return else: # 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 _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 w3afMustStopOnUrlError: return else: body = resp.get_body() body = body.replace(user, '').replace(pwd, '') if not self._matches_failed_login(body, login_failed_result_list): # 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._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) send_mutant = self._uri_opener.send_mutant try: insecure_response = send_mutant(insecure_fr, grep=False) secure_response = send_mutant(secure_fr, grep=False) except w3afException: # 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 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