created a clickjacking scanner that will test a page for an X-Frame-Options header, if the header is not there, then it will create a basic HTML page with that URL as an iframe

This commit is contained in:
ekultek 2017-11-01 21:48:58 -05:00
parent d1adbcdc10
commit bc335fa2d3
6 changed files with 142 additions and 9 deletions

View file

@ -1,4 +1,4 @@
7fa426efc9d5b26d93101c0058840788 ./zeus.py
b100d6428f04f18100e9358688004abd ./zeus.py
6ad5f22ec4a6f8324bfb1b01ab6d51ec ./etc/scripts/cleanup.sh
155c9482f690f1482f324a7ffd8b8098 ./etc/scripts/fix_pie.sh
0e435c641bc636ac0b3d54e032d9cf6a ./etc/scripts/install_nmap.sh
@ -9,6 +9,7 @@
82cc68f46539d0255f7ce14cd86cd49b ./etc/link_ext.txt
a3ee2d4610056c6fe270c2a74dda49f9 ./etc/dorks.txt
b34aacdcb683a2de649c002601f53746 ./etc/xss_payloads.txt
f914fafb99953a9124295b381be2954b .etc/clickjacking_test_page.html
d41d8cd98f00b204e9800998ecf8427e ./bin/__init__.py
ff2511effe21aa36002f37ed9bfe47ec ./bin/unzip_gecko.py
dc1eb4ebe0f372af48b5a9c107ebc68d ./bin/drivers/geckodriver-v0.18.0-linux32.tar.gz
@ -40,11 +41,12 @@ e2b494ba257444ed4a9a8a554dcbe250 ./lib/attacks/whois_lookup/whois.py
27358f26bda30d7356143c3ea1fa99c5 ./lib/attacks/nmap_scan/__init__.py
216999fa0e84866d5c1d96d5676034e4 ./lib/attacks/nmap_scan/nmap_opts.py
f746d2867f493104a78d0540cf50c03f ./lib/attacks/intel_me/__init__.py
49c88c6927a141a688d3fc0c7c101019 .lib/attacks/clickjacking_scan/__init__.py
1faa2b5dfad6eb538bbfe42942d2a9da ./lib/core/errors.py
d41d8cd98f00b204e9800998ecf8427e ./lib/core/__init__.py
97bfe118adfdfc3ef02c5dca4c0aec62 ./lib/core/settings.py
1781e9963d34604b626b6fe3db513bc7 ./lib/core/settings.py
d41d8cd98f00b204e9800998ecf8427e ./var/google_search/__init__.py
2d984415adc457061e1757cbd79ea12b ./var/google_search/search.py
914ac7cf2e216ca878a5f711a71290a8 ./var/google_search/search.py
d41d8cd98f00b204e9800998ecf8427e ./var/__init__.py
d41d8cd98f00b204e9800998ecf8427e ./var/auto_issue/__init__.py
4506850a02aa18e12bef4efeb760ad9e ./var/auto_issue/github.py

View file

@ -0,0 +1,5 @@
<html>
<body>
<iframe src="{}" width="" height=""></iframe>
</body>
</html>

View file

@ -0,0 +1,112 @@
import requests
import lib.core.settings
import var.auto_issue.github
class ClickJackingScanner(object):
def __init__(self, url):
self.url = url
self.safe = "X-Frame-Options"
self.html = open(lib.core.settings.CLICKJACKING_TEST_PAGE_PATH).read()
def generate_html(self):
"""
generate the HTML page if the clicking test was successfully completed
"""
return self.html.format(self.url)
def extract_and_test_headers(self, **kwargs):
"""
extract the headers from the url given to test if they contain the correct protection
against clickjacking
"""
proxy = kwargs.get("proxy", None)
agent = kwargs.get("agent", None)
forward = kwargs.get("forward", None)
if forward is not None:
ip_addrs = lib.core.settings.create_random_ip()
headers = {
"user-agent": agent,
"X-Forwarded-From": "{}, {}, {}".format(
ip_addrs[0], ip_addrs[1], ip_addrs[2]
),
"Connection": "close"
}
else:
headers = {
"user-agent": agent,
"Connection": "close"
}
req = requests.get(self.url, headers=headers, proxies=lib.core.settings.proxy_string_to_dict(proxy))
headers = req.headers
if self.safe in headers:
return False
return True
def clickjacking_main(url, **kwargs):
"""
main function for the clickjacking scan
"""
agent = kwargs.get("agent", None)
proxy = kwargs.get("proxy", None)
forward = kwargs.get("forward", None)
verbose = kwargs.get("verbose", False)
batch = kwargs.get("batch", False)
if not batch:
if lib.core.settings.URL_QUERY_REGEX.match(url):
question = lib.core.settings.prompt(
"it is recommended to use a URL without a GET(query) parameter, "
"heuristic testing has detected that the URL provided contains a "
"GET(query) parameter in it, would you like to continue", opts="yN"
)
if question.lower().startswith("n"):
lib.core.settings.logger.info(lib.core.settings.set_color(
"automatically removing all queries from URL..."
))
url = "http://{}".format(lib.core.settings.replace_http(url, complete=True))
scanner = ClickJackingScanner(url)
if verbose:
lib.core.settings.logger.debug(lib.core.settings.set_color(
"generating HTML...", level=10
))
data = scanner.generate_html()
if verbose:
lib.core.settings.logger.debug(lib.core.settings.set_color(
"HTML generated successfully...", level=10
))
print("{}\n{}\n{}".format("-" * 30, data, "-" * 30))
try:
results = scanner.extract_and_test_headers(agent=agent, proxy=proxy, forward=forward)
if results:
lib.core.settings.logger.info(lib.core.settings.set_color(
"it appears that provided URL '{}' is vulnerable to clickjacking, writing "
"to HTML file...".format(url)
))
lib.core.settings.write_to_log_file(
data,
lib.core.settings.CLICKJACKING_RESULTS_PATH,
"{}-clickjacking.html".format(lib.core.settings.replace_http(url))
)
else:
lib.core.settings.logger.error(lib.core.settings.set_color(
"provided URL '{}' seems to have the correct protection from clickjacking...".format(
url
), level=40
))
except Exception as e: # until I figure out the errors, we'll just make issues about them
lib.core.settings.logger.exception(lib.core.settings.set_color(
"Zeus failed to process the clickjacking test and received "
"error code '{}'...".format(e), level=50
))
var.auto_issue.github.request_issue_creation()

View file

@ -41,7 +41,7 @@ PATCH_ID = str(subprocess.check_output(["git", "rev-parse", "origin/master"]))[:
# clone link
CLONE = "https://github.com/ekultek/zeus-scanner.git"
# current version <major.minor.commit.patch ID>
VERSION = "1.1.12.{}".format(PATCH_ID)
VERSION = "1.1.13".format(PATCH_ID)
# colors to output depending on the version
VERSION_TYPE_COLORS = {"dev": 33, "stable": 92, "other": 30}
# version string formatting
@ -83,10 +83,14 @@ CLEANUP_TOOL_PATH = "{}/etc/scripts/cleanup.sh".format(os.getcwd())
LAUNCH_SQLMAP_API_TOOL = "{}/etc/scripts/launch_sqlmap_api.sh".format(os.getcwd())
# path to nmap installer
NMAP_INSTALLER_TOOL = "{}/etc/scripts/install_nmap.sh".format(os.getcwd())
# clickjacking HTML test page path
CLICKJACKING_TEST_PAGE_PATH = "{}/etc/clickjacking_test_page.html".format(os.getcwd())
# path to check if the program has been executed or not
EXECUTED_PATH = "{}/bin/executed.txt".format(os.getcwd())
# paths to sqlmap and nmap
TOOL_PATHS = "{}/bin/paths/path_config.ini".format(os.getcwd())
# path to write the HTML in
CLICKJACKING_RESULTS_PATH = "{}/log/clickjacking-log".format(os.getcwd())
# the log for found admin pages on a site
ADMIN_PAGE_FILE_PATH = "{}/log/admin-page-log".format(os.getcwd())
# path to the sitemap log file
@ -465,7 +469,7 @@ def write_to_log_file(data_to_write, path, filename):
log.write(etree.tostring(data_to_write, pretty_print=True))
except TypeError:
logger.warning(set_color(
"unable to serialize XML, writing as plain text (usually means the file already exists)...",
"unable to serialize data, writing as plain text (usually means the file already exists)...",
level=30
))
log.write(data_to_write)

View file

@ -275,7 +275,7 @@ def parse_search_results(query, url_to_search, verbose=False, **kwargs):
ip_to_use = (create_random_ip(), create_random_ip(), create_random_ip())
if verbose:
logger.debug(set_color(
"random IP address generated for headers '{}'...".format(ip_to_use), level=10
"random IP addresses generated for headers '{}'...".format(ip_to_use), level=10
))
headers = {

16
zeus.py
View file

@ -19,6 +19,7 @@ from lib.attacks.xss_scan import main_xss
from lib.attacks.nmap_scan.nmap_opts import NMAP_API_OPTS
from lib.attacks.sqlmap_scan.sqlmap_opts import SQLMAP_API_OPTIONS
from lib.attacks.whois_lookup.whois import whois_lookup_main
from lib.attacks.clickjacking_scan import clickjacking_main
from lib.core.errors import (
InvalidInputProvided,
@ -92,6 +93,8 @@ if __name__ == "__main__":
help="Run an XSS scan on the found URL's")
attacks.add_option("-w", "--whois-lookup", dest="performWhoisLookup", action="store_true",
help="Perform a WhoIs lookup on the provided domain")
attacks.add_option("-c", "--clickjacking", dest="performClickjackingScan", action="store_true",
help="Perform a clickjacking scan on a provided URL")
attacks.add_option("--sqlmap-args", dest="sqlmapArguments", metavar="SQLMAP-ARGS",
help="Pass the arguments to send to the sqlmap API within quotes & "
"separated by a comma. IE 'dbms mysql, verbose 3, level 5'")
@ -268,6 +271,7 @@ if __name__ == "__main__":
admin = kwargs.get("admin", False)
verbose = kwargs.get("verbose", False)
whois = kwargs.get("whois", False)
clickjacking = kwargs.get("clickjacking", False)
batch = kwargs.get("batch", False)
auto_start = kwargs.get("auto_start", False)
@ -277,7 +281,8 @@ if __name__ == "__main__":
"xss": opt.runXssScan,
"admin": opt.adminPanelFinder,
"intel": opt.intelCheck,
"whois": opt.performWhoisLookup
"whois": opt.performWhoisLookup,
"clickjacking": opt.performClickjackingScan
}
enabled = set()
@ -296,7 +301,7 @@ if __name__ == "__main__":
if not batch:
question = prompt(
"would you like to process found URL: '{}'gi".format(url), opts=["y", "N"]
"would you like to process found URL: '{}'".format(url), opts=["y", "N"]
)
else:
question = "y"
@ -332,6 +337,9 @@ if __name__ == "__main__":
whois_lookup_main(
url, verbose=opt.runInVerbose
)
elif clickjacking:
clickjacking_main(url, agent=agent_to_use, proxy=proxy_to_use,
forward=opt.forwardedForRandomIP, batch=opt.runInBatch)
else:
pass
else:
@ -364,7 +372,8 @@ if __name__ == "__main__":
options = [
opt.runSqliScan, opt.runPortScan,
opt.intelCheck, opt.adminPanelFinder,
opt.runXssScan, opt.performWhoisLookup
opt.runXssScan, opt.performWhoisLookup,
opt.performClickjackingScan
]
if any(options):
with open(urls_to_use) as urls:
@ -374,6 +383,7 @@ if __name__ == "__main__":
sqlmap=opt.runSqliScan, nmap=opt.runPortScan,
intel=opt.intelCheck, xss=opt.runXssScan,
whois=opt.performWhoisLookup, admin=opt.adminPanelFinder,
clickjacking=opt.performClickjackingScan,
verbose=opt.runInVerbose, batch=opt.runInBatch,
auto_start=opt.autoStartSqlmap
)