def grep(self, request, response): """ Plugin entry point, find feeds. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ dom = response.get_dom() uri = response.get_uri() # In some strange cases, we fail to normalize the document if dom is None: return # Find all feed tags element_list = self._tag_xpath(dom) for element in element_list: feed_tag = element.tag feed_type = self._feed_types[feed_tag.lower()] version = element.attrib.get('version', 'unknown') fmt = 'The URL "%s" is a %s version %s feed.' desc = fmt % (uri, feed_type, version) i = Info('Content feed resource', desc, response.id, self.get_name()) i.set_uri(uri) i.add_to_highlight(feed_type) self.kb_append_uniq(self, 'feeds', i, 'URL')
def _log_info_to_kb(self): """ This method creates an Info object containing information about failed authentication attempts and stores it in the knowledge base. The information stored in the Info object is: * The log messages from self._log_messages * HTTP response IDs from self._htp_response_ids :return: None """ desc = ('The authentication plugin failed to get a valid application' ' session using the user-provided configuration settings.\n' '\n' 'The plugin generated the following log messages:\n' '\n') desc += '\n'.join(self._log_messages) i = Info('Authentication failure', desc, self._http_response_ids, self.get_name()) i.set_uri(self._get_main_authentication_url()) kb.kb.clear('authentication', 'error') kb.kb.append('authentication', 'error', i)
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. """ headers = response.get_headers() heaver_value, header_name = headers.iget('x-xss-protection', '') heaver_value = heaver_value.strip() if heaver_value == '0': desc = 'The remote web server sent the HTTP X-XSS-Protection'\ ' header with a 0 value, which disables Internet' \ ' Explorer\'s XSS filter. In most cases, this is a bad' \ ' practice and should be subject to review.' i = Info('Insecure X-XSS-Protection header usage', desc, response.id, self.get_name()) i.add_to_highlight('X-XSS-Protection') i.set_uri(response.get_uri()) self.kb_append_uniq_group(self, 'xss_protection_header', i, group_klass=XSSProtectionInfoSet)
def analyze_document_links(self, request, response): """ Find session IDs in the URI and store them in the KB. """ try: doc_parser = parser_cache.dpc.get_document_parser_for(response) except: pass else: parsed_refs, _ = doc_parser.get_references() for link_uri in parsed_refs: if self._has_sessid(link_uri) and \ response.get_url() not in self._already_reported: # report these informations only once self._already_reported.add(response.get_url()) desc = 'The HTML content at "%s" contains a link (%s)'\ ' which holds a session id. The ID could be leaked'\ ' to third party domains through the referrer'\ ' header.' desc = desc % (response.get_url(), link_uri) # append the info object to the KB. i = Info('Session ID in URL', desc, response.id, self.get_name()) i.set_uri(response.get_uri()) self.kb_append(self, 'url_session', i) break
def _log_info_to_kb(self, title, message, include_log_messages=True): """ This method creates an Info object containing information about failed authentication attempts and stores it in the knowledge base. The information stored in the Info object is: * The log messages from self._log_messages * HTTP response IDs from self._htp_response_ids :return: None """ desc = message if include_log_messages: log_messages = ' - ' + '\n - '.join(self._log_messages) args = (message, log_messages) msg_fmt = ( '%s\n' '\n' 'The following are the last log messages from the authentication plugin:\n' '\n' '%s') desc = msg_fmt % args i = Info(title, desc, self._http_response_ids, self.get_name()) i.set_uri(self._get_main_authentication_url()) kb.kb.append('authentication', 'error', i)
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 and \ (comment, response.get_url()) not in self._already_reported_interesting: # 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 = desc % (comment, response.get_url()) i = Info('HTML comment contains HTML code', desc, response.id, self.get_name()) i.set_dc(request.get_dc()) i.set_uri(response.get_uri()) i.add_to_highlight(html_in_comment.group(0)) kb.kb.append(self, 'html_comment_hides_html', i) om.out.information(i.get_desc()) self._already_reported_interesting.add( (comment, response.get_url()))
def grep(self, request, response): """ Plugin entry point, find feeds. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ uri = response.get_uri() for tag in mp_doc_parser.get_tags_by_filter(response, self.TAGS): feed_tag = tag.name feed_type = self._feed_types[feed_tag.lower()] version = tag.attrib.get('version', 'unknown') fmt = 'The URL "%s" is a %s version %s feed.' desc = fmt % (uri, feed_type, version) i = Info('Content feed resource', desc, response.id, self.get_name()) i.set_uri(uri) i.add_to_highlight(feed_type) self.kb_append_uniq(self, 'feeds', i, 'URL')
def grep(self, request, response): """ Plugin entry point, search for meta tags. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html() or is_404(response): return try: dp = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: return meta_tag_list = dp.get_meta_tags() for tag in meta_tag_list: for attr_name, attr_value in tag.items(): if not attr_name or not attr_value: # https://github.com/andresriancho/w3af/issues/2012 continue for word in self.INTERESTING_WORDS: # Check if we have something interesting and WHERE that # thing actually is if word in attr_name: where = ATTR_NAME content = attr_name elif word in attr_value: where = ATTR_VALUE content = attr_value else: # Go to the next one if nothing is found continue # Now... if we found something, report it =) desc = ('The URI: "%s" sent a <meta> tag with the attribute' ' %s set to "%s" which looks interesting.') desc %= (response.get_uri(), where, content) tag_name = self._find_tag_name(tag) usage = self.INTERESTING_WORDS.get(tag_name, None) if usage is not None: desc += ' The tag is used for %s.' % usage i = Info('Interesting META tag', desc, response.id, self.get_name()) i.set_uri(response.get_uri()) i.add_to_highlight(where, content) i[CONTENT] = content i[WHERE] = where self.kb_append_uniq_group(self, 'meta_tags', i, group_klass=MetaTagsInfoSet)
def grep(self, request, response): """ Plugin entry point, search for meta tags. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html() or is_404(response): return try: dp = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: return meta_tag_list = dp.get_meta_tags() for tag in meta_tag_list: for attr_name, attr_value in tag.items(): for word in self.INTERESTING_WORDS: # Check if we have something interesting and WHERE that # thing actually is where = content = None if word in attr_name: where = ATTR_NAME content = attr_name elif word in attr_value: where = ATTR_VALUE content = attr_value # Go to the next one if nothing is found if where is None: continue # Now... if we found something, report it =) desc = ( 'The URI: "%s" sent a <meta> tag with the attribute' ' %s set to "%s" which looks interesting.') desc %= (response.get_uri(), where, content) tag_name = self._find_tag_name(tag) usage = self.INTERESTING_WORDS.get(tag_name, None) if usage is not None: desc += ' The tag is used for %s.' % usage i = Info('Interesting META tag', desc, response.id, self.get_name()) i.set_uri(response.get_uri()) i.add_to_highlight(where, content) i[CONTENT] = content i[WHERE] = where self.kb_append_uniq_group(self, 'meta_tags', i, group_klass=MetaTagsInfoSet)
def grep(self, request, response): """ Plugin entry point, search for meta tags. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html() or is_404(response): return try: dp = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: return meta_tag_list = dp.get_meta_tags() for tag in meta_tag_list: for attr_name, attr_value in tag.items(): for word in self.INTERESTING_WORDS: # Check if we have something interesting # and WHERE that thing actually is where = content = None if word in attr_name: where = self.ATTR_NAME content = attr_name elif word in attr_value: where = self.ATTR_VALUE content = attr_value # Now... if we found something, report it =) if self._should_report(attr_name, attr_value, where): # The attribute is interesting! fmt = 'The URI: "%s" sent a <meta> tag with attribute'\ ' %s set to "%s" which looks interesting.' desc = fmt % (response.get_uri(), where, content) tag_name = self._find_name(tag) if self.INTERESTING_WORDS.get(tag_name, None): usage = self.INTERESTING_WORDS[tag_name] desc += ' The tag is used for %s.' % usage i = Info('Interesting META tag', desc, response.id, self.get_name()) i.set_uri(response.get_uri()) i.add_to_highlight(where, content) self.kb_append_uniq(self, 'meta_tags', i, 'URL')
def _save_to_kb(self, request, response, generator): desc = 'Found generator meta tag value: "%s"' % generator info = Info('Generator information', desc, response.id, self.get_name()) info.set_uri(response.get_uri()) info.add_to_highlight(generator) info[MetaTagsInfoSet.ITAG] = generator self.kb_append_uniq_group(self, 'content_generator', info, group_klass=MetaTagsInfoSet)
def grep(self, request, response): """ Plugin entry point, search for meta tags. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html() or is_404(response): return try: dp = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: return meta_tag_list = dp.get_meta_tags() for tag in meta_tag_list: tag_name = self._find_name(tag) for key, val in tag.items(): for word in self.INTERESTING_WORDS: # Check if we have something interesting # and WHERE that thing actually is where = content = None if word in key: where = "name" content = key elif word in val: where = "value" content = val # Now... if we found something, report it =) if where is not None: # The atribute is interesting! fmt = ( 'The URI: "%s" sent a <meta> tag with attribute' ' %s set to "%s" which looks interesting.' ) desc = fmt % (response.get_uri(), where, content) if self.INTERESTING_WORDS.get(tag_name, None): usage = self.INTERESTING_WORDS[tag_name] desc += " The tag is used for %s." % usage i = Info("Interesting META tag", desc, response.id, self.get_name()) i.set_uri(response.get_uri()) i.add_to_highlight(where, content) self.kb_append_uniq(self, "meta_tags", i, "URL")
def _analyze_strange(self, request, response, ref, token_name, token_value): if self._is_strange(request, token_name, token_value): desc = ('The URI: "%s" has a parameter named: "%s" with value:' ' "%s", which is very uncommon. and requires manual' ' verification.') desc %= (response.get_uri(), token_name, token_value) i = Info('Uncommon query string parameter', desc, response.id, self.get_name()) i['parameter_value'] = token_value i.add_to_highlight(token_value) i.set_uri(ref) self.kb_append(self, 'strange_parameters', i) return True return False
def analyze_uri(self, request, response): """ Find session IDs in the URI and store them in the KB. """ request_uri = request.get_uri() if self._has_sessid(request_uri) and \ response.get_url() not in self._already_reported: # report these informations only once self._already_reported.add(response.get_url()) desc = 'The URL "%s" contains a session id which could be'\ ' leaked to third party domains through the referrer'\ ' header.' desc = desc % request_uri # append the info object to the KB. i = Info('Session ID in URL', desc, response.id, self.get_name()) i.set_uri(response.get_uri()) self.kb_append(self, 'url_session', i)
def crawl(self, fuzzable_request): """ Find CAPTCHA images. :param fuzzable_request: A fuzzable_request instance that contains (among other things) the URL to test. """ result, captchas = self._identify_captchas(fuzzable_request) if result: for captcha in captchas: desc = 'Found a CAPTCHA image at: "%s".' % captcha.img_src response_ids = [response.id for response in captcha.http_responses] i = Info('Captcha image detected', desc, response_ids, self.get_name()) i.set_uri(captcha.img_src) kb.kb.append(self, 'CAPTCHA', i) om.out.information(i.get_desc())
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. """ # 501 Code is "Not Implemented" which in some cases responds with # this in the body: # <body><h2>HTTP/1.1 501 Not Implemented</h2></body> # Which creates a false positive. if response.get_code() == 501: return if not response.is_text_or_html(): return body_without_tags = response.get_clear_text_body() if body_without_tags is None: return uri = response.get_uri() for match, _, _, reqres in self._multi_re.query(body_without_tags): if reqres == 'REQUEST': desc = 'An HTTP request was found in the HTTP body of a response.' i = Info('HTTP Request in HTTP body', desc, response.id, self.get_name()) i.set_uri(uri) i.add_to_highlight(match.group(0)) kb.kb.append(self, 'request', i) if reqres == 'RESPONSE': desc = 'An HTTP response was found in the HTTP body of a response.' i = Info('HTTP Response in HTTP body', desc, response.id, self.get_name()) i.set_uri(uri) i.add_to_highlight(match.group(0)) kb.kb.append(self, 'response', i)
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. """ # 501 Code is "Not Implemented" which in some cases responds with # this in the body: # <body><h2>HTTP/1.1 501 Not Implemented</h2></body> # Which creates a false positive. if response.get_code() != 501\ and response.is_text_or_html(): body_without_tags = response.get_clear_text_body() if body_without_tags is None: return uri = response.get_uri() for match, _, _, reqres in self._multi_re.query(body_without_tags): if reqres == 'REQUEST': desc = 'An HTTP request was found in the HTTP body of'\ ' a response.' i = Info('HTTP Request in HTTP body', desc, response.id, self.get_name()) i.set_uri(uri) i.add_to_highlight(match.group(0)) kb.kb.append(self, 'request', i) if reqres == 'RESPONSE': desc = 'An HTTP response was found in the HTTP body of'\ ' a response.' i = Info('HTTP Response in HTTP body', desc, response.id, self.get_name()) i.set_uri(uri) i.add_to_highlight(match.group(0)) kb.kb.append(self, 'response', i)
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()) not in self._already_reported_interesting: desc = 'A comment with the string "%s" was found in: "%s".'\ ' This could be interesting.' desc = desc % (word, response.get_url()) i = Info('Interesting HTML comment', desc, response.id, self.get_name()) i.set_dc(request.get_dc()) i.set_uri(response.get_uri()) i.add_to_highlight(word) kb.kb.append(self, 'interesting_comments', i) om.out.information(i.get_desc()) self._already_reported_interesting.add((word, response.get_url()))
def _analyze_strange(self, request, response, ref, token_name, token_value): if not self._is_strange(request, token_name, token_value): return False if request.sent(token_value): return False desc = ('The URI: "%s" has a parameter named: "%s" with value:' ' "%s", which is very uncommon and requires manual' ' inspection.') args = (response.get_uri(), token_name, token_value) args = tuple(smart_str_ignore(i) for i in args) desc %= args i = Info('Uncommon query string parameter', desc, response.id, self.get_name()) i['parameter_value'] = token_value i.add_to_highlight(token_value) i.set_uri(ref) self.kb_append(self, 'strange_parameters', i) return True
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. """ headers = response.get_headers() heaver_value, header_name = headers.iget("x-xss-protection", "") heaver_value = heaver_value.strip() if heaver_value == "0": desc = ( "The remote web server sent the HTTP X-XSS-Protection" " header with a 0 value, which disables Internet" " Explorer's XSS filter. In most cases, this is a bad" " practice and should be subject to review." ) i = Info("Insecure X-XSS-Protection header usage", desc, response.id, self.get_name()) i.add_to_highlight("X-XSS-Protection") i.set_uri(response.get_uri()) self.kb_append_uniq_group(self, "xss_protection_header", i, group_klass=XSSProtectionInfoSet)
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)