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 _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 _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 crawl(self, fuzzable_request): """ Plugin entry point, perform all the work. """ to_check = self._get_to_check(fuzzable_request.get_url()) # I found some URLs, create fuzzable requests phishtank_matches = self._is_in_phishtank(to_check) for ptm in phishtank_matches: response = self._uri_opener.GET(ptm.url) for fr in self._create_fuzzable_requests(response): self.output_queue.put(fr) # Only create the vuln object once if phishtank_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, response.id, 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: 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 _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_crossdomain_clientaccesspolicy(self, url, response, file_name): # https://github.com/andresriancho/w3af/issues/14491 if file_name not in self.FILE_TAG_ATTR: return try: dom = xml.dom.minidom.parseString(response.get_body()) except Exception: # Report this, it may be interesting for the final user # not a vulnerability per-se... but... it's information after all if 'allow-access-from' in response.get_body() or \ 'cross-domain-policy' in response.get_body() or \ 'cross-domain-access' in response.get_body(): desc = 'The "%s" file at: "%s" is not a valid XML.' desc %= (file_name, response.get_url()) i = Info('Invalid RIA settings file', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'info', i) om.out.information(i.get_desc()) return tag, attribute = self.FILE_TAG_ATTR.get(file_name) url_list = dom.getElementsByTagName(tag) for url in url_list: url = url.getAttribute(attribute) if url == '*': desc = 'The "%s" file at "%s" allows flash/silverlight'\ ' access from any site.' desc %= (file_name, response.get_url()) v = Vuln('Insecure RIA settings', desc, severity.LOW, 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=v.get_severity()) else: desc = 'The "%s" file at "%s" allows flash/silverlight'\ ' access from "%s".' desc %= (file_name, response.get_url(), url) i = Info('Cross-domain allow ACL', desc, response.id, self.get_name()) i.set_url(response.get_url()) i.set_method('GET') kb.kb.append(self, 'info', i) om.out.information(i.get_desc())
def _analyze_crossdomain_clientaccesspolicy(self, url, response, file_name): try: dom = xml.dom.minidom.parseString(response.get_body()) except Exception: # Report this, it may be interesting for the final user # not a vulnerability per-se... but... it's information after all if 'allow-access-from' in response.get_body() or \ 'cross-domain-policy' in response.get_body() or \ 'cross-domain-access' in response.get_body(): desc = 'The "%s" file at: "%s" is not a valid XML.' desc = desc % (file_name, response.get_url()) i = Info('Invalid RIA settings file', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'info', i) om.out.information(i.get_desc()) else: if file_name == 'crossdomain.xml': url_list = dom.getElementsByTagName("allow-access-from") attribute = 'domain' if file_name == 'clientaccesspolicy.xml': url_list = dom.getElementsByTagName("domain") attribute = 'uri' for url in url_list: url = url.getAttribute(attribute) if url == '*': desc = 'The "%s" file at "%s" allows flash/silverlight'\ ' access from any site.' desc = desc % (file_name, response.get_url()) v = Vuln('Insecure RIA settings', desc, severity.LOW, 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=v.get_severity()) else: desc = 'The "%s" file at "%s" allows flash/silverlight'\ ' access from "%s".' desc = desc % (file_name, response.get_url(), url) i = Info('Cross-domain allow ACL', desc, response.id, self.get_name()) i.set_url(response.get_url()) i.set_method('GET') kb.kb.append(self, 'info', i) om.out.information(i.get_desc())
def _display_errors(self, response): regex_str = 'display_errors</td><td class="v">(On|<i>no value</i>)</td>' display_errors = re.search(regex_str, response.get_body(), re.I) if display_errors: desc = 'The phpinfo()::display_errors is enabled.' v = Vuln('PHP display_errors: On', 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())
def _expose_php(self, response): regex_str = 'expose_php</td><td class="v">(On|<i>no value</i>)</td>' expose_php = re.search(regex_str, response.get_body(), re.I) if expose_php: desc = 'The phpinfo()::expose_php is enabled.' v = Vuln('PHP expose_php: On', 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())
def _session_cookie_httponly(self, response): regex_str = 'session\.cookie_httponly</td><td class="v">(Off|no|0)</td>' session_cookie_httponly = re.search(regex_str, response.get_body(), re.I) if session_cookie_httponly: desc = 'The phpinfo()::session.cookie_httponly is off.' v = Vuln('PHP session.cookie_httponly: Off', 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())
def _allow_url_include(self, response): regex_str = 'allow_url_include</td><td class="v">(On|<i>no value</i>)</td>' allow_url_include = re.search(regex_str, response.get_body(), re.I) if allow_url_include: desc = 'The phpinfo()::allow_url_include is enabled.' v = Vuln('PHP allow_url_include: On', 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())
def _session_save_path(self, response): regex_str = 'session\.save_path</td><td class="v">(<i>no value</i>)</td>' session_save_path = re.search(regex_str, response.get_body(), re.I) if session_save_path: desc = 'The phpinfo()::session.save_path may be set to world-'\ 'accessible directory.' v = Vuln('PHP session_save_path:Everyone', desc, severity.LOW, 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())
def _session_use_trans(self, response): regex_str = 'session\.use_trans</td><td class="v">(On)</td>' session_use_trans = re.search(regex_str, response.get_body(), re.I) if session_use_trans: desc = 'The phpinfo()::session.use_trans is enabled. This makes'\ ' session hijacking easier.' v = Vuln('PHP session_use_trans: On', 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())
def _upload_tmp_dir(self, response): regex_str = 'upload_tmp_dir</td><td class="v">(<i>no value</i>)</td>' upload_tmp_dir = re.search(regex_str, response.get_body(), re.I) if upload_tmp_dir: desc = 'The phpinfo()::upload_tmp_dir may be set to world-'\ 'accessible directory.' v = Vuln('PHP upload_tmp_dir:Everyone', desc, severity.LOW, 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())
def _cgi_force_redirect(self, response): regex_str = 'cgi_force_redirect</td><td class="v">(.*?)</td>' cgi_force_redirect = re.search(regex_str, response.get_body(), re.I) if cgi_force_redirect: utd = cgi_force_redirect.group(1) + '' if utd != 'On': desc = 'The phpinfo()::CGI::force_redirect is disabled.' v = Vuln('PHP cgi_force_redirect: Off', 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())
def _default_charset(self, response): regex_str = 'default_charset</td><td class="v">(Off|no|0)</td>' default_charset = re.search(regex_str, response.get_body(), re.I) if default_charset: desc = 'The phpinfo()::default_charset is set to none. This'\ ' makes PHP scripts vulnerable to variable charset'\ ' encoding XSS.' v = Vuln('PHP default_charset: Off', 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())
def _brute_worker(self, url, combination): """ Try a user/password combination with HTTP basic authentication against a specific URL. :param url: A string representation of an URL :param combination: A tuple that contains (user,pass) """ # Remember that this worker is called from a thread which lives in a # threadpool. If the worker finds something, it has to let the rest know # and the way we do that is by setting self._found. # # If one thread sees that we already bruteforced the access, the rest # will simply no-op if not self._found or not self._stop_on_first: user, passwd = combination raw_values = "%s:%s" % (user, passwd) auth = 'Basic %s' % base64.b64encode(raw_values).strip() headers = Headers([('Authorization', auth)]) fr = FuzzableRequest(url, headers=headers, method='GET') try: response = self._uri_opener.send_mutant(fr, cache=False, grep=False) except BaseFrameworkException, w3: msg = 'Exception while brute-forcing basic authentication,'\ ' error message: "%s".' om.out.debug(msg % w3) else: # GET was OK if response.get_code() != 401: self._found = True desc = 'Found authentication credentials to: "%s".'\ ' A valid user and password combination is: %s/%s .' desc = desc % (url, user, passwd) v = Vuln('Guessable credentials', desc, severity.HIGH, response.id, self.get_name()) v.set_url(url) v['user'] = user v['pass'] = passwd v['response'] = response v['request'] = fr kb.kb.append(self, 'auth', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def _disable_functions(self, response): regex_str = 'disable_functions</td><td class="v">(.*?)</td>' disable_functions = re.search(regex_str, response.get_body(), re.I) if disable_functions: secure_df = 8 df = disable_functions.group(1) dfe = df.split(',') if len(dfe) < secure_df: desc = 'The phpinfo()::disable_functions are set to few.' v = Vuln('PHP disable_functions:few', 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())
def _memory_limit(self, response): regex_str = 'memory_limit</td><td class="v">(\d.*?)</td>' memory_limit = re.search(regex_str, response.get_body(), re.I) if memory_limit: secure_ml = 10 ml = memory_limit.group(1) + '' ml = ml.replace('M', '') if ml > secure_ml: desc = 'The phpinfo()::memory_limit is set to higher value'\ ' (%s).' % memory_limit.group(1) v = Vuln('PHP memory_limit:high', 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())
def _post_max_size(self, response): regex_str = 'post_max_size</td><td class="v">(\d.*?)</td>' post_max_size = re.search( regex_str, response.get_body(), re.IGNORECASE) if post_max_size: secure_pms = 20 pms = post_max_size.group(1) + '' pms = pms.replace('M', '') pms = int(pms) if pms > secure_pms: desc = 'The phpinfo()::post_max_size is set to higher value'\ ' (%s).' % post_max_size.group(1) v = Vuln('PHP post_max_size:high', desc, severity.LOW, 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())
def _upload_max_filesize(self, response): regex_str = 'upload_max_filesize</td><td class="v">(\d.*?)</td>' upload_max_filesize = re.search( regex_str, response.get_body(), re.IGNORECASE) if upload_max_filesize: secure_umf = 20 umf = upload_max_filesize.group(1) + '' umf = umf.replace('M', '') umf = int(umf) if umf > secure_umf: desc = 'The phpinfo()::upload_max_filesize is set to higher'\ ' value (%s).' % upload_max_filesize.group(1) v = Vuln('PHP upload_max_filesize:high', desc, severity.LOW, 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())
def _check_and_analyze(self, domain_path, php_info_filename): """ Check if a php_info_filename exists in the domain_path. :return: None, everything is put() into the self.output_queue. """ php_info_url = domain_path.url_join(php_info_filename) response = self._uri_opener.GET(php_info_url, cache=True, grep=False) if is_404(response): return # Check if it is a phpinfo file php_version = self.PHP_VERSION_RE.search(response.get_body(), re.I) sysinfo = self.SYSTEM_RE.search(response.get_body(), re.I) if not php_version: return if not sysinfo: return # Create the fuzzable request and send it to the core fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr) desc = ('The phpinfo() file was found at: %s. The version' ' of PHP is: "%s" and the system information is:' ' "%s".') desc %= (response.get_url(), php_version.group(2), sysinfo.group(1)) v = Vuln('phpinfo() file found', 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()) if not self._has_audited: self._has_audited = True self.audit_phpinfo(response)
def _check_and_analyze(self, domain_path): """ Check if a .DS_Store filename exists in the domain_path. :return: None, everything is saved to the self.out_queue. """ # Request the file url = domain_path.url_join(self.DS_STORE) try: response = self.http_get_and_parse(url, binary_response=True) except BaseFrameworkException as w3: msg = 'Failed to GET .DS_Store file: %s. Exception: %s.' om.out.debug(msg, (url, w3)) return # Check if it's a .DS_Store file if is_404(response): return try: store = DsStore(response.get_raw_body()) entries = store.get_file_entries() except Exception as e: om.out.debug('Unexpected error while parsing DS_Store file: "%s"' % e) return parsed_url_list = [] for filename in entries: parsed_url_list.append(domain_path.url_join(filename)) self.worker_pool.map(self.http_get_and_parse, parsed_url_list) desc = ('A .DS_Store file was found at: %s. The contents of this file' ' disclose filenames') desc %= (response.get_url()) v = Vuln('.DS_Store file found', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) kb.kb.append(self, 'dot_ds_store', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
def lowest_privilege_test(response): regex_str = 'User/Group </td><td class="v">(.*?)\((\d.*?)\)/(\d.*?)</td>' lowest_privilege_test_mo = re.search(regex_str, response.get_body(), re.I) if not lowest_privilege_test_mo: return lpt_uname = lowest_privilege_test_mo.group(1) lpt_uid = lowest_privilege_test_mo.group(2) lpt_uid = int(lpt_uid) lpt_gid = lowest_privilege_test_mo.group(3) lpt_gid = int(lpt_gid) is_privileged_username_mo = re.match('root|apache|daemon|bin|operator|adm', lpt_uname, re.I) if lpt_uid < 99 or lpt_gid < 99 or is_privileged_username_mo: desc = ('phpinfo()::PHP may be executing as a higher privileged' ' user or group. Username: %s, User id: %s, Group id: %s.') desc %= (lpt_uname, lpt_uid, lpt_gid) v = Vuln('PHP running with privileged user', 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()) else: desc = ('PHP seems to be running as a low privileged user.' ' Username: %s, User id: %s, Group id: %s.') desc %= (lpt_uname, lpt_uid, lpt_gid) i = Info('PHP running as low privileged user', desc, response.id, 'phpinfo') i.set_url(response.get_url()) kb.kb.append('phpinfo', 'phpinfo', i) om.out.information(i.get_desc())
def _curl_file_support(self, response): regex_str = '<h1 class="p">PHP Version (\d).(\d).(\d)</h1>' curl_file_support = re.search(regex_str, response.get_body(), re.I) if curl_file_support: php_major_ver = curl_file_support.group(1) php_minor_ver = curl_file_support.group(2) php_rev_ver = curl_file_support.group(3) current_ver = php_major_ver + '.' + php_minor_ver + \ '' + php_rev_ver current_ver = float(current_ver) php_major_ver = int(php_major_ver) php_minor_ver = int(php_minor_ver) php_rev_ver = int(php_rev_ver) cv4check = float(4.44) cv5check = float(5.16) curl_vuln = 1 if php_major_ver == 4: if current_ver >= cv4check: curl_vuln = 0 elif php_major_ver == 5: if current_ver >= cv5check: curl_vuln = 0 elif php_major_ver >= 6: curl_vuln = 0 else: curl_vuln = 0 if curl_vuln == 1: desc = 'The phpinfo()::cURL::file_support has a security hole'\ ' present in this version of PHP allows the cURL'\ ' functions to bypass safe_mode and open_basedir'\ ' restrictions.' v = Vuln('PHP curl_file_support:not_fixed', 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())
def curl_file_support(response): regex_str = '<h1 class="p">PHP Version (\d).(\d).(\d)</h1>' curl_file_support_mo = re.search(regex_str, response.get_body(), re.I) if not curl_file_support_mo: return php_major_ver = curl_file_support_mo.group(1) php_minor_ver = curl_file_support_mo.group(2) php_rev_ver = curl_file_support_mo.group(3) current_ver = php_major_ver + '.' + php_minor_ver + php_rev_ver current_ver = float(current_ver) php_major_ver = int(php_major_ver) cv4check = float(4.44) cv5check = float(5.16) curl_vuln = 1 if php_major_ver == 4: if current_ver >= cv4check: curl_vuln = 0 elif php_major_ver == 5: if current_ver >= cv5check: curl_vuln = 0 elif php_major_ver >= 6: curl_vuln = 0 else: curl_vuln = 0 if curl_vuln == 1: desc = ('The phpinfo()::cURL::file_support has a security hole' ' present in this version of PHP allows the cURL' ' functions to bypass safe_mode and open_basedir' ' restrictions.') v = Vuln('PHP curl_file_support:not_fixed', 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 _enable_dl(self, response): regex_str = 'enable_dl</td><td class="v">(On|Off)</td>' enable_dl = re.search(regex_str, response.get_body(), re.I) if enable_dl: rg = enable_dl.group(1) if rg == 'On': desc = 'The phpinfo()::enable_dl is on.' v = Vuln('PHP enable_dl: On', 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: ed_name = 'PHP enable_dl: Off' ed_desc = 'The phpinfo()::enable_dl is off.' i = Info(ed_name, ed_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 _register_globals(self, response): regex_str = 'register_globals</td><td class="v">(On|Off)</td>' register_globals = re.search(regex_str, response.get_body(), re.I) if register_globals: rg = register_globals.group(1) if rg == 'On': desc = 'The phpinfo()::register_globals is on.' v = Vuln('PHP register_globals: On', 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: rg_name = 'PHP register_globals: Off' rg_desc = 'The phpinfo()::register_globals is off.' i = Info(rg_name, rg_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 memory_limit(response): regex_str = 'memory_limit</td><td class="v">(\d.*?)</td>' memory_limit_mo = re.search(regex_str, response.get_body(), re.I) if not memory_limit_mo: return secure_ml = 10 ml = memory_limit_mo.group(1) + '' ml = ml.replace('M', '') ml = int(ml) if ml > secure_ml: desc = 'The phpinfo()::memory_limit is set to a high value: %s' desc %= (memory_limit_mo.group(1), ) v = Vuln('PHP high memory limit', 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 _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 response.get_code() == 200: 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) else: return
def _verify_upload(self, domain_path, rand_file, upload_id, debugging_id): """ Verify if the file was uploaded. :param domain_path: http://localhost/f00/ :param rand_file: The filename that was (potentially) uploaded :param upload_id: The id of the POST request to author.dll """ target_url = domain_path.url_join(rand_file) try: res = self._uri_opener.GET(target_url, cache=False, grep=False, debugging_id=debugging_id) except BaseFrameworkException as e: om.out.debug('Exception while verifying if the file that was uploaded' 'using author.dll was there: %s' % e) else: # The file we uploaded has the reversed filename as body if rand_file[::-1] not in res.get_body(): return desc = ('An insecure configuration in the frontpage extensions' ' allows unauthenticated users to upload files to the' ' remote web server.') response_ids = [upload_id, res.id] if upload_id is not None else [res.id] v = Vuln('Insecure Frontpage extensions configuration', desc, severity.HIGH, response_ids, self.get_name()) v.set_url(target_url) v.set_method('POST') om.out.vulnerability(v.get_desc(), severity=v.get_severity()) self.kb_append(self, 'frontpage', v)
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_handler = self._is_in_phishtank(to_check) for ptm in pt_handler.matches: fr = FuzzableRequest(ptm.url) self.output_queue.put(fr) # Only create the vuln object once if pt_handler.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 post_max_size(response): regex_str = 'post_max_size</td><td class="v">(\d.*?)</td>' post_max_size_mo = re.search(regex_str, response.get_body(), re.IGNORECASE) if not post_max_size_mo: return secure_pms = 20 pms = post_max_size_mo.group(1) + '' pms = pms.replace('M', '') pms = int(pms) if pms <= secure_pms: return desc = 'The phpinfo()::post_max_size is set to a high value: %s' desc %= (post_max_size_mo.group(1), ) v = Vuln('PHP high POST max size', 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())
return desc = ('An insecure configuration in the frontpage extensions' ' allows unauthenticated users to upload files to the' ' remote web server.') response_ids = [upload_id, res.id ] if upload_id is not None else [res.id] v = Vuln('Insecure Frontpage extensions configuration', desc, severity.HIGH, response_ids, self.get_name()) v.set_url(target_url) v.set_method('POST') om.out.vulnerability(v.get_desc(), severity=v.get_severity()) self.kb_append(self, 'frontpage', v) def get_plugin_deps(self): """ :return: A list with the names of the plugins that should be run before the current one. """ return ['infrastructure.frontpage_version'] def get_long_desc(self): """ :return: A DETAILED description of the plugin functions and features. """ return """ This plugin audits the frontpage extension configuration by trying to
def audit_phpinfo(self, response): """ Scan for insecure php settings :author: Aung Khant (aungkhant[at]yehg.net) :return none two divisions: vulnerable settings and useful informative settings """ ##### [Vulnerable Settings] ##### ### [register_globals] ### regex_str = 'register_globals</td><td class="v">(On|Off)</td>' register_globals = re.search(regex_str, response.get_body(), re.I) rg_flag = '' if register_globals: rg = register_globals.group(1) if (rg == 'On'): desc = 'The phpinfo()::register_globals is on.' v = Vuln('PHP register_globals: On', 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: rg_flag = 'info' rg_name = 'PHP register_globals: Off' rg_desc = 'The phpinfo()::register_globals is off.' ### [/register_globals] ### ### [allow_url_fopen] ### regex_str = 'allow_url_fopen</td><td class="v">(On|<i>no value</i>)</td>' allow_url_fopen = re.search(regex_str, response.get_body(), re.I) if allow_url_fopen: desc = 'The phpinfo()::allow_url_fopen is enabled.' v = Vuln('PHP allow_url_fopen: On', 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()) ### [/allow_url_fopen] ### ### [allow_url_include] ### regex_str = 'allow_url_include</td><td class="v">(On|<i>no value</i>)</td>' allow_url_include = re.search(regex_str, response.get_body(), re.I) if allow_url_include: desc = 'The phpinfo()::allow_url_include is enabled.' v = Vuln('PHP allow_url_include: On', 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()) ### [/allow_url_include] ### ### [display_errors] ### regex_str = 'display_errors</td><td class="v">(On|<i>no value</i>)</td>' display_errors = re.search(regex_str, response.get_body(), re.I) if display_errors: desc = 'The phpinfo()::display_errors is enabled.' v = Vuln('PHP display_errors: On', 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()) ### [/display_errors] ### ### [expose_php] ### regex_str = 'expose_php</td><td class="v">(On|<i>no value</i>)</td>' expose_php = re.search(regex_str, response.get_body(), re.I) if expose_php: desc = 'The phpinfo()::expose_php is enabled.' v = Vuln('PHP expose_php: On', 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()) ### [/expose_php] ### ### [lowest_privilege_test] ### regex_str = 'User/Group </td><td class="v">(.*?)\((\d.*?)\)/(\d.*?)</td>' lowest_privilege_test = re.search(regex_str, response.get_body(), re.I) lpt_flag = '' 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_flag = 'info' 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 ### [/lowest_privilege_test] ### ### [disable_functions] ### regex_str = 'disable_functions</td><td class="v">(.*?)</td>' disable_functions = re.search(regex_str, response.get_body(), re.I) if disable_functions: secure_df = 8 df = disable_functions.group(1) dfe = df.split(',') if (len(dfe) < secure_df): desc = 'The phpinfo()::disable_functions are set to few.' v = Vuln('PHP disable_functions:few', 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()) ### [/disable_functions] ### ### [curl_file_support] ### regex_str = '<h1 class="p">PHP Version (\d).(\d).(\d)</h1>' curl_file_support = re.search(regex_str, response.get_body(), re.I) if curl_file_support: php_major_ver = curl_file_support.group(1) php_minor_ver = curl_file_support.group(2) php_rev_ver = curl_file_support.group(3) current_ver = php_major_ver + '.' + php_minor_ver + \ '' + php_rev_ver current_ver = float(current_ver) php_major_ver = int(php_major_ver) php_minor_ver = int(php_minor_ver) php_rev_ver = int(php_rev_ver) cv4check = float(4.44) cv5check = float(5.16) curl_vuln = 1 if (php_major_ver == 4): if (current_ver >= cv4check): curl_vuln = 0 elif (php_major_ver == 5): if (current_ver >= cv5check): curl_vuln = 0 elif (php_major_ver >= 6): curl_vuln = 0 else: curl_vuln = 0 if (curl_vuln == 1): desc = 'The phpinfo()::cURL::file_support has a security hole'\ ' present in this version of PHP allows the cURL'\ ' functions to bypass safe_mode and open_basedir'\ ' restrictions.' v = Vuln('PHP curl_file_support:not_fixed', 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()) ### [/curl_file_support] ### ### [cgi_force_redirect] ### regex_str = 'cgi_force_redirect</td><td class="v">(.*?)</td>' cgi_force_redirect = re.search(regex_str, response.get_body(), re.I) if cgi_force_redirect: utd = cgi_force_redirect.group(1) + '' if (utd != 'On'): desc = 'The phpinfo()::CGI::force_redirect is disabled.' v = Vuln('PHP cgi_force_redirect: Off', 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()) ### [/cgi_force_redirect] ### ### [session_cookie_httponly] ### regex_str = 'session\.cookie_httponly</td><td class="v">(Off|no|0)</td>' session_cookie_httponly = re.search(regex_str, response.get_body(), re.I) if session_cookie_httponly: desc = 'The phpinfo()::session.cookie_httponly is off.' v = Vuln('PHP session.cookie_httponly: Off', 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()) ### [/session_cookie_httponly] ### ### [session_save_path] ### regex_str = 'session\.save_path</td><td class="v">(<i>no value</i>)</td>' session_save_path = re.search(regex_str, response.get_body(), re.I) if session_save_path: desc = 'The phpinfo()::session.save_path may be set to world-'\ 'accessible directory.' v = Vuln('PHP session_save_path:Everyone', desc, severity.LOW, 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()) ### [/session_save_path] ### ### [session_use_trans] ### regex_str = 'session\.use_trans</td><td class="v">(On)</td>' session_use_trans = re.search(regex_str, response.get_body(), re.I) if session_use_trans: desc = 'The phpinfo()::session.use_trans is enabled. This makes'\ ' session hijacking easier.' v = Vuln('PHP session_use_trans: On', 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()) ### [/session_use_trans] ### ### [default_charset] ### regex_str = 'default_charset</td><td class="v">(Off|no|0)</td>' default_charset = re.search(regex_str, response.get_body(), re.I) if default_charset: desc = 'The phpinfo()::default_charset is set to none. This'\ ' makes PHP scripts vulnerable to variable charset'\ ' encoding XSS.' v = Vuln('PHP default_charset: Off', 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()) ### [/default_charset] ### ### [enable_dl] ### regex_str = 'enable_dl</td><td class="v">(On|Off)</td>' enable_dl = re.search(regex_str, response.get_body(), re.I) ed_flag = '' if enable_dl: rg = enable_dl.group(1) if (rg == 'On'): desc = 'The phpinfo()::enable_dl is on.' v = Vuln('PHP enable_dl: On', 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: ed_flag = 'info' ed_name = 'PHP enable_dl: Off' ed_desc = 'The phpinfo()::enable_dl is off.' ### [/enable_dl] ### ### [memory_limit] ### regex_str = 'memory_limit</td><td class="v">(\d.*?)</td>' memory_limit = re.search(regex_str, response.get_body(), re.I) if memory_limit: secure_ml = 10 ml = memory_limit.group(1) + '' ml = ml.replace('M', '') if (ml > secure_ml): desc = 'The phpinfo()::memory_limit is set to higher value'\ ' (%s).' % memory_limit.group(1) v = Vuln('PHP memory_limit:high', 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()) ### [/memory_limit] ### ### [post_max_size] ### regex_str = 'post_max_size</td><td class="v">(\d.*?)</td>' post_max_size = re.search(regex_str, response.get_body(), re.IGNORECASE) if post_max_size: secure_pms = 20 pms = post_max_size.group(1) + '' pms = pms.replace('M', '') pms = int(pms) if (pms > secure_pms): desc = 'The phpinfo()::post_max_size is set to higher value'\ ' (%s).' % post_max_size.group(1) v = Vuln('PHP post_max_size:high', desc, severity.LOW, 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()) ### [/post_max_size] ### ### [upload_max_filesize] ### regex_str = 'upload_max_filesize</td><td class="v">(\d.*?)</td>' upload_max_filesize = re.search(regex_str, response.get_body(), re.IGNORECASE) if upload_max_filesize: secure_umf = 20 umf = upload_max_filesize.group(1) + '' umf = umf.replace('M', '') umf = int(umf) if (umf > secure_umf): desc = 'The phpinfo()::upload_max_filesize is set to higher'\ ' value (%s).' % upload_max_filesize.group(1) v = Vuln('PHP upload_max_filesize:high', desc, severity.LOW, 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()) ### [/upload_max_filesize] ### ### [upload_tmp_dir] ### regex_str = 'upload_tmp_dir</td><td class="v">(<i>no value</i>)</td>' upload_tmp_dir = re.search(regex_str, response.get_body(), re.I) if upload_tmp_dir: desc = 'The phpinfo()::upload_tmp_dir may be set to world-'\ 'accessible directory.' v = Vuln('PHP upload_tmp_dir:Everyone', desc, severity.LOW, 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()) ### [/upload_tmp_dir] ### ##### [/Vulnerable Settings] ##### ##### [Useful Informative Settings] ##### ### [privilege] ### if lpt_flag == 'info': 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()) ### [/privilege] ### ### [register_globals]### if rg_flag == 'info': i = Info(rg_name, rg_desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/register_globals]### ### [enable_dl]### if ed_flag == 'info': i = Info(ed_name, ed_desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/enable_dl]### ### [file_uploads] ### regex_str = 'file_uploads</td><td class="v">(On|<i>no value</i>)</td>' file_uploads = re.search(regex_str, response.get_body(), re.IGNORECASE) if file_uploads: desc = 'The phpinfo()::file_uploads is enabled.' i = Info('PHP file_uploads: On', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/file_uploads] ### ### [magic_quotes_gpc] ### regex_str = 'magic_quotes_gpc</td><td class="v">(On|Off)</td>' magic_quotes_gpc = re.search(regex_str, response.get_body(), re.I) if magic_quotes_gpc: mqg = magic_quotes_gpc.group(1) if (mqg == 'On'): desc = 'The phpinfo()::magic_quotes_gpc is on.' i = Info('PHP magic_quotes_gpc: On', desc, response.id, self.get_name()) else: desc = 'The phpinfo()::magic_quotes_gpc is off.' i = Info('PHP magic_quotes_gpc: Off', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/magic_quotes_gpc] ### ### [open_basedir] ### regex_str = 'open_basedir</td><td class="v">(.*?)</td>' open_basedir = re.search(regex_str, response.get_body(), re.I) if open_basedir: obd = open_basedir.group(1) if (obd == '<i>no value</i>'): desc = 'The phpinfo()::open_basedir is not set.' i = Info('PHP open_basedir:disabled', desc, response.id, self.get_name()) else: desc = 'The phpinfo()::open_basedir is set to %s.' desc = desc % open_basedir.group(1) i = Info('PHP open_basedir:enabled', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/open_basedir] ### ### [session_hash_function] ### regex_str = 'session\.hash_function</td><td class="v">(.*?)</td>' session_hash_function = re.search(regex_str, response.get_body(), re.I) if session_hash_function: if session_hash_function.group(1) == 0\ or session_hash_function.group(1) != 'no': desc = 'The phpinfo()::session.hash_function use md5 algorithm.' i = Info('PHP session.hash_function:md5', desc, response.id, self.get_name()) else: desc = 'The phpinfo()::session.hash_function use sha algorithm.' i = Info('PHP session.hash_function:sha', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc())
sysinfo = re.search(regex_str, response.get_body(), re.I) if (php_version and sysinfo): desc = 'The phpinfo() file was found at: %s. The version'\ ' of PHP is: "%s" and the system information is:'\ ' "%s".' desc = desc % (response.get_url(), php_version.group(2), sysinfo.group(1)) v = Vuln('phpinfo() file found', 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()) if (self._has_audited == 0): self.audit_phpinfo(response) self._has_audited = 1 def audit_phpinfo(self, response): """ Scan for insecure php settings :author: Aung Khant (aungkhant[at]yehg.net) :return none two divisions: vulnerable settings and useful informative settings """
def audit_phpinfo(self, response): """ Scan for insecure php settings :author: Aung Khant (aungkhant[at]yehg.net) :return none two divisions: vulnerable settings and useful informative settings """ ##### [Vulnerable Settings] ##### ### [register_globals] ### regex_str = 'register_globals</td><td class="v">(On|Off)</td>' register_globals = re.search(regex_str, response.get_body(), re.I) rg_flag = '' if register_globals: rg = register_globals.group(1) if(rg == 'On'): desc = 'The phpinfo()::register_globals is on.' v = Vuln('PHP register_globals: On', 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: rg_flag = 'info' rg_name = 'PHP register_globals: Off' rg_desc = 'The phpinfo()::register_globals is off.' ### [/register_globals] ### ### [allow_url_fopen] ### regex_str = 'allow_url_fopen</td><td class="v">(On|<i>no value</i>)</td>' allow_url_fopen = re.search(regex_str, response.get_body(), re.I) if allow_url_fopen: desc = 'The phpinfo()::allow_url_fopen is enabled.' v = Vuln('PHP allow_url_fopen: On', 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()) ### [/allow_url_fopen] ### ### [allow_url_include] ### regex_str = 'allow_url_include</td><td class="v">(On|<i>no value</i>)</td>' allow_url_include = re.search(regex_str, response.get_body(), re.I) if allow_url_include: desc = 'The phpinfo()::allow_url_include is enabled.' v = Vuln('PHP allow_url_include: On', 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()) ### [/allow_url_include] ### ### [display_errors] ### regex_str = 'display_errors</td><td class="v">(On|<i>no value</i>)</td>' display_errors = re.search(regex_str, response.get_body(), re.I) if display_errors: desc = 'The phpinfo()::display_errors is enabled.' v = Vuln('PHP display_errors: On', 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()) ### [/display_errors] ### ### [expose_php] ### regex_str = 'expose_php</td><td class="v">(On|<i>no value</i>)</td>' expose_php = re.search(regex_str, response.get_body(), re.I) if expose_php: desc = 'The phpinfo()::expose_php is enabled.' v = Vuln('PHP expose_php: On', 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()) ### [/expose_php] ### ### [lowest_privilege_test] ### regex_str = 'User/Group </td><td class="v">(.*?)\((\d.*?)\)/(\d.*?)</td>' lowest_privilege_test = re.search(regex_str, response.get_body(), re.I) lpt_flag = '' 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_flag = 'info' 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 ### [/lowest_privilege_test] ### ### [disable_functions] ### regex_str = 'disable_functions</td><td class="v">(.*?)</td>' disable_functions = re.search(regex_str, response.get_body(), re.I) if disable_functions: secure_df = 8 df = disable_functions.group(1) dfe = df.split(',') if(len(dfe) < secure_df): desc = 'The phpinfo()::disable_functions are set to few.' v = Vuln('PHP disable_functions:few', 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()) ### [/disable_functions] ### ### [curl_file_support] ### regex_str = '<h1 class="p">PHP Version (\d).(\d).(\d)</h1>' curl_file_support = re.search(regex_str, response.get_body(), re.I) if curl_file_support: php_major_ver = curl_file_support.group(1) php_minor_ver = curl_file_support.group(2) php_rev_ver = curl_file_support.group(3) current_ver = php_major_ver + '.' + php_minor_ver + \ '' + php_rev_ver current_ver = float(current_ver) php_major_ver = int(php_major_ver) php_minor_ver = int(php_minor_ver) php_rev_ver = int(php_rev_ver) cv4check = float(4.44) cv5check = float(5.16) curl_vuln = 1 if(php_major_ver == 4): if (current_ver >= cv4check): curl_vuln = 0 elif (php_major_ver == 5): if (current_ver >= cv5check): curl_vuln = 0 elif (php_major_ver >= 6): curl_vuln = 0 else: curl_vuln = 0 if(curl_vuln == 1): desc = 'The phpinfo()::cURL::file_support has a security hole'\ ' present in this version of PHP allows the cURL'\ ' functions to bypass safe_mode and open_basedir'\ ' restrictions.' v = Vuln('PHP curl_file_support:not_fixed', 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()) ### [/curl_file_support] ### ### [cgi_force_redirect] ### regex_str = 'cgi_force_redirect</td><td class="v">(.*?)</td>' cgi_force_redirect = re.search(regex_str, response.get_body(), re.I) if cgi_force_redirect: utd = cgi_force_redirect.group(1) + '' if(utd != 'On'): desc = 'The phpinfo()::CGI::force_redirect is disabled.' v = Vuln('PHP cgi_force_redirect: Off', 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()) ### [/cgi_force_redirect] ### ### [session_cookie_httponly] ### regex_str = 'session\.cookie_httponly</td><td class="v">(Off|no|0)</td>' session_cookie_httponly = re.search(regex_str, response.get_body(), re.I) if session_cookie_httponly: desc = 'The phpinfo()::session.cookie_httponly is off.' v = Vuln('PHP session.cookie_httponly: Off', 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()) ### [/session_cookie_httponly] ### ### [session_save_path] ### regex_str = 'session\.save_path</td><td class="v">(<i>no value</i>)</td>' session_save_path = re.search(regex_str, response.get_body(), re.I) if session_save_path: desc = 'The phpinfo()::session.save_path may be set to world-'\ 'accessible directory.' v = Vuln('PHP session_save_path:Everyone', desc, severity.LOW, 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()) ### [/session_save_path] ### ### [session_use_trans] ### regex_str = 'session\.use_trans</td><td class="v">(On)</td>' session_use_trans = re.search(regex_str, response.get_body(), re.I) if session_use_trans: desc = 'The phpinfo()::session.use_trans is enabled. This makes'\ ' session hijacking easier.' v = Vuln('PHP session_use_trans: On', 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()) ### [/session_use_trans] ### ### [default_charset] ### regex_str = 'default_charset</td><td class="v">(Off|no|0)</td>' default_charset = re.search(regex_str, response.get_body(), re.I) if default_charset: desc = 'The phpinfo()::default_charset is set to none. This'\ ' makes PHP scripts vulnerable to variable charset'\ ' encoding XSS.' v = Vuln('PHP default_charset: Off', 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()) ### [/default_charset] ### ### [enable_dl] ### regex_str = 'enable_dl</td><td class="v">(On|Off)</td>' enable_dl = re.search(regex_str, response.get_body(), re.I) ed_flag = '' if enable_dl: rg = enable_dl.group(1) if(rg == 'On'): desc = 'The phpinfo()::enable_dl is on.' v = Vuln('PHP enable_dl: On', 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: ed_flag = 'info' ed_name = 'PHP enable_dl: Off' ed_desc = 'The phpinfo()::enable_dl is off.' ### [/enable_dl] ### ### [memory_limit] ### regex_str = 'memory_limit</td><td class="v">(\d.*?)</td>' memory_limit = re.search(regex_str, response.get_body(), re.I) if memory_limit: secure_ml = 10 ml = memory_limit.group(1) + '' ml = ml.replace('M', '') if(ml > secure_ml): desc = 'The phpinfo()::memory_limit is set to higher value'\ ' (%s).' % memory_limit.group(1) v = Vuln('PHP memory_limit:high', 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()) ### [/memory_limit] ### ### [post_max_size] ### regex_str = 'post_max_size</td><td class="v">(\d.*?)</td>' post_max_size = re.search( regex_str, response.get_body(), re.IGNORECASE) if post_max_size: secure_pms = 20 pms = post_max_size.group(1) + '' pms = pms.replace('M', '') pms = int(pms) if(pms > secure_pms): desc = 'The phpinfo()::post_max_size is set to higher value'\ ' (%s).' % post_max_size.group(1) v = Vuln('PHP post_max_size:high', desc, severity.LOW, 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()) ### [/post_max_size] ### ### [upload_max_filesize] ### regex_str = 'upload_max_filesize</td><td class="v">(\d.*?)</td>' upload_max_filesize = re.search( regex_str, response.get_body(), re.IGNORECASE) if upload_max_filesize: secure_umf = 20 umf = upload_max_filesize.group(1) + '' umf = umf.replace('M', '') umf = int(umf) if(umf > secure_umf): desc = 'The phpinfo()::upload_max_filesize is set to higher'\ ' value (%s).' % upload_max_filesize.group(1) v = Vuln('PHP upload_max_filesize:high', desc, severity.LOW, 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()) ### [/upload_max_filesize] ### ### [upload_tmp_dir] ### regex_str = 'upload_tmp_dir</td><td class="v">(<i>no value</i>)</td>' upload_tmp_dir = re.search(regex_str, response.get_body(), re.I) if upload_tmp_dir: desc = 'The phpinfo()::upload_tmp_dir may be set to world-'\ 'accessible directory.' v = Vuln('PHP upload_tmp_dir:Everyone', desc, severity.LOW, 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()) ### [/upload_tmp_dir] ### ##### [/Vulnerable Settings] ##### ##### [Useful Informative Settings] ##### ### [privilege] ### if lpt_flag == 'info': 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()) ### [/privilege] ### ### [register_globals]### if rg_flag == 'info': i = Info(rg_name, rg_desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/register_globals]### ### [enable_dl]### if ed_flag == 'info': i = Info(ed_name, ed_desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/enable_dl]### ### [file_uploads] ### regex_str = 'file_uploads</td><td class="v">(On|<i>no value</i>)</td>' file_uploads = re.search(regex_str, response.get_body(), re.IGNORECASE) if file_uploads: desc = 'The phpinfo()::file_uploads is enabled.' i = Info('PHP file_uploads: On', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/file_uploads] ### ### [magic_quotes_gpc] ### regex_str = 'magic_quotes_gpc</td><td class="v">(On|Off)</td>' magic_quotes_gpc = re.search(regex_str, response.get_body(), re.I) if magic_quotes_gpc: mqg = magic_quotes_gpc.group(1) if (mqg == 'On'): desc = 'The phpinfo()::magic_quotes_gpc is on.' i = Info('PHP magic_quotes_gpc: On', desc, response.id, self.get_name()) else: desc = 'The phpinfo()::magic_quotes_gpc is off.' i = Info('PHP magic_quotes_gpc: Off', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/magic_quotes_gpc] ### ### [open_basedir] ### regex_str = 'open_basedir</td><td class="v">(.*?)</td>' open_basedir = re.search(regex_str, response.get_body(), re.I) if open_basedir: obd = open_basedir.group(1) if(obd == '<i>no value</i>'): desc = 'The phpinfo()::open_basedir is not set.' i = Info('PHP open_basedir:disabled', desc, response.id, self.get_name()) else: desc = 'The phpinfo()::open_basedir is set to %s.' desc = desc % open_basedir.group(1) i = Info('PHP open_basedir:enabled', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc()) ### [/open_basedir] ### ### [session_hash_function] ### regex_str = 'session\.hash_function</td><td class="v">(.*?)</td>' session_hash_function = re.search(regex_str, response.get_body(), re.I) if session_hash_function: if session_hash_function.group(1) == 0\ or session_hash_function.group(1) != 'no': desc = 'The phpinfo()::session.hash_function use md5 algorithm.' i = Info('PHP session.hash_function:md5', desc, response.id, self.get_name()) else: desc = 'The phpinfo()::session.hash_function use sha algorithm.' i = Info('PHP session.hash_function:sha', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'phpinfo', i) om.out.information(i.get_desc())
if (php_version and sysinfo): desc = 'The phpinfo() file was found at: %s. The version'\ ' of PHP is: "%s" and the system information is:'\ ' "%s".' desc = desc % (response.get_url(), php_version.group(2), sysinfo.group(1)) v = Vuln('phpinfo() file found', 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()) if (self._has_audited == 0): self.audit_phpinfo(response) self._has_audited = 1 def audit_phpinfo(self, response): """ Scan for insecure php settings :author: Aung Khant (aungkhant[at]yehg.net) :return none two divisions: vulnerable settings and useful informative settings """
'using author.dll was there: %s' % e om.out.debug(msg) else: # The file we uploaded has the reversed filename as body if res.get_body() == rand_file[::-1] and not is_404(res): desc = 'An insecure configuration in the frontpage extensions'\ ' allows unauthenticated users to upload files to the'\ ' remote web server.' v = Vuln('Insecure Frontpage extensions configuration', desc, severity.HIGH, [upload_id, res.id], self.get_name()) v.set_url(target_url) v.set_method('POST') om.out.vulnerability(v.get_desc(), severity=v.get_severity()) self.kb_append(self, 'frontpage', v) else: msg = 'The file that was uploaded using the POST method is'\ ' not present on the remote web server at "%s".' om.out.debug(msg % target_url) def get_plugin_deps(self): """ :return: A list with the names of the plugins that should be run before the current one. """ return ['infrastructure.frontpage_version'] def get_long_desc(self): """
def _check_and_analyze(self, domain_path): """ Check if a .listing filename exists in the domain_path. :return: None, everything is saved to the self.out_queue. """ url = domain_path.url_join('.listing') response = self._uri_opener.GET(url, cache=True) if is_404(response): return parsed_url_set = set() users = set() groups = set() # Check if it's a .listing file extracted_info = self._extract_info_from_listing(response.get_body()) for username, group, filename in extracted_info: if filename in ('.', '..'): continue parsed_url_set.add(domain_path.url_join(filename)) users.add(username) groups.add(group) self.worker_pool.map(self.http_get_and_parse, parsed_url_set) if parsed_url_set: desc = ('A .listing file was found at: "%s". The contents' ' of this file disclose filenames.') desc %= (response.get_url()) v = Vuln('.listing file found', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) kb.kb.append(self, 'dot_listing', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity()) fr = FuzzableRequest(response.get_url()) self.output_queue.put(fr) real_users = set([u for u in users if not u.isdigit()]) real_groups = set([g for g in groups if not g.isdigit()]) if real_users or real_groups: desc = ( 'A .listing file which leaks operating system user names' ' and groups was identified at %s. The leaked users are %s,' ' and the groups are %s. This information can be used' ' during a bruteforce attack of the Web application,' ' SSH or FTP services.') desc %= (response.get_url(), ', '.join(real_users), ', '.join(real_groups)) v = Vuln('Operating system username and group leak', desc, severity.LOW, response.id, self.get_name()) v.set_url(response.get_url()) kb.kb.append(self, 'dot_listing', v) om.out.vulnerability(v.get_desc(), severity=v.get_severity())
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. """ # Here we use the new http_get instead of http_get_and_parse because # we want to check BAD_HTTP_CODES and the response body (see below) # before we send the response to the core http_response = self.http_get(repo_url, binary_response=True, respect_size_limit=False, grep=False) if is_404(http_response): return if http_response.get_code() in self.BAD_HTTP_CODES: return if not http_response.get_body(): return try: filenames = repo_get_files(http_response.get_raw_body()) except Exception as e: # We get here when the HTTP response is NOT a 404, but the response # body couldn't be properly parsed. This is usually because of a false # positive in the is_404 function, OR a new version-format of the file # to be parsed. # # Log in order to be able to improve the framework. args = (e, repo_get_files.__name__, repo_url) om.out.debug('Got a "%s" exception while running "%s" on "%s"' % args) return parsed_url_set = set() for filename in self._clean_filenames(filenames): test_url = domain_path.url_join(filename) if test_url in self._analyzed_filenames: continue parsed_url_set.add(test_url) self._analyzed_filenames.add(filename) if not parsed_url_set: return self.worker_pool.map(self.http_get_and_parse, parsed_url_set) # After performing the checks (404, redirects, body is not empty, body # can be parsed, body actually had filenames inside) send the URL to the # core fr = FuzzableRequest(repo_url, method='GET') self.output_queue.put(fr) # Now we send this finding to the report for manual analysis 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 %= (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 _analyze_crossdomain_clientaccesspolicy(self, url, response, file_name): # https://github.com/andresriancho/w3af/issues/14491 if file_name not in self.FILE_TAG_ATTR: return try: dom = xml.dom.minidom.parseString(response.get_body()) except Exception: # Report this, it may be interesting for the final user # not a vulnerability per-se... but... it's information after all if 'allow-access-from' in response.get_body() or \ 'cross-domain-policy' in response.get_body() or \ 'cross-domain-access' in response.get_body(): desc = 'The "%s" file at: "%s" is not a valid XML.' desc %= (file_name, response.get_url()) i = Info('Invalid RIA settings file', desc, response.id, self.get_name()) i.set_url(response.get_url()) kb.kb.append(self, 'info', i) om.out.information(i.get_desc()) return tag, attribute = self.FILE_TAG_ATTR.get(file_name) url_list = dom.getElementsByTagName(tag) for url in url_list: url = url.getAttribute(attribute) if url == '*': desc = 'The "%s" file at "%s" allows flash / silverlight'\ ' access from any site.' desc %= (file_name, response.get_url()) v = Vuln('Insecure RIA settings', desc, severity.LOW, 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=v.get_severity()) fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr) else: desc = 'The "%s" file at "%s" allows flash / silverlight'\ ' access from "%s".' desc %= (file_name, response.get_url(), url) i = Info('Cross-domain allow ACL', desc, response.id, self.get_name()) i.set_url(response.get_url()) i.set_method('GET') kb.kb.append(self, 'info', i) om.out.information(i.get_desc()) fr = FuzzableRequest.from_http_response(response) self.output_queue.put(fr)