def _origin_echo(self, forged_req, url, origin, response, allow_origin, allow_credentials_str, allow_methods): """ First check if the @allow_origin is set to the value we sent (@origin) and if the allow_credentials is set to True. If this test is successful (most important vulnerability) then do not check for the @allow_origin is set to the value we sent. :return: A list of vulnerability objects with the identified vulns (if any). """ if allow_origin is None: return allow_origin = allow_origin.lower() allow_credentials = False if allow_credentials_str is not None: allow_credentials = 'true' in allow_credentials_str.lower() if origin not in allow_origin: return if allow_credentials: sev = severity.HIGH name = 'Insecure Access-Control-Allow-Origin with credentials' msg = ('The remote Web application, specifically "%s", returned' ' a "%s" header with the value set to the value sent in the' ' request\'s Origin header and a %s header with the value' ' set to "true", which is insecure and leaves the' ' application open to Cross-domain attacks which can' ' affect logged-in users.') msg = msg % (forged_req.get_url(), ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_ALLOW_CREDENTIALS) v = Vuln(name, msg, sev, response.get_id(), self.get_name()) v.set_url(forged_req.get_url()) v[DOMAIN] = forged_req.get_url().get_domain() self.kb_append_uniq_group(self, 'cors_origin', v, group_klass=OriginEchoWithCredsInfoSet) else: sev = severity.LOW name = 'Insecure Access-Control-Allow-Origin' msg = ('The remote Web application, specifically "%s", returned' ' a "%s" header with the value set to the value sent in the' ' request\'s Origin header, which is insecure and leaves' ' the application open to Cross-domain attacks.') msg = msg % (forged_req.get_url(), ACCESS_CONTROL_ALLOW_ORIGIN) v = Vuln(name, msg, sev, response.get_id(), self.get_name()) v.set_url(forged_req.get_url()) v[DOMAIN] = forged_req.get_url().get_domain() self.kb_append_uniq_group(self, 'cors_origin', v, group_klass=OriginEchoInfoSet)
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 grep(self, request, response): """ Plugin entry point, test existence of HTML forms containing password-type inputs. :param request: The HTTP request object. :param response: The HTTP response object :return: None, all results are saved in the kb. """ url = request.get_url() proto = url.get_protocol() url_string = url.url_string try: dp = parser_cache.dpc.get_document_parser_for(response) except BaseFrameworkException: # Failed to find a suitable parser for the document return for form in dp.get_forms(): for p in form.keys(): type = form._types.get(p) if type == 'password': action = form.get_action() action_proto = action.get_protocol() # form is to be submitted over http if action_proto == 'http': desc = 'The URL: "%s" contains a <form> tag' \ ' which submits credentials over HTTP' desc = desc % url_string v = Vuln('Insecure password submission over HTTP', desc, severity.MEDIUM, response.id, self.get_name()) v.set_url(response.get_url()) self.kb_append_uniq(self, 'form_cleartext_password', v) break else: # form was received over http if proto == 'http': desc = 'The URL: "%s" was delivered over the' \ ' insecure HTTP protocol and has <form>' \ ' which contains a password input' desc = desc % url_string v = Vuln('Insecure password form access over HTTP', desc, severity.MEDIUM, response.id, self.get_name()) self.kb_append_uniq(self, 'form_cleartext_password', v) break
def _classic_worker(self, gh, search_term): """ Perform the searches and store the results in the kb. """ google_list = self._google_se.get_n_results(search_term, 9) for result in google_list: # I found a vuln in the site! response = self._uri_opener.GET(result.URL, cache=True) if not is_404(response): desc = ('ghdb plugin found a vulnerability at URL: "%s".' ' According to GHDB the vulnerability description' ' is "%s".') desc %= (response.get_url(), gh.desc) v = Vuln('Google hack database match', desc, severity.MEDIUM, response.id, self.get_name()) v.set_url(response.get_url()) v.set_method('GET') kb.kb.append(self, 'vuln', v) om.out.vulnerability(v.get_desc(), severity=severity.LOW) # Create the fuzzable requests fr = FuzzableRequest(response.get_url()) self.output_queue.put(fr)
def _PROPFIND(self, domain_path): """ Test PROPFIND method """ content = "<?xml version='1.0'?>\r\n" content += "<a:propfind xmlns:a='DAV:'>\r\n" content += "<a:prop>\r\n" content += "<a:displayname:/>\r\n" content += "</a:prop>\r\n" content += "</a:propfind>\r\n" headers = copy.deepcopy(self.CONTENT_TYPE) headers['Depth'] = '1' res = self._uri_opener.PROPFIND(domain_path, data=content, headers=headers) if "D:href" in res and res.get_code() in xrange(200, 300): msg = 'Directory listing with HTTP PROPFIND method was found at' \ ' directory: "%s".' % domain_path v = Vuln('Insecure DAV configuration', msg, severity.MEDIUM, res.id, self.get_name()) v.set_url(res.get_url()) v.set_method('PROPFIND') self.kb_append(self, 'dav', 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 BaseFrameworkException, w3: msg = 'Failed to find a suitable document parser. ' \ 'Exception: ' + str(w3) om.out.debug(msg)
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 _send_and_check(self, repo_url, repo_get_files, repo, domain_path): """ Check if a repository index exists in the domain_path. :return: None, everything is saved to the self.out_queue. """ http_response = self.http_get_and_parse(repo_url) if not is_404(http_response): filenames = repo_get_files(http_response.get_body()) parsed_url_set = set() for filename in self._clean_filenames(filenames): test_url = domain_path.url_join(filename) if test_url not in self._analyzed_filenames: parsed_url_set.add(test_url) self._analyzed_filenames.add(filename) self.worker_pool.map(self.http_get_and_parse, parsed_url_set) if parsed_url_set: desc = 'A %s was found at: "%s"; this could indicate that'\ ' a %s is accessible. You might be able to download'\ ' the Web application source code.' desc = desc % (repo, http_response.get_url(), repo) v = Vuln('Source code repository', desc, severity.MEDIUM, http_response.id, self.get_name()) v.set_url(http_response.get_url()) kb.kb.append(self, repo, v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def end(self): # If all URLs implement protection, don't report anything. if not self._vuln_count: return response_ids = [_id for _id in self._ids] # If none of the URLs implement protection, simply report # ONE vulnerability that says that. if self._total_count == self._vuln_count: desc = 'The whole target has no protection (X-Frame-Options'\ ' header) against Click-Jacking attacks' # If most of the URLs implement the protection but some # don't, report ONE vulnerability saying: "Most are protected, # but x, y are not. if self._total_count > self._vuln_count: desc = 'Some URLs have no protection (X-Frame-Options header) '\ 'against Click-Jacking attacks. Among them:\n '\ ' '.join([str(url) + '\n' for url in self._vulns]) v = Vuln('Click-Jacking vulnerability', desc, severity.MEDIUM, response_ids, self.get_name()) self.kb_append(self, 'click_jacking', v) self._vulns.cleanup() self._ids.cleanup()
def end(self): """ This method is called when the plugin wont be used anymore. The real job of this plugin is done here, where I will try to see if one of the error_500 responses were not identified as a vuln by some of my audit plugins """ all_vuln_ids = set() for info in kb.kb.get_all_findings_iter(): for _id in info.get_id(): all_vuln_ids.add(_id) for request, error_500_response_id in self._error_500_responses: if error_500_response_id not in all_vuln_ids: # Found a error 500 that wasn't identified ! desc = ('An unidentified web application error (HTTP response' ' code 500) was found at: "%s". Enable all plugins and' ' try again, if the vulnerability still is not' ' identified, please verify manually and report it to' ' the w3af developers.') desc %= request.get_url() v = Vuln('Unhandled error in web application', desc, severity.MEDIUM, error_500_response_id, self.get_name()) v.set_uri(request.get_uri()) self.kb_append_uniq(self, 'error_500', v, 'VAR') self._error_500_responses.cleanup()
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 test_no_duplicate_vuln_reports(self): # The xml_file plugin had a bug where vulnerabilities were written to # disk multiple times, this test makes sure I fixed that vulnerability # First we create one vulnerability in the KB self.kb.cleanup() desc = 'Just a test for the XML file output plugin.' v = Vuln('SQL injection', desc, severity.HIGH, 1, 'sqli') self.kb.append('sqli', 'sqli', v) self.assertEqual(len(self.kb.get_all_vulns()), 1) # Setup the plugin plugin_instance = xml_file() # Set the output file for the unittest ol = OptionList() d = 'Output file name where to write the XML data' o = opt_factory('output_file', self.FILENAME, d, OUTPUT_FILE) ol.add(o) # Then we flush() twice to disk, this reproduced the issue plugin_instance.set_options(ol) plugin_instance.flush() plugin_instance.flush() plugin_instance.flush() # Now we parse the vulnerabilities from disk and confirm only one # is there file_vulns = self._from_xml_get_vulns(self.FILENAME) self.assertEqual(len(file_vulns), 1, file_vulns)
def grep(self, request, response): """ Plugin entry point, search for directory indexing. :param request: The HTTP request object. :param response: The HTTP response object :return: None """ if not response.is_text_or_html(): return if response.get_url().get_domain_path() in self._already_visited: return self._already_visited.add(response.get_url().get_domain_path()) html_string = response.get_body() if self._multi_in.query(html_string): desc = 'The URL: "%s" has a directory indexing vulnerability.' desc = desc % response.get_url() v = Vuln('Directory indexing', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) self.kb_append_uniq(self, 'directory', v, 'URL')
def _universal_allow(self, forged_req, url, origin, response, allow_origin, allow_credentials, allow_methods): """ Check if the allow_origin is set to *. :return: A list of vulnerability objects with the identified vulns (if any). """ if allow_origin == '*': msg = 'The remote Web application, specifically "%s", returned' \ ' an %s header with the value set to "*" which is insecure'\ ' and leaves the application open to Cross-domain attacks.' msg = msg % (forged_req.get_url(), ACCESS_CONTROL_ALLOW_ORIGIN) v = Vuln('Access-Control-Allow-Origin set to "*"', msg, severity.LOW, response.get_id(), self.get_name()) v.set_url(forged_req.get_url()) self.kb_append(self, 'cors_origin', v) return self._filter_report('_universal_allow_counter', 'universal allow-origin', severity.MEDIUM, [ v, ]) return []
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 write_vuln_to_kb(vulnty, url, funcs): vulndata = php_sca.KB_DATA[vulnty] for f in funcs: vuln_sev = vulndata['severity'] desc = name = vulndata['name'] v = Vuln(name, desc, vuln_sev, 1, 'PHP Static Code Analyzer') v.set_uri(url) v.set_token((f.vulnsources[0], 0)) args = list(vulndata['kb_key']) + [v] # TODO: Extract the method from the PHP code # $_GET == GET # $_POST == POST # $_REQUEST == GET v.set_method('GET') # TODO: Extract all the other variables that are # present in the PHP file using the SCA v.set_dc(DataContainer()) # # TODO: This needs to be checked! OS Commanding specific # attributes. v['os'] = 'unix' v['separator'] = '' kb.kb.append(*args)
def _check_if_exists(self, web_shell_url): """ Check if the file exists. :param web_shell_url: The URL to check """ try: response = self._uri_opener.GET(web_shell_url, cache=True) except BaseFrameworkException: om.out.debug('Failed to GET webshell:' + web_shell_url) else: signature = self._match_signature(response) if signature is None: return desc = (u'An HTTP response matching the web backdoor signature' u' "%s" was found at: "%s"; this could indicate that the' u' server has been compromised.') desc %= (signature, response.get_url()) # It's probability is higher if we found a long signature _severity = severity.HIGH if len( signature) > 8 else severity.MEDIUM v = Vuln(u'Potential web backdoor', desc, _severity, response.id, self.get_name()) v.set_url(response.get_url()) kb.kb.append(self, 'backdoors', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr)
def crawl(self, fuzzable_request): """ Plugin entry point, performs all the work. """ to_check = self._get_to_check(fuzzable_request.get_url()) # I found some URLs, create fuzzable requests pt_matches = self._is_in_phishtank(to_check) for ptm in pt_matches: fr = FuzzableRequest(ptm.url) self.output_queue.put(fr) # Only create the vuln object once if pt_matches: desc = 'The URL: "%s" seems to be involved in a phishing scam.' \ ' Please see %s for more info.' desc = desc % (ptm.url, ptm.more_info_url) v = Vuln('Phishing scam', desc, severity.MEDIUM, [], self.get_name()) v.set_url(ptm.url) kb.kb.append(self, 'phishtank', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def crawl(self, fuzzable_request, debugging_id): """ Plugin entry point, performs all the work. :param debugging_id: A unique identifier for this call to discover() :param fuzzable_request: A fuzzable_request instance that contains (among other things) the URL to test. """ to_check = self._get_to_check(fuzzable_request.get_url()) # I found some URLs, create fuzzable requests pt_matches = self._is_in_phishtank(to_check) if not pt_matches: return for ptm in pt_matches: fr = FuzzableRequest(ptm.url) self.output_queue.put(fr) desc = ('The URL: "%s" seems to be involved in a Phishing scam.' ' Please see %s for more info.') desc %= (ptm.url, ptm.more_info_url) v = Vuln('Phishing scam', desc, severity.MEDIUM, [], self.get_name()) v.set_url(ptm.url) kb.kb.append(self, 'phishtank', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def _check_if_exists(self, web_shell_url): """ Check if the file exists. :param web_shell_url: The URL to check """ try: response = self._uri_opener.GET(web_shell_url, cache=True) except BaseFrameworkException: om.out.debug('Failed to GET webshell:' + web_shell_url) else: if self._is_possible_backdoor(response): desc = 'A web backdoor was found at: "%s"; this could ' \ 'indicate that the server has been compromised.' desc = desc % response.get_url() v = Vuln('Potential web backdoor', desc, severity.HIGH, response.id, self.get_name()) v.set_url(response.get_url()) kb.kb.append(self, 'backdoors', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr)
def end(self): # If all URLs implement protection, don't report anything. if not self._vuln_count: return # If none of the URLs implement protection, simply report # ONE vulnerability that says that. if self._total_count == self._vuln_count: desc = 'The whole target web application has no protection (Pragma'\ ' and Cache-Control headers) against sensitive content'\ ' caching.' # If most of the URLs implement the protection but some # don't, report ONE vulnerability saying: "Most are protected, but x, y # are not. if self._total_count > self._vuln_count: desc = 'Some URLs have no protection (Pragma and Cache-Control'\ ' headers) against sensitive content caching. Among them:\n' desc += ' '.join([str(url) + '\n' for url in self._vulns]) response_ids = [_id for _id in self._ids] v = Vuln('Missing cache control for HTTPS content', desc, severity.LOW, response_ids, self.get_name()) self.kb_append_uniq(self, 'cache_control', v, 'URL') self._vulns.cleanup() self._ids.cleanup()
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 _check_methods(self, url): """ Perform some requests in order to check if we are able to retrieve some data with methods that may be wrongly enabled. """ allowed_methods = [] for method in ['GET', 'POST', 'ABCD', 'HEAD']: method_functor = getattr(self._uri_opener, method) try: response = apply(method_functor, (url, ), {}) code = response.get_code() except: pass else: if code not in self.BAD_METHODS: allowed_methods.append((method, response.id)) if len(allowed_methods) > 0: response_ids = [i for m, i in allowed_methods] methods = ', '.join([m for m, i in allowed_methods]) + '.' desc = 'The resource: "%s" requires authentication but the access'\ ' is misconfigured and can be bypassed using these'\ ' methods: %s.' desc = desc % (url, methods) v = Vuln('Misconfigured access control', desc, severity.MEDIUM, response_ids, self.get_name()) v.set_url(url) v['methods'] = allowed_methods self.kb_append(self, 'auth', v)
def _lowest_privilege_test(self, response): regex_str = 'User/Group </td><td class="v">(.*?)\((\d.*?)\)/(\d.*?)</td>' lowest_privilege_test = re.search(regex_str, response.get_body(), re.I) if lowest_privilege_test: lpt_uname = lowest_privilege_test.group(1) lpt_uid = lowest_privilege_test.group(2) lpt_uid = int(lpt_uid) lpt_gid = lowest_privilege_test.group(3) if lpt_uid < 99 or lpt_gid < 99 or \ re.match('root|apache|daemon|bin|operator|adm', lpt_uname, re.I): desc = 'phpinfo()::PHP may be executing as a higher privileged'\ ' group. Username: %s, UserID: %s, GroupID: %s.' desc = desc % (lpt_uname, lpt_uid, lpt_gid) v = Vuln('PHP lowest_privilege_test:fail', desc, severity.MEDIUM, response.id, self.get_name()) v.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) else: lpt_name = 'privilege:' + lpt_uname lpt_desc = 'phpinfo()::PHP is executing under ' lpt_desc += 'username: '******', ' lpt_desc += 'userID: ' + str(lpt_uid) + ', ' lpt_desc += 'groupID: ' + lpt_gid i = Info(lpt_name, lpt_desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc())
def end(self): """ This method is called when the plugin wont be used anymore. The real job of this plugin is done here, where I will try to see if one of the error_500 responses were not identified as a vuln by some of my audit plugins """ all_vulns = kb.kb.get_all_vulns() all_vulns_tuples = [(v.get_uri(), v.get_dc()) for v in all_vulns] for request, error_500_response_id in self._error_500_responses: if (request.get_uri(), request.get_dc()) not in all_vulns_tuples: # Found a err 500 that wasnt identified !!! desc = 'An unidentified web application error (HTTP response'\ ' code 500) was found at: "%s". Enable all plugins and'\ ' try again, if the vulnerability still is not identified'\ ', please verify manually and report it to the w3af'\ ' developers.' desc = desc % request.get_url() v = Vuln('Unhandled error in web application', desc, severity.MEDIUM, error_500_response_id, self.get_name()) v.set_uri(request.get_uri()) self.kb_append_uniq(self, 'error_500', v, 'VAR') self._error_500_responses.cleanup()
def disable_functions(response): regex_str = 'disable_functions</td><td class="v">(.*?)</td>' disable_functions_mo = re.search(regex_str, response.get_body(), re.I) if not disable_functions_mo: return secure_df = 8 df = disable_functions_mo.group(1) dfe = df.split(',') if len(dfe) >= secure_df: return desc = ('The phpinfo()::disable_functions does NOT seem to be set. This' ' configuration parameter is a good indicator of a security' '-enabled PHP installation. The disabled functions are: %s') desc %= (', '.join(dfe), ) v = Vuln('PHP disable_functions weakness', desc, severity.MEDIUM, response.id, 'phpinfo') v.set_url(response.get_url()) kb.kb.append('phpinfo', 'phpinfo', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def _SEARCH(self, domain_path): """ Test SEARCH method. """ content = "<?xml version='1.0'?>\r\n" content += "<g:searchrequest xmlns:g='DAV:'>\r\n" content += "<g:sql>\r\n" content += "Select 'DAV:displayname' from scope()\r\n" content += "</g:sql>\r\n" content += "</g:searchrequest>\r\n" res = self._uri_opener.SEARCH(domain_path, data=content, headers=self.CONTENT_TYPE) content_matches = '<a:response>' in res or '<a:status>' in res or \ 'xmlns:a="DAV:"' in res if content_matches and res.get_code() in xrange(200, 300): msg = 'Directory listing with HTTP SEARCH method was found at' \ 'directory: "%s".' % domain_path v = Vuln('Insecure DAV configuration', msg, severity.MEDIUM, res.id, self.get_name()) v.set_url(res.get_url()) v.set_method('SEARCH') self.kb_append(self, 'dav', v)
def upload_max_filesize(response): regex_str = 'upload_max_filesize</td><td class="v">(\d.*?)</td>' upload_max_filesize_mo = re.search(regex_str, response.get_body(), re.IGNORECASE) if not upload_max_filesize_mo: return secure_umf = 20 umf = upload_max_filesize_mo.group(1) + '' umf = umf.replace('M', '') umf = int(umf) if umf <= secure_umf: return desc = 'The phpinfo()::upload_max_filesize is set to a high value: %s' desc %= (upload_max_filesize_mo.group(1), ) v = Vuln('PHP upload_max_filesize:high', desc, severity.LOW, response.id, 'phpinfo') v.set_url(response.get_url()) kb.kb.append('phpinfo', 'phpinfo', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def discover(self, fuzzable_request, debugging_id): """ Detect CVE-2015-2080 aka. JetLeak :param debugging_id: A unique identifier for this call to discover() :param fuzzable_request: A fuzzable_request instance that contains (among other things) the URL to test. """ url = fuzzable_request.get_url() headers = Headers([('Referer', '\x00')]) response = self._uri_opener.GET(url, cache=False, grep=False, headers=headers) if response.get_code() != 400: return if 'Illegal character 0x0 in state' not in response.get_msg(): return desc = ('The application appears to be running a version of Jetty' ' vulnerable to CVE-2015-2080, which allows attackers to' ' read arbitrary server memory buffers') v = Vuln('JetLeak', desc, HIGH, response.id, self.get_name()) v.set_url(response.get_url()) self.kb_append_uniq(self, 'jetleak', 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.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')