def scan(session: Session): tty = sys.stdout.isatty() output.norm("Beginning SSL Labs scan (this could take a minute or two)") output.empty() # list the messages from SSL Labs - this is required as part of the ToS messages = api.get_info_message() for msg in messages: output.norm("[SSL Labs] {msg}".format(msg=msg)) api.start_scan(session.domain) status = "" error_count = 0 completed: List[str] = [] body = None while status != "READY" and status != "ERROR": sleep(5) try: status, body = api.check_scan(session.domain) except Exception: # if we find ourselves here, we want to try a couple more times before we give up for good output.debug_exception() if error_count > 3: raise else: error_count += 1 if tty: # clear the current line sys.stdout.write("\r\033[K") # display the current status if "endpoints" in body: msg = "" for ep in body["endpoints"]: if (ep["statusMessage"] == "Ready" and ep["ipAddress"] not in completed): completed.append(ep["ipAddress"]) sys.stdout.write( f'\r Status - {ep["ipAddress"]}: {ep["statusMessage"]}\n\r' ) elif (ep["statusMessage"] != "Pending" and ep["statusMessage"] != "Ready"): # get the completion percentage pct = "--" if "progress" in ep: pct = ep["progress"] sm = "unknown" if "statusDetailsMessage" in ep: sm = ep["statusDetailsMessage"] msg = f'\r Status - {ep["ipAddress"]}: {ep["statusMessage"]} ({sm}) - {pct}%' break if msg != "": sys.stdout.write(msg) else: sys.stdout.write(f"\r Status: Working...") elif "status" in body: sys.stdout.write(f'\r Status: {body["status"]}') else: sys.stdout.write(f"\r Status: Working...") # flush the buffer, to make sure it's actually written sys.stdout.flush() else: print(".", end="", flush=True) output.empty() output.empty() reporter.register_data("ssl_labs_results", body) # HACK: this needs to be refactored, once we have a better way to do it. This is awful. # (from a separation of duties perspective. this should happen in the plugin) if body["status"] == "ERROR": raise ValueError( f"SSL Labs Error: {body['status']}: {body['statusMessage']}") elif "endpoints" in body: for ep in body["endpoints"]: if ep["statusMessage"] == "Ready": output.norm(f'IP: {ep["ipAddress"]} - Grade: {ep["grade"]}') _get_cert_info(body, ep, session.url) _get_protocol_info(ep, session.url) _get_vulnerability_info(ep, session.url) else: output.error( f'Error getting information for IP: {ep["ipAddress"]}: {ep["statusMessage"]}' ) output.empty() else: output.debug(f"Invalid response received: {body}") output.error("Invalid response received: Endpoint data not found.") output.empty() raise ValueError(f"SSL Labs Error: {body['status']}")
def scan(session: Session): reporter.register_data("url", session.url) reporter.register_data("domain", session.domain) output.empty() output.norm("HEAD:") head = network.http_head(session.url) raw = network.http_build_raw_response(head) for line in raw.splitlines(): output.norm(f"\t{line}") output.empty() res = http_basic.get_header_issues(head, raw, session.url) if res: output.norm("Header Issues:") reporter.display_results(res, "\t") output.empty() res = http_basic.get_cookie_issues(head, session.url) if res: output.norm("Cookie Issues:") reporter.display_results(res, "\t") output.empty() # check for WAF signatures res = waf.get_waf(head.headers, raw, session.url) if res: output.norm("WAF Detection:") reporter.display_results(res, "\t") output.empty() output.norm("Performing vulnerability scan (this will take a while)...") links: List[str] = [] with Spinner(): try: links, res = spider.spider(session.url) except Exception as error: output.debug_exception() output.error(f"Error running scan: {str(error)}") output.norm(f"Identified {len(links) + 1} pages.") output.empty() if res: output.norm("Issues Detected:") reporter.display_results(res, "\t") output.empty() # get files, and add those to the link list links += _file_search(session, links) if ( session.args.pass_reset_page is not None and len(session.args.pass_reset_page) > 0 ): _check_password_reset(session) with Spinner(): res = http_basic.check_local_ip_disclosure(session) if res: reporter.display_results(res, "\t") with Spinner(): res = apache_httpd.check_all(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = apache_tomcat.check_all(session.url, links) if res: reporter.display_results(res, "\t") with Spinner(): res = nginx.check_all(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = iis.check_all(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = http_basic.check_propfind(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = http_basic.check_trace(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = http_basic.check_options(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = php.find_phpinfo(links) if res: reporter.display_results(res, "\t") with Spinner(): res, jira_path = jira.check_for_jira(session) if res: reporter.display_results(res, "\t") if jira_path is not None: with Spinner(): res = jira.check_jira_user_registration(jira_path) if res: reporter.display_results(res, "\t") with Spinner(): wp_path, res = wordpress.identify(session.url) if res: reporter.display_results(res, "\t") if wp_path is not None: with Spinner(): res = wordpress.check_json_user_enum(wp_path) res += wordpress.check_path_disclosure(wp_path) if res: reporter.display_results(res, "\t")
def scan(session: Session): reporter.register_data("url", session.url) reporter.register_data("domain", session.domain) # check to see if this is an IP, if so, bail out if utils.is_ip(session.domain): return output.empty() output.norm("DNS Information:") # get the root domain, by looking up via the PSL psl = PublicSuffixList() root_domain = psl.privatesuffix(session.domain) reporter.register_data("root_domain", root_domain) # IP Addresses for the domain we are scanning ips = basic.get_ips(session.domain) reporter.register_data("ip", ips) for ip in ips: output.norm("\t%s (%s)" % (ip, basic.get_host(str(ip)))) addr = ipaddress.ip_address(str(ip)) if not addr.is_private: ni = network_info.network_info(str(ip)) output.norm("\t\t%s" % ni) if addr.version == 4: output.norm("\t\thttps://www.shodan.io/host/%s" % ip) output.norm("\t\thttps://censys.io/ipv4/%s" % ip) else: output.norm("\t\thttps://www.shodan.io/host/%s" % str(ip).lower()) output.empty() # TXT records for the domain we are scanning try: txt = basic.get_text(session.domain) reporter.register_data("dns_txt", {session.domain: txt}) for rec in txt: output.norm("\tTXT: %s" % rec) except Exception as err: output.error(f"Error getting TXT records: {str(err)}") # TXT records for the root domain try: if root_domain != session.domain: txt = basic.get_text(root_domain) reporter.register_data("dns_txt", {root_domain: txt}) for rec in txt: output.norm("\tTXT (%s): %s" % (root_domain, rec)) except Exception as err: output.error(f"Error getting TXT (root) records: {str(err)}") output.empty() # MX records for the domain we are scanning try: mx = basic.get_mx(session.domain) reporter.register_data("dns_mx", {session.domain: mx}) for rec in mx: server_ip, ni = _get_ip_info(rec[0]) info = "%s (%s) - %s (%s)" % (rec[0], rec[1], server_ip, ni) output.norm("\tMX: %s" % info) except Exception as err: output.error(f"Error getting MX records: {str(err)}") try: # MX records for the root domain if root_domain != session.domain: mx = basic.get_mx(root_domain) reporter.register_data("dns_mx", {root_domain: mx}) for rec in mx: server_ip, ni = _get_ip_info(rec[0]) info = "%s (%s) - %s (%s)" % (rec[0], rec[1], server_ip, ni) output.norm("\tMX (%s): %s" % (root_domain, info)) except Exception as err: output.error(f"Error getting MX (root) records: {str(err)}") output.empty() # NS records for the root domain try: ns = basic.get_ns(root_domain) reporter.register_data("dns_ns", {root_domain: ns}) for rec in ns: server_ip, ni = _get_ip_info(rec) info = "%s - %s (%s)" % (rec, server_ip, ni) output.norm("\tNS: %s" % info) except Exception as err: output.error(f"Error getting NS records: {str(err)}") output.empty() if session.args.srv: try: output.norm( "Searching for SRV records, this will take a minute...") output.empty() with Spinner(): srv_records = srv.find_srv_records(root_domain) reporter.register_data("dns_srv", srv_records) for rec in srv_records: server_ip, ni = _get_ip_info(rec[1]) info = "%s: %s:%s - %s (%s)" % (rec[0], rec[1], rec[2], server_ip, ni) output.norm("\tSRV: %s" % info) output.empty() except Exception as err: output.error(f"Error getting SRV records: {str(err)}") if session.args.subdomains: try: output.norm( "Searching for sub-domains, this will take a few minutes...") output.empty() with Spinner(): sds = subdomains.find_subdomains(root_domain) reporter.register_data("dns_subdomains", sds) for rec in sds: info = "" if rec[0] == "CNAME": server_ip, ni = _get_ip_info(rec[2]) info = "(CNAME) %s -> %s - %s (%s)" % ( rec[1], rec[2], server_ip, ni, ) elif rec[0] == "A": ni = network_info.network_info(rec[2]) info = "(A) %s: %s (%s)" % (rec[1], rec[2], ni) elif rec[0] == "AAAA": ni = network_info.network_info(rec[2]) info = "(AAAA) %s: %s (%s)" % (rec[1], rec[2], ni) output.norm("\tSubdomain: %s" % info) except Exception as err: output.error(f"Error getting subdomain records: {str(err)}") output.empty() try: caa_count = 0 carec = caa.get_caa(session.domain) reporter.register_data("dns_caa", carec) for rec in carec: curr = rec[0] if rec[1] == "CNAME": output.norm("\tCAA (%s): CNAME Found: -> %s" % (curr, rec[2])) elif rec[1] == "CAA": if len(rec[2]) > 0: for line in rec[2]: output.norm('\tCAA (%s): "%s"' % (curr, line)) caa_count += 1 else: output.norm("\tCAA (%s): No Records Found" % curr) # notify the user if there's an issue if caa_count == 0: reporter.display( "\tCAA: Domain does not have protection from CAA", issue.Issue(Vulnerabilities.DNS_CAA_MISSING, session.url, {"caa_records": carec}), ) except Exception as err: output.error(f"Error getting CAA records: {str(err)}") output.empty() try: dk = dnssec.get_dnskey(session.domain) reporter.register_data("dns_dnskey", dk) if len(dk) > 0: for rec in dk: output.norm( "\tDNSKEY: Algorithm: '%s' - Flags: '%s' - Key Length: %s" % (rec[2], rec[0], len(rec[3]) * 8)) else: reporter.display( "\tDNSKEY: Domain does not use DNSSEC", issue.Issue(Vulnerabilities.DNS_DNSSEC_NOT_ENABLED, session.url, {}), ) except Exception as err: output.error(f"Error getting DNSKEY records: {str(err)}") output.empty()
def scan(session: Session): reporter.register_data("url", session.url) reporter.register_data("domain", session.domain) output.empty() output.norm("HEAD:") head = network.http_head(session.url) raw = network.http_build_raw_response(head) for line in raw.splitlines(): output.norm(f"\t{line}") output.empty() res = http_basic.get_header_issues(head, raw, session.url) if res: output.norm("Header Issues:") reporter.display_results(res, "\t") output.empty() res = http_basic.get_cookie_issues(head, session.url) if res: output.norm("Cookie Issues:") reporter.display_results(res, "\t") output.empty() # check for WAF signatures res = waf.get_waf(head.headers, raw, session.url) if res: output.norm("WAF Detection:") reporter.display_results(res, "\t") output.empty() # check the HSTS preload status results = http_basic.check_hsts_preload(session.url) if len(results) > 0: reporter.register_data("hsts_preload_status", results) output.norm("HSTS Preload Status:") for result in results: chrome = result["chrome"] is not None firefox = result["firefox"] is not None tor = result["tor"] is not None output.norm( f"\t({result['domain']}) Chrome: {chrome}\tFirefox: {firefox}\t\tTor: {tor}" ) output.empty() methods, res = http_basic.check_http_methods(session.url) if len(methods) == 0: output.norm("Server responds to invalid HTTP methods - check skipped.") else: reporter.register_data({"http_methods_supported": methods}) output.norm("Supported HTTP methods:") for method in methods: output.norm(f"\t{method}") output.empty() if res: reporter.display_results(res, "\t") output.empty() output.norm("Performing vulnerability scan (this will take a while)...") links: List[str] = [] with Spinner(): try: links, res = spider.spider(session.url) except Exception as error: output.debug_exception() output.error(f"Error running scan: {str(error)}") output.norm(f"Identified {len(links) + 1} pages.") output.empty() if res: output.norm("Issues Detected:") reporter.display_results(res, "\t") output.empty() # get files, and add those to the link list links += _file_search(session, links) if ( session.args.pass_reset_page is not None and len(session.args.pass_reset_page) > 0 ): _check_password_reset(session) with Spinner(): res = http_basic.check_local_ip_disclosure(session) if res: reporter.display_results(res, "\t") with Spinner(): res = apache_httpd.check_all(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = apache_tomcat.check_all(session.url, links) if res: reporter.display_results(res, "\t") with Spinner(): res = nginx.check_all(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = iis.check_all(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = http_basic.check_propfind(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = http_basic.check_trace(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = http_basic.check_options(session.url) if res: reporter.display_results(res, "\t") with Spinner(): res = php.find_phpinfo(links) if res: reporter.display_results(res, "\t") if session.args.php_page is not None and len(session.args.php_page) > 0: with Spinner(): res = php.check_cve_2019_11043(session, links) if res: reporter.display_results(res, "\t") with Spinner(): res, jira_path = jira.check_for_jira(session) if res: reporter.display_results(res, "\t") if jira_path is not None: with Spinner(): res = jira.check_jira_user_registration(jira_path) if res: reporter.display_results(res, "\t") with Spinner(): wp_path, res = wordpress.identify(session.url) if res: reporter.display_results(res, "\t") if wp_path is not None: with Spinner(): res = wordpress.check_json_user_enum(wp_path) res += wordpress.check_path_disclosure(wp_path) if res: reporter.display_results(res, "\t")
def scan(args: Namespace, url: str, domain: str): reporter.register_data("url", url) reporter.register_data("domain", domain) output.empty() output.norm("HEAD:") head = network.http_head(url) raw = network.http_build_raw_response(head) for line in raw.splitlines(): output.norm(f"\t{line}") output.empty() res = http_basic.get_header_issues(head, raw, url) if len(res) > 0: output.norm("Header Issues:") reporter.display_results(res, "\t") output.empty() res = http_basic.get_cookie_issues(head, raw, url) if len(res) > 0: output.norm("Cookie Issues:") reporter.display_results(res, "\t") output.empty() # check for WAF signatures res = waf.get_waf(head.headers, raw, url) if len(res) > 0: output.norm("WAF Detection:") reporter.display_results(res, "\t") output.empty() output.norm("Performing vulnerability scan (this will take a while)...") links: List[str] = [] with Spinner(): try: links, res = spider.spider(url) except Exception as error: output.debug_exception() output.error(f"Error running scan: {str(error)}") output.norm(f"Identified {len(links) + 1} pages.") output.empty() if len(res) > 0: output.norm("Issues Detected:") reporter.display_results(res, "\t") output.empty() # get files, and add those to the link list links += _file_search(args, url, links) res = apache_httpd.check_all(url) if len(res) > 0: reporter.display_results(res, "\t") res = apache_tomcat.check_all(url, links) if len(res) > 0: reporter.display_results(res, "\t") res = nginx.check_all(url) if len(res) > 0: reporter.display_results(res, "\t") res = iis.check_all(url) if len(res) > 0: reporter.display_results(res, "\t") res = http_basic.check_propfind(url) if len(res) > 0: reporter.display_results(res, "\t") res = http_basic.check_trace(url) if len(res) > 0: reporter.display_results(res, "\t") res = http_basic.check_options(url) if len(res) > 0: reporter.display_results(res, "\t") wp_path, res = wordpress.identify(url) if len(res) > 0: reporter.display_results(res, "\t") if wp_path is not None: res = wordpress.check_json_user_enum(wp_path) if len(res) > 0: reporter.display_results(res, "\t")