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,