diff --git a/etc/checksum/md5sum.md5 b/etc/checksum/md5sum.md5 index 89d3c2d..2217e8e 100644 --- a/etc/checksum/md5sum.md5 +++ b/etc/checksum/md5sum.md5 @@ -1,4 +1,4 @@ -913ce06f47d730817768c6d434bccad9 ./zeus.py +f7dfe15a281f72ff02f937a51838c614 ./zeus.py 4b32db388e8acda35570c734d27c950c ./etc/scripts/launch_sqlmap.sh 6ad5f22ec4a6f8324bfb1b01ab6d51ec ./etc/scripts/cleanup.sh 155c9482f690f1482f324a7ffd8b8098 ./etc/scripts/fix_pie.sh @@ -6,6 +6,7 @@ 66b11aa388ea909de7b212341259a318 ./etc/auths/git_auth 8f686b05c5c5dfc02f0fcaa7ebc8677c ./etc/auths/whois_auth d3ad89703575a712a0aeead2b176d8c5 ./etc/html/clickjacking_test_page.html +7526750f8bd909ef08fa2c15d278c244 ./etc/xml/headers.xml 642a77905d8bb4e5533e0e9c2137c0fa ./etc/text_files/agents.txt 82cc68f46539d0255f7ce14cd86cd49b ./etc/text_files/link_ext.txt a3ee2d4610056c6fe270c2a74dda49f9 ./etc/text_files/dorks.txt @@ -51,7 +52,8 @@ d41d8cd98f00b204e9800998ecf8427e ./lib/attacks/whois_lookup/__init__.py a0b5265f235a266f7a48874c3d1e08f9 ./lib/attacks/intel_me/__init__.py 1faa2b5dfad6eb538bbfe42942d2a9da ./lib/core/errors.py d41d8cd98f00b204e9800998ecf8427e ./lib/core/__init__.py -30112312eefcb5444d7916fb8f78142a ./lib/core/settings.py +ecb1912f467c0c3d6fc58975f9317126 ./lib/core/settings.py +623830ef2e6f389c961edcb5bc135132 ./lib/header_check/__init__.py d41d8cd98f00b204e9800998ecf8427e ./var/google_search/__init__.py e9f1e2624154401840b0b118216ba82e ./var/google_search/search.py d41d8cd98f00b204e9800998ecf8427e ./var/__init__.py diff --git a/etc/xml/headers.xml b/etc/xml/headers.xml new file mode 100644 index 0000000..0890e23 --- /dev/null +++ b/etc/xml/headers.xml @@ -0,0 +1,7 @@ + +
+
+
+
+
+ \ No newline at end of file diff --git a/lib/core/settings.py b/lib/core/settings.py index fa03297..f9f48dd 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -44,7 +44,7 @@ PATCH_ID = str(subprocess.check_output(["git", "rev-parse", "origin/master"]))[: CLONE = "https://github.com/ekultek/zeus-scanner.git" # current version -VERSION = "1.1.21.{}".format(PATCH_ID) +VERSION = "1.1.22".format(PATCH_ID) # colors to output depending on the version VERSION_TYPE_COLORS = {"dev": 33, "stable": 92, "other": 30} @@ -103,9 +103,18 @@ NMAP_INSTALLER_TOOL = "{}/etc/scripts/install_nmap.sh".format(os.getcwd()) # clickjacking HTML test page path CLICKJACKING_TEST_PAGE_PATH = "{}/etc/html/clickjacking_test_page.html".format(os.getcwd()) +# check the site headers to see what it's possibly vulnerable against +HEADER_XML_DATA = "{}/etc/xml/headers.xml".format(os.getcwd()) + # holder for sqlmap API ID hashes, makes it so that they are all unique ALREADY_USED = set() +# holder for protection +PROTECTED = set() + +# save the headers to a file for further use +HEADER_RESULT_PATH = "{}/log/header-log".format(os.getcwd()) + # path to write the HTML in CLICKJACKING_RESULTS_PATH = "{}/log/clickjacking-log".format(os.getcwd()) @@ -754,3 +763,26 @@ def rewrite_all_paths(): open(path, "w").close() with open(EXECUTED_PATH, "w") as log: log.write("FALSE") + + +def check_for_protection(protected, attack_type): + """ + check if the provided target URL has header protection against an attack type + """ + items = [item.lower() for item in protected] + if attack_type in items: + protected.clear() # clear the set + logger.warning(set_color( + "provided target seems to have protection against this attack type...", level=30 + )) + question = prompt( + "continuing will most likely result in a failure, would you like to continue", opts="yN" + ) + if question.lower().startswith("y"): + return True + else: + logger.warning(set_color( + "skipping provided target URL..." + )) + return False + return True diff --git a/lib/header_check/__init__.py b/lib/header_check/__init__.py new file mode 100644 index 0000000..87ff5fa --- /dev/null +++ b/lib/header_check/__init__.py @@ -0,0 +1,114 @@ +import json + +import requests +from xml.dom import minidom + +from lib.core.settings import ( + logger, set_color, + HEADER_XML_DATA, + proxy_string_to_dict, + create_random_ip, + write_to_log_file, + HEADER_RESULT_PATH, + replace_http, + PROTECTED +) + + +def load_xml_data(path, start_node="header", search_node="name"): + """ + load the XML data + """ + retval = [] + fetched_xml = minidom.parse(path) + item_list = fetched_xml.getElementsByTagName(start_node) + for value in item_list: + retval.append(value.attributes[search_node].value) + return retval + + +def load_headers(url, **kwargs): + """ + load the URL headers + """ + agent = kwargs.get("agent", None) + proxy = kwargs.get("proxy", None) + xforward = kwargs.get("xforward", False) + + if proxy is not None: + proxy = proxy_string_to_dict(proxy) + if not xforward: + header_value = { + "connection": "close", + "user-agent": agent + } + else: + ip_list = create_random_ip(), create_random_ip(), create_random_ip() + header_value = { + "connection": "close", + "user-agent": agent, + "X-Forwarded-For": "{}, {}, {}".format( + ip_list[0], ip_list[1], ip_list[2] + ) + } + req = requests.get(url, params=header_value, proxies=proxy) + return req.headers + + +def compare_headers(found_headers, comparable_headers): + """ + compare the headers against one another + """ + retval = set() + for header in comparable_headers: + if header in found_headers: + retval.add(header) + return retval + + +def main_header_check(url, **kwargs): + """ + main function + """ + verbose = kwargs.get("verbose", False) + agent = kwargs.get("agent", None) + proxy = kwargs.get("proxy", None) + xforward = kwargs.get("xforward", False) + + protection = {} + definition = { + "x-xss": ("protection against XSS attacks", "XSS"), + "strict-transport": ("protection against unencrypted connections (force HTTPS connection)", "HTTPS"), + "x-frame": ("protection against clickjacking vulnerabilities", "CLICKJACKING"), + "x-content": ("protection against MIME type attacks", "MIME"), + "content-security": ("protection against multiple attacks", "ALL") + } + if verbose: + logger.debug(set_color( + "loading XML data...", level=10 + )) + comparable_headers = load_xml_data(HEADER_XML_DATA) + logger.info(set_color( + "attempting to get request headers..." + )) + found_headers = load_headers(url, proxy=proxy, agent=agent, xforward=xforward) + if verbose: + logger.debug(set_color( + "fetched {}...".format(found_headers), level=10 + )) + headers_established = [str(h) for h in compare_headers(found_headers, comparable_headers)] + protection["target"] = url + for key in definition.iterkeys(): + if any(key in h.lower() for h in headers_established): + logger.error(set_color( + "provided target has {}...".format(definition[key][0]), level=40 + )) + protection[key] = True + PROTECTED.add(definition[key][1]) + else: + logger.info(set_color( + "provided target does not have {}...".format(definition[key][0]) + )) + protection[key] = False + data_to_write = json.dumps(protection, indent=4) + write_to_log_file(data_to_write, HEADER_RESULT_PATH, "{}-headers.json".format(replace_http(url))) diff --git a/zeus.py b/zeus.py index 60eec60..0946cc2 100755 --- a/zeus.py +++ b/zeus.py @@ -14,6 +14,7 @@ except ImportError: from var import blackwidow from var.google_search import search from var.auto_issue.github import request_issue_creation +from lib.header_check import main_header_check from lib.attacks.admin_panel_finder import main from lib.attacks.xss_scan import main_xss from lib.attacks.nmap_scan.nmap_opts import NMAP_API_OPTS @@ -54,7 +55,9 @@ from lib.core.settings import ( config_headers, config_search_engine, find_running_opts, - create_arguments + create_arguments, + PROTECTED, + check_for_protection ) @@ -86,7 +89,8 @@ if __name__ == "__main__": attacks.add_option("-p", "--port-scan", dest="runPortScan", action="store_true", help="Run a Nmap port scan on the discovered URL's") attacks.add_option("-i", "--intel-check", dest="intelCheck", action="store_true", - help="Check if a URL's host is exploitable via Intel ME AMT (CVE-2017-5689)") + help="Check if a URL's host is exploitable via Intel ME AMT (CVE-2017-5689) " + "scans will be deprecated by version 1.2") attacks.add_option("-a", "--admin-panel", dest="adminPanelFinder", action="store_true", help="Search for the websites admin panel") attacks.add_option("-x", "--xss-scan", dest="runXssScan", action="store_true", @@ -322,6 +326,9 @@ if __name__ == "__main__": opts=create_arguments(nmap=True, nmap_args=opt.nmapArguments) ) elif intel: + logger.warning(set_color( + "intel AMT bypass scans will be deprecated by version 1.2...", level=30 + )) url = get_true_url(url) return intel_me.main_intel_amt( url, agent=agent_to_use, verbose=opt.runInVerbose, @@ -333,17 +340,19 @@ if __name__ == "__main__": verbose=verbose, do_threading=opt.threadPanels ) elif xss: - main_xss( - url, verbose=verbose, proxy=proxy_to_use, - agent=agent_to_use, tamper=opt.tamperXssPayloads - ) + if check_for_protection(PROTECTED, "xss"): + main_xss( + url, verbose=verbose, proxy=proxy_to_use, + agent=agent_to_use, tamper=opt.tamperXssPayloads + ) elif whois: whois_lookup_main( url, verbose=opt.runInVerbose, timeout=opt.controlTimeout ) elif clickjacking: - clickjacking_main(url, agent=agent_to_use, proxy=proxy_to_use, - forward=opt.forwardedForRandomIP, batch=opt.runInBatch) + if check_for_protection(PROTECTED, "clickjacking"): + clickjacking_main(url, agent=agent_to_use, proxy=proxy_to_use, + forward=opt.forwardedForRandomIP, batch=opt.runInBatch) else: pass else: @@ -386,6 +395,13 @@ if __name__ == "__main__": "ran into unexpected webcache URL skipping...", level=30 )) else: + logger.info(set_color( + "checking URL headers..." + )) + main_header_check( + url, verbose=opt.runInVerbose, agent=agent_to_use, + proxy=proxy_to_use, xforward=opt.forwardedForRandomIP + ) __run_attacks( url.strip(), sqlmap=opt.runSqliScan, nmap=opt.runPortScan,