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. ''' uri = response.get_uri() if response.is_text_or_html() and response.get_code() == 200 \ and response.get_clear_text_body() is not None \ and uri not in self._already_inspected: # Don't repeat URLs self._already_inspected.add(uri) found_ssn, validated_ssn = self._find_SSN( response.get_clear_text_body()) if validated_ssn: 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_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 _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 w3afException, w3: msg = 'Failed to find a suitable document parser. ' \ 'Exception: ' + str(w3) om.out.debug(msg)
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. ''' uri = response.get_uri() if response.is_text_or_html() and uri not in self._already_inspected: # Don't repeat URLs self._already_inspected.add(uri) for regex in self._regex_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.set_uri(uri) v['user'] = user v.add_to_highlight(user) self.kb_append_uniq(self, 'users', 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. """ uri = response.get_uri() if ( response.is_text_or_html() and response.get_code() == 200 and response.get_clear_text_body() is not None and uri not in self._already_inspected ): # Don't repeat URLs self._already_inspected.add(uri) found_ssn, validated_ssn = self._find_SSN(response.get_clear_text_body()) if validated_ssn: 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, 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 response.is_text_or_html() and \ response.get_url() not in self._already_added: 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') self._already_added.add(response.get_url()) else: self._first_404 = False desc = 'The URL: "%s" has a %s code disclosure'\ ' vulnerability in the customized 404 script.' desc = desc % (v.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 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(self, 'dom_xss', v)
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. :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 w3afException: 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)
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'\