def _find_auth_uri(self, response): """ Analyze a 200 response and report any findings of http://user:[email protected]/ :return: None """ # # Analyze the HTTP URL # if ('@' in response.get_uri() and self._auth_uri_regex.match(response.get_uri().url_string)): # An authentication URI was found! desc = 'The resource: "%s" has a user and password in' \ ' the URI.' desc = desc % response.get_uri() v = Vuln('Basic HTTP credentials', desc, severity.HIGH, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(response.get_uri().url_string) kb.kb.append(self, 'userPassUri', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) # # Analyze the HTTP response body # url_list = [] try: DocumentParser = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException, w3: msg = 'Failed to find a suitable document parser. ' \ 'Exception: ' + str(w3) om.out.debug(msg)
def _analyze_param(self, request, response, parameter_name, parameter_value, language, serialized_object_re): """ Check if one parameter holds a serialized object :param request: The HTTP request which holds the parameter :param response: The HTTP response :param parameter_name: The name of the parameter :param parameter_value: The parameter value (might have been decoded from b64) :param language: The programming language :param serialized_object_re: The regular expression to match :return: None. We just save the vulnerability to the KB """ if not serialized_object_re.search(parameter_value): return # We found a match! The parameter value is a serialized object # Just report this to get the user's attention desc = ('Identified a %s serialized object being sent by the web' ' application in a request to "%s" in a parameter named "%s".' ' While this is not a vulnerability by itself, it is a strong' ' indicator of potential insecure deserialization issues.') desc %= (language, request.get_url(), parameter_name) v = Vuln('Serialized object', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(parameter_value) v[SerializedObjectInfoSet.ITAG] = parameter_name self.kb_append_uniq_group(self, 'serialized_object', v, group_klass=SerializedObjectInfoSet)
def _analyze_html(self, request, response): """ Search for IP addresses in the HTML """ if not response.is_text_or_html(): return # Performance improvement! if not (('10.' in response) or ('172.' in response) or ('192.168.' in response) or ('169.254.' in response)): return for regex in self._regex_list: for match in regex.findall(response.get_body()): match = match.strip() # Some proxy servers will return errors that include headers in the body # along with the client IP which we want to ignore if re.search("^.*X-Forwarded-For: .*%s" % match, response.get_body(), re.M): continue # If i'm requesting 192.168.2.111 then I don't want to be alerted about it if match not in self._ignore_if_match and \ not request.sent(match): desc = 'The URL: "%s" returned an HTML document'\ ' with a private IP address: "%s".' desc = desc % (response.get_url(), match) v = Vuln('Private IP disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v['IP'] = match v.add_to_highlight(match) self.kb_append(self, 'HTML', v)
def grep(self, request, response): """ Plugin entry point, find the SSN numbers. :param request: The HTTP request object. :param response: The HTTP response object :return: None. """ if not response.is_text_or_html() or response.get_code() != 200 \ or response.get_clear_text_body() is None: return found_ssn, validated_ssn = self._find_SSN( response.get_clear_text_body()) if validated_ssn: uri = response.get_uri() desc = 'The URL: "%s" possibly discloses a US Social Security' \ ' Number: "%s".' desc = desc % (uri, validated_ssn) v = Vuln('US Social Security Number disclosure', desc, severity.LOW, response.id, self.get_name()) v.set_uri(uri) v.add_to_highlight(found_ssn) self.kb_append_uniq(self, 'ssn', v, 'URL')
def _analyze_SQL(self, request, response, ref, token_name, token_value): """ To find this kind of vulns http://thedailywtf.com/Articles/Oklahoma- Leaks-Tens-of-Thousands-of-Social-Security-Numbers,-Other- Sensitive-Data.aspx :return: True if the parameter value contains SQL sentences """ for match in SQL_RE.findall(token_value): if request.sent(match): continue desc = ('The URI: "%s" has a parameter named: "%s" with value:' ' "%s", which is a SQL query.') desc %= (response.get_uri(), token_name, token_value) v = Vuln('Parameter has SQL sentence', desc, severity.LOW, response.id, self.get_name()) v['parameter_value'] = token_value v.add_to_highlight(token_value) v.set_uri(ref) self.kb_append(self, 'strange_parameters', v) return True return False
def grep(self, request, response): """ Plugin entry point, search for the credit cards. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if response.is_text_or_html() and response.get_code() == 200 \ and response.get_clear_text_body() is not None: found_cards = self._find_card(response.get_clear_text_body()) for card in found_cards: desc = 'The URL: "%s" discloses the credit card number: "%s"' desc = desc % (response.get_url(), card) v = Vuln('Credit card number disclosure', desc, severity.INFORMATION, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(card) om.out.vulnerability(v.get_desc(), severity=severity.INFORMATION) self.kb_append_uniq(self, 'credit_cards', v, 'URL')
def grep(self, request, response): """ Plugin entry point, find the SSN numbers. :param request: The HTTP request object. :param response: The HTTP response object :return: None. """ if not response.is_text_or_html() or response.get_code() != 200 \ or response.get_clear_text_body() is None: return found_ssn, validated_ssn = self._find_SSN(response.get_clear_text_body()) if validated_ssn: uri = response.get_uri() desc = 'The URL: "%s" possibly discloses a US Social Security'\ ' Number: "%s".' desc = desc % (uri, validated_ssn) v = Vuln('US Social Security Number disclosure', desc, severity.LOW, response.id, self.get_name()) v.set_uri(uri) v.add_to_highlight(found_ssn) self.kb_append_uniq(self, 'ssn', v, 'URL')
def grep(self, request, response): """ Plugin entry point. :param request: The HTTP request object. :param response: The HTTP response object :return: None, all results are saved in the kb. """ if not response.is_text_or_html(): return uri = response.get_uri() for regex in self.RE_LIST: for m in regex.findall(response.get_body()): user = m[0] desc = 'The URL: "%s" contains a SVN versioning signature'\ ' with the username "%s".' desc = desc % (uri, user) v = Vuln('SVN user disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.add_to_highlight(user) v.set_uri(uri) v[SVNUserInfoSet.ITAG] = user self.kb_append_uniq_group(self, 'users', v, group_klass=SVNUserInfoSet)
def grep(self, request, response): """ Plugin entry point, search for the credit cards. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return if not response.get_code() == 200: return clear_text_body = response.get_clear_text_body() if clear_text_body is None: return found_cards = self._find_card(clear_text_body) for card in found_cards: desc = u'The URL: "%s" discloses the credit card number: "%s"' desc %= (response.get_url(), card) v = Vuln('Credit card number disclosure', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(card) self.kb_append_uniq(self, 'credit_cards', v, 'URL')
def _analyze_headers(self, request, response): """ Search for IP addresses in HTTP headers """ # Get the headers string headers_string = response.dump_headers() # Match the regular expressions for regex in self._regex_list: for match in regex.findall(headers_string): # If i'm requesting 192.168.2.111 then I don't want to be # alerted about it if match not in self._ignore_if_match: desc = 'The URL: "%s" returned an HTTP header with a'\ ' private IP address: "%s".' desc = desc % (response.get_url(), match) v = Vuln('Private IP disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v['IP'] = match v.add_to_highlight(match) self.kb_append(self, 'header', v)
def grep(self, request, response): """ Plugin entry point. :param request: The HTTP request object. :param response: The HTTP response object :return: None, all results are saved in the kb. """ if not response.is_text_or_html(): return uri = response.get_uri() for regex in self.RE_LIST: for m in regex.findall(response.get_body()): user = m[0] desc = 'The URL: "%s" contains a SVN versioning signature' \ ' with the username "%s".' desc = desc % (uri, user) v = Vuln('SVN user disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.add_to_highlight(user) v.set_uri(uri) v[SVNUserInfoSet.ITAG] = user self.kb_append_uniq_group(self, 'users', v, group_klass=SVNUserInfoSet)
def _find_auth_uri(self, response): """ Analyze a 200 response and report any findings of http://user:[email protected]/ :return: None """ # # Analyze the HTTP URL # if self._url_has_auth(response.get_uri()): # An authentication URI was found! desc = ('The resource: "%s" has a user and password in' ' the URI.') desc %= response.get_uri() v = Vuln('Basic HTTP credentials', desc, severity.HIGH, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(response.get_uri().url_string) kb.kb.append(self, 'userPassUri', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) # # Analyze the HTTP response body # url_list = [] try: document_parser = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException as e: msg = 'Failed to find a suitable document parser. Exception: "%s"' om.out.debug(msg % e) else: parsed_references, re_references = document_parser.get_references() url_list.extend(parsed_references) url_list.extend(re_references) for url in url_list: if self._url_has_auth(url): desc = ('The resource: "%s" has a user and password in the' ' body. The offending URL is: "%s".') desc %= (response.get_url(), url) v = Vuln('Basic HTTP credentials', desc, severity.HIGH, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(url.url_string) kb.kb.append(self, 'userPassUri', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def _analyze_match(self, match, request, response): # This if is to avoid false positives if request.sent(match): return False if self._is_attr_value(match, response): return False # Decode the URL, this will transform things like # http://host.tld/?id=%2Fhome # into, # http://host.tld/?id=/home realurl = response.get_url().url_decode() # Check for dups if (realurl, match) in self._already_added: return False # There is a rare bug also, which is triggered in cases like this one: # # >>> import re # >>> re.findall('/var/www/.*','/var/www/foobar/htdocs/article.php') # ['/var/www/foobar/htdocs/article.php'] # >>> re.findall('/htdocs/.*','/var/www/foobar/htdocs/article.php') # ['/htdocs/article.php'] # >>> # # What I need to do here, is to keep the longest match. for realurl_added, match_added in self._already_added: if match_added.endswith(match): break else: # Note to self: I get here when "break" is NOT executed. # It's a new one, report! self._already_added.append((realurl, match)) desc = 'The URL: "%s" has a path disclosure'\ ' vulnerability which discloses "%s".' desc = desc % (response.get_url(), match) v = Vuln('Path disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(realurl) v['path'] = match v.add_to_highlight(match) self.kb_append(self, 'path_disclosure', v) return True return False
def grep(self, request, response): """ Plugin entry point, test existence of HTML auto-completable forms containing password-type inputs. Either form's <autocomplete> attribute is not present or is 'off'. :param request: The HTTP request object. :param response: The HTTP response object :return: None, all results are saved in the kb. """ if not response.is_text_or_html(): return try: doc_parser = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: return for form in doc_parser.get_forms(): # Only analyze forms which have autocomplete enabled at <form> if form.get_autocomplete() is False: continue for form_field_list in form.meta.itervalues(): for form_field in form_field_list: if form_field.input_type != INPUT_TYPE_PASSWD: continue if not form_field.autocomplete: continue url = response.get_url() desc = ('The URL: "%s" has a "<form>" element with ' 'auto-complete enabled.') desc %= url v = Vuln('Auto-completable form', desc, severity.INFORMATION, response.id, self.get_name()) v.add_to_highlight('autocomplete') v.set_url(url) #om.out.vulnerability(v.get_desc(), severity=severity.INFORMATION) self.kb_append_uniq(self, 'form_autocomplete', v, filter_by='URL') break
def _grep_worker(self, request, response, kb_key, domain=None): """ Helper method for using in self.grep() :param request: The HTTP request :param response: The HTTP response :param kb_key: Knowledge base dict key :param domain: Target domain for get_emails filter :return: None """ try: dp = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: msg = 'Failed to get document parser for "%s" at get_emails.' om.out.debug(msg % response.get_url()) return emails = set(dp.get_emails(domain)) for mail_address in emails: # Reduce false positives # if request.sent(mail_address): # continue # Email address are case insensitive mail_address = mail_address.lower() url = response.get_url() uniq_key = (mail_address, url) if uniq_key in self._already_reported: continue # Avoid dups self._already_reported.add(uniq_key) # Create a new info object, and report it desc = 'The mail account: "%s" was found at "%s".' desc = desc % (mail_address, url) v = Vuln('Email address disclosure', desc, severity.INFORMATION, response.id, self.get_name()) v.add_to_highlight(mail_address) v.set_url(url) v[EmailInfoSet.ITAG] = mail_address v['user'] = mail_address.split('@')[0] self.kb_append_uniq_group('emails', kb_key, v, group_klass=EmailInfoSet)
def grep(self, request, response): """ Plugin entry point, search for the code disclosures. Unit tests are available at plugins/grep/tests. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return response_is_404 = is_404(response) # This is a performance improvement to prevent the plugin from # applying contains_source_code to a 404 response that will be # discarded even if it matches if response_is_404 and not self._report_404_match: return match, lang = contains_source_code(response) if not match: return # Only report 404 findings once if response_is_404 and self._report_404_match: self._report_404_match = False desc = (u'The URL: "%s" has a %s code disclosure' u' vulnerability in the customized 404 script.') name = u'Code disclosure vulnerability in 404 page' else: desc = u'The URL: "%s" has a %s code disclosure vulnerability.' name = u'Code disclosure vulnerability' # Report the vulnerability desc %= (response.get_url(), ' or '.join(list(lang))) v = Vuln(name, desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(match.group()) self.kb_append_uniq(self, 'code_disclosure', v, 'URL')
def _analyze_html(self, request, response): """ Search for IP addresses in the HTML """ if not response.is_text_or_html(): return # Performance improvement! if not (('10.' in response) or ('172.' in response) or ('192.168.' in response) or ('169.254.' in response)): return for regex in self.RE_LIST: for ip_address in regex.findall(response.get_body()): ip_address = ip_address.strip() # Some proxy servers will return errors that include headers # in the body along with the client IP which we want to ignore if re.search("^.*X-Forwarded-For: .*%s" % ip_address, response.get_body(), re.M): continue # If i'm requesting 192.168.2.111 then I don't want to be # alerted about it if ip_address in self._ignore_if_match: continue # Don't match things I've sent if request.sent(ip_address): continue desc = 'The URL: "%s" returned an HTML document which' \ ' contains the private IP address: "%s".' desc = desc % (response.get_url(), ip_address) v = Vuln('Private IP disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(ip_address) v[HTMLPrivateIPInfoSet.ITAG] = ip_address self.kb_append_uniq_group(self, 'HTML', v, group_klass=HTMLPrivateIPInfoSet)
def _check_x_power(self, fuzzable_request): """ Analyze X-Powered-By header. """ response = self._uri_opener.GET(fuzzable_request.get_url(), cache=True) for header_name in response.get_headers().keys(): for i in ['ASPNET', 'POWERED']: if i in header_name.upper() or header_name.upper() in i: powered_by = response.get_headers()[header_name] # Only get the first one self._x_powered = False # # Check if I already have this info in the KB # pow_by_kb = kb.kb.get('server_header', 'powered_by') powered_by_in_kb = [j['powered_by'] for j in pow_by_kb] if powered_by not in powered_by_in_kb: # # I don't have it in the KB, so I need to add it, # desc = 'The %s header for the target HTTP server is "%s".' desc = desc % (header_name, powered_by) v = Vuln('Powered-by header', desc, severity.INFORMATION, response.id, self.get_name()) v['powered_by'] = powered_by v.add_to_highlight(header_name + ':') # Save the results in the KB so that other plugins can # use this information. Before knowing that some servers # may return more than one poweredby header I had: # kb.kb.raw_write( self , 'powered_by' , powered_by ) # But I have seen an IIS server with PHP that returns # both the ASP.NET and the PHP headers kb.kb.append(self, 'powered_by', v) # Update the list and save it, powered_by_in_kb.append(powered_by) kb.kb.raw_write(self, 'powered_by_string', powered_by_in_kb)
def grep(self, request, response): """ Plugin entry point, search for the code disclosures. Unit tests are available at plugins/grep/tests. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return # https://github.com/andresriancho/w3af/issues/5379 # Avoid some (rather common) false positives that appear in JS files if 'javascript' in response.content_type: return match, lang = is_source_file(response.get_body()) if not match: return # Only report 404 findings once if is_404(response) and self._report_404_match: self._report_404_match = False desc = u'The URL: "%s" has a %s code disclosure' \ u' vulnerability in the customized 404 script.' name = u'Code disclosure vulnerability in 404 page' else: desc = u'The URL: "%s" has a %s code disclosure vulnerability.' name = u'Code disclosure vulnerability' # Report the vulnerability desc %= (response.get_url(), lang) v = Vuln(name, desc, severity.INFORMATION, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(match.group()) om.out.vulnerability(v.get_desc(), severity=severity.INFORMATION) self.kb_append_uniq(self, 'code_disclosure', v, 'URL')
def grep(self, request, response): """ Plugin entry point, search for the code disclosures. Unit tests are available at plugins/grep/tests. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return # https://github.com/andresriancho/w3af/issues/5379 # Avoid some (rather common) false positives that appear in JS files if 'javascript' in response.content_type: return match, lang = is_source_file(response.get_body()) if not match: return # Only report 404 findings once if is_404(response) and self._report_404_match: self._report_404_match = False desc = u'The URL: "%s" has a %s code disclosure' \ u' vulnerability in the customized 404 script.' name = u'Code disclosure vulnerability in 404 page' else: desc = u'The URL: "%s" has a %s code disclosure vulnerability.' name = u'Code disclosure vulnerability' # Report the vulnerability desc %= (response.get_url(), lang) v = Vuln(name, desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(match.group()) self.kb_append_uniq(self, 'code_disclosure', v, 'URL')
def grep(self, request, response): """ Plugin entry point, search for the code disclosures. Unit tests are available at plugins/grep/tests. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return match, lang = is_source_file(response.get_body()) if match: # Check also for 404 if not is_404(response): desc = 'The URL: "%s" has a %s code disclosure vulnerability.' desc = desc % (response.get_url(), lang) v = Vuln('Code disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(match.group()) self.kb_append_uniq(self, 'code_disclosure', v, 'URL') else: self._first_404 = False desc = 'The URL: "%s" has a %s code disclosure'\ ' vulnerability in the customized 404 script.' desc = desc % (response.get_url(), lang) v = Vuln('Code disclosure vulnerability in 404 page', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(match.group()) self.kb_append_uniq(self, 'code_disclosure', v, 'URL')
def _analyze_401(self, response): """ Analyze a 401 response and report it. :return: None """ realm = self._get_realm(response) if realm is None: self._report_no_realm(response) return insecure = response.get_url().get_protocol() == 'http' vuln_severity = severity.HIGH if insecure else severity.LOW desc = 'The resource: "%s" requires HTTP authentication' if insecure: desc += ' over a non-encrypted channel, which allows'\ ' potential intruders to sniff traffic and capture'\ ' valid credentials.' else: desc += '.' desc += ' The received authentication realm is: "%s".' desc = desc % (response.get_url(), realm) # Report the common case, were a realm is set. if 'ntlm' in realm.lower(): v = Vuln('NTLM authentication', desc, vuln_severity, response.id, self.get_name()) else: v = Vuln('HTTP Basic authentication', desc, vuln_severity, response.id, self.get_name()) v.set_url(response.get_url()) v['message'] = realm v.add_to_highlight(realm) kb.kb.append(self, 'auth', v) om.out.information(v.get_desc())
def _analyze_401(self, response): """ Analyze a 401 response and report it. :return: None """ realm = self._get_realm(response) if realm is None: self._report_no_realm(response) return insecure = response.get_url().get_protocol() == 'http' vuln_severity = severity.HIGH if insecure else severity.LOW desc = 'The resource: "%s" requires HTTP authentication' if insecure: desc += ' over a non-encrypted channel, which allows' \ ' potential intruders to sniff traffic and capture' \ ' valid credentials.' else: desc += '.' desc += ' The received authentication realm is: "%s".' desc = desc % (response.get_url(), realm) # Report the common case, were a realm is set. if 'ntlm' in realm.lower(): v = Vuln('NTLM authentication', desc, vuln_severity, response.id, self.get_name()) else: v = Vuln('HTTP Basic authentication', desc, vuln_severity, response.id, self.get_name()) v.set_url(response.get_url()) v['message'] = realm v.add_to_highlight(realm) kb.kb.append(self, 'auth', v) om.out.information(v.get_desc())
def grep(self, request, response): """ Plugin entry point, search for the credit cards. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if response.is_text_or_html() and response.get_code() == 200 and response.get_clear_text_body() is not None: found_cards = self._find_card(response.get_clear_text_body()) for card in found_cards: desc = 'The URL: "%s" discloses the credit card number: "%s"' desc = desc % (response.get_url(), card) v = Vuln("Credit card number disclosure", desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(card) self.kb_append_uniq(self, "credit_cards", v, "URL")
def grep(self, request, response): """ Plugin entry point, search for the DOM XSS vulns. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return for vuln_code in self._smart_grep(response): desc = 'The URL: "%s" has a DOM XSS (insecure javascript code)'\ ' bug using: "%s".' desc = desc % (response.get_url(), vuln_code) v = Vuln('DOM Cross site scripting', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(vuln_code) self.kb_append_uniq(self, 'dom_xss', v, filter_by='URL')
def grep(self, request, response): """ Plugin entry point, search for the DOM XSS vulns. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return for vuln_code in self._smart_grep(response): desc = 'The URL: "%s" has a DOM XSS (insecure javascript code)' \ ' bug using: "%s".' desc = desc % (response.get_url(), vuln_code) v = Vuln('DOM Cross site scripting', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(vuln_code) self.kb_append_uniq(self, 'dom_xss', v, filter_by='URL')
def _analyze_headers(self, request, response): """ Search for IP addresses in HTTP headers """ # Get the headers string headers_string = response.dump_headers() # Match the regular expressions for regex in self.RE_LIST: for ip_address in regex.findall(headers_string): ip_address = ip_address.strip() # If i'm requesting 192.168.2.111 then I don't want to be # alerted about it if ip_address in self._ignore_if_match: continue # I want to know the header name, this shouldn't consume much # CPU since we're only doing it when the headers already match # the initial regex run header_name = self._get_header_name(response, ip_address, regex) desc = 'The URL "%s" returned the private IP address: "%s"'\ ' in the HTTP response header "%s"' desc = desc % (response.get_url(), ip_address, header_name) v = Vuln('Private IP disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(ip_address) v['ip_address'] = ip_address v['header_name'] = header_name v[HeaderPrivateIPInfoSet.ITAG] = (ip_address, header_name) self.kb_append_uniq_group(self, 'header', v, group_klass=HeaderPrivateIPInfoSet)
def _check_server_header(self, fuzzable_request): """ HTTP GET and analyze response for server header """ response = self._uri_opener.GET(fuzzable_request.get_url(), cache=True) for hname, hvalue in response.get_lower_case_headers().iteritems(): if hname == 'server': server = hvalue desc = 'The server header for the remote web server is: "%s".' desc = desc % server v = Vuln('Server header', desc, severity.INFORMATION, response.id, self.get_name()) v['server'] = server v.add_to_highlight(hname + ':') # Save the results in the KB so the user can look at it kb.kb.append(self, 'server', v) # Also save this for easy internal use # other plugins can use this information kb.kb.raw_write(self, 'server_string', server) break else: # strange ! desc = 'The remote HTTP Server omitted the "server" header in'\ ' its response.' v = Vuln('Omitted server header', desc, severity.INFORMATION, response.id, self.get_name()) # Save the results in the KB so that other plugins can use this # information kb.kb.append(self, 'ommited_server_header', v) # Also save this for easy internal use # other plugins can use this information kb.kb.raw_write(self, 'server_string', '')
def find_path_disclosure(self, request, response): """ Actually find the path disclosure vulnerabilities """ match_list = [] body_text = response.get_body() real_url = response.get_url().url_decode() for match, _, _ in self._signature_re.query(body_text): match_list.append(match.group(1)) # Sort by the longest match, this is needed for filtering out # some false positives please read the note below. match_list.sort(longest_cmp) for match in match_list: # Avoid duplicated reports if (real_url, match) in self._reported: continue # Remove false positives if self._is_false_positive(match, request, response): continue # Found! self._reported.append((real_url, match)) desc = ('The URL: "%s" has a path disclosure vulnerability which' ' discloses "%s".') desc %= (response.get_url(), match) v = Vuln('Path disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.add_to_highlight(match) v.set_url(real_url) v['path'] = match self.kb_append(self, 'path_disclosure', v) return v
def find_path_disclosure(self, request, response): """ Actually find the path disclosure vulnerabilities """ body_text = response.get_body() match_list = [] for match, _, _ in self._signature_re.query(body_text): match_list.append(match.group(1)) # Sort by the longest match, this is needed for filtering out # some false positives please read the note below. match_list.sort(longest_cmp) real_url = response.get_url().url_decode() for match in match_list: # Avoid duplicated reports if (real_url, match) in self._reported: continue # Remove false positives if self._is_false_positive(match, request, response): continue # Found! self._reported.append((real_url, match)) desc = ('The URL: "%s" has a path disclosure vulnerability which' ' discloses "%s".') desc %= (response.get_url(), match) v = Vuln('Path disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.add_to_highlight(match) v.set_url(real_url) v['path'] = match self.kb_append(self, 'path_disclosure', v) return v
def find_path_disclosure(self, request, response): """ Actually find the path disclosure vulnerabilities """ html_string = response.get_body() for potential_disclosure in self._potential_disclosures(html_string): path_disc_regex = self._compiled_regexes[potential_disclosure] match_list = path_disc_regex.findall(html_string) # Sort by the longest match, this is needed for filtering out # some false positives please read the note below. match_list.sort(longest_cmp) real_url = response.get_url().url_decode() for match in match_list: # Avoid duplicated reports if (real_url, match) in self._reported: continue # Remove false positives if not self._is_false_positive(match, request, response): self._reported.append((real_url, match)) desc = 'The URL: "%s" has a path disclosure'\ ' vulnerability which discloses "%s".' desc = desc % (response.get_url(), match) v = Vuln('Path disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(real_url) v['path'] = match v.add_to_highlight(match) self.kb_append(self, 'path_disclosure', v) return v
def grep(self, request, response): """ Plugin entry point. :param request: The HTTP request object. :param response: The HTTP response object :return: None, all results are saved in the kb. """ try: dp = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: return # Note: # - With parsed_references I'm 100% that it's really something in the # HTML that the developer intended to add. # # - The re_references are the result of regular expressions, which in # some cases are just false positives. # parsed_references, _ = dp.get_references() for ref in parsed_references: qs = ref.querystring for param_name in qs: # This for loop is to address the repeated parameter name issue for element_index in xrange(len(qs[param_name])): if self._is_strange(request, param_name, qs[param_name][element_index])\ and (ref.uri2url(), param_name) not in self._already_reported: # Don't repeat findings self._already_reported.add((ref.uri2url(), param_name)) desc = 'The URI: "%s" has a parameter named: "%s"'\ ' with value: "%s", which is very uncommon.'\ ' and requires manual verification.' desc = desc % (response.get_uri(), param_name, qs[param_name][element_index]) i = Info('Uncommon query string parameter', desc, response.id, self.get_name()) i.set_uri(ref) i.set_var(param_name) i['parameter_value'] = qs[param_name][element_index] i.add_to_highlight(qs[param_name][element_index]) self.kb_append(self, 'strange_parameters', i) # To find this kind of vulns # http://thedailywtf.com/Articles/Oklahoma- # Leaks-Tens-of-Thousands-of-Social-Security-Numbers,-Other- # Sensitive-Data.aspx if self._is_SQL(request, param_name, qs[param_name][element_index])\ and ref not in self._already_reported: # Don't repeat findings self._already_reported.add(ref) desc = 'The URI: "%s" has a parameter named: "%s"'\ ' with value: "%s", which is a SQL query.' desc = desc % (response.get_uri(), param_name, qs[param_name][element_index]) v = Vuln('Parameter has SQL sentence', desc, severity.LOW, response.id, self.get_name()) v.set_uri(ref) v.set_var(param_name) v['parameter_value'] = qs[param_name][element_index] v.add_to_highlight(qs[param_name][element_index]) self.kb_append(self, 'strange_parameters', v)
url_list.extend(parsed_references) url_list.extend(re_references) for url in url_list: if ('@' in url.url_string and self._auth_uri_regex.match(url.url_string)): desc = 'The resource: "%s" has a user and password in the' \ ' body. The offending URL is: "%s".' desc = desc % (response.get_url(), url) v = Vuln('Basic HTTP credentials', desc, severity.HIGH, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(url.url_string) kb.kb.append(self, 'userPassUri', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) def _get_realm(self, response): for key in response.get_headers(): if key.lower() == 'www-authenticate': realm = response.get_headers()[key] return realm return None def _report_no_realm(self, response): # Report this strange case desc = 'The resource: "%s" requires authentication (HTTP Code' \
def find_path_disclosure(self, request, response): """ Actually find the path disclosure vulnerabilities """ html_string = response.get_body() for potential_disclosure in self._potential_disclosures(html_string): path_disc_regex = self._compiled_regexes[potential_disclosure] match_list = path_disc_regex.findall(html_string) # Decode the URL, this will transform things like # http://host.tld/?id=%2Fhome # into, # http://host.tld/?id=/home realurl = response.get_url().url_decode() # Sort by the longest match, this is needed for filtering out # some false positives please read the note below. match_list.sort(self._longest) for match in match_list: # This if is to avoid false positives if not request.sent(match) and not \ self._attr_value(match, html_string): # Check for dups if (realurl, match) in self._already_added: continue # There is a rare bug also, which is triggered in cases like this one: # # >>> import re # >>> re.findall('/var/www/.*','/var/www/foobar/htdocs/article.php') # ['/var/www/foobar/htdocs/article.php'] # >>> re.findall('/htdocs/.*','/var/www/foobar/htdocs/article.php') # ['/htdocs/article.php'] # >>> # # What I need to do here, is to keep the longest match. for realurl_added, match_added in self._already_added: if match_added.endswith(match): break else: # Note to self: I get here when "break" is NOT executed. # It's a new one, report! self._already_added.append((realurl, match)) desc = 'The URL: "%s" has a path disclosure'\ ' vulnerability which discloses "%s".' desc = desc % (response.get_url(), match) v = Vuln('Path disclosure vulnerability', desc, severity.LOW, response.id, self.get_name()) v.set_url(realurl) v['path'] = match v.add_to_highlight(match) self.kb_append(self, 'path_disclosure', v) return True return False
url_list.extend(re_references) for url in url_list: if ('@' in url.url_string and self._auth_uri_regex.match(url.url_string)): desc = 'The resource: "%s" has a user and password in the'\ ' body. The offending URL is: "%s".' desc = desc % (response.get_url(), url) v = Vuln('Basic HTTP credentials', desc, severity.HIGH, response.id, self.get_name()) v.set_url(response.get_url()) v.add_to_highlight(url.url_string) kb.kb.append(self, 'userPassUri', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) def _get_realm(self, response): for key in response.get_headers(): if key.lower() == 'www-authenticate': realm = response.get_headers()[key] return realm return None def _report_no_realm(self, response): # Report this strange case desc = 'The resource: "%s" requires authentication (HTTP Code'\