#!/usr/bin/env python import os import io import time import shlex import optparse import warnings import subprocess from var import blackwidow from var.search import selenium_search from var.auto_issue.github import request_issue_creation from lib.header_check import main_header_check from lib.attacks.nmap_scan.nmap_opts import NMAP_API_OPTS from lib.attacks.sqlmap_scan.sqlmap_opts import SQLMAP_API_OPTIONS from lib.core.errors import ( InvalidInputProvided, InvalidProxyType ) from lib.core.common import ( start_up, shutdown, prompt ) from lib.core.settings import ( setup, BANNER, logger, set_color, get_latest_log_file, CURRENT_LOG_FILE_PATH, URL_LOG_PATH, get_random_dork, update_zeus, VERSION_STRING, URL_REGEX, URL_QUERY_REGEX, NMAP_MAN_PAGE_URL, SQLMAP_MAN_PAGE_URL, fix_log_file, SPIDER_LOG_PATH, config_headers, config_search_engine, find_running_opts, run_attacks, ) warnings.simplefilter("ignore") if __name__ == "__main__": parser = optparse.OptionParser(usage="{} -d|r|l|f|b| DORK|FILE|URL [ATTACKS] [--OPTS]".format( os.path.basename(__file__) )) # mandatory options mandatory = optparse.OptionGroup(parser, "Mandatory Options", "These options have to be used in order for Zeus to run") mandatory.add_option("-d", "--dork", dest="dorkToUse", metavar="DORK", help="Specify a singular Google dork to use for queries") mandatory.add_option("-l", "--dork-list", dest="dorkFileToUse", metavar="FILE-PATH", help="Specify a file full of dorks to run through"), mandatory.add_option("-r", "--rand-dork", dest="useRandomDork", action="store_true", help="Use a random dork from the etc/dorks.txt file to perform the scan") mandatory.add_option("-b", "--blackwidow", dest="spiderWebSite", metavar="URL", help="Spider a single webpage for all available URL's") mandatory.add_option("-f", "--url-file", dest="fileToEnumerate", metavar="FILE-PATH", help="Run an attack on URL's in a given file") # attack options attacks = optparse.OptionGroup(parser, "Attack arguments", "These arguments will give you the choice on how you want to check the websites") attacks.add_option("-s", "--sqli", dest="runSqliScan", action="store_true", help="Run a Sqlmap SQLi scan on the discovered URL's") 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("-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", 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("-g", "--github-search", dest="searchGithub", action="store_true", help="Perform a Github Gist search for any information on the found websites") attacks.add_option("-P", "--pgp", dest="pgpLookup", action="store_true", help="Perform a PGP public key lookup on the found URLs") 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'") attacks.add_option("--sqlmap-conf", dest="sqlmapConfigFile", metavar="CONFIG-FILE-PATH", help="Pass a configuration file that contains the sqlmap arguments") attacks.add_option("--nmap-args", dest="nmapArguments", metavar="NMAP-ARGS", help="Pass the arguments to send to the nmap API within quotes & " "separated by a pipe. IE '-O|-p 445, 1080'") attacks.add_option("--show-sqlmap", dest="showSqlmapArguments", action="store_true", help="Show the arguments that the sqlmap API understands") attacks.add_option("--show-nmap", dest="showNmapArgs", action="store_true", help="Show the arguments that nmap understands") attacks.add_option("--show-possibles", dest="showAllConnections", action="store_true", help="Show all connections made during the admin panel search") attacks.add_option("--tamper", dest="tamperXssPayloads", metavar="TAMPER-SCRIPT", help="Send the XSS payloads through tampering before sending to the target") attacks.add_option("--thread", dest="threadPanels", action="store_true", help="Run multiple threads on functions that support multi-threading") attacks.add_option("--auto", dest="autoStartSqlmap", action="store_true", help="Automatically start the sqlmap API (or at least try to)") # search engine options engines = optparse.OptionGroup(parser, "Search engine arguments", "Arguments to change the search engine used (default is Google)") engines.add_option("-D", "--search-engine-ddg", dest="useDDG", action="store_true", help="Use DuckDuckGo as the search engine") engines.add_option("-B", "--search-engine-bing", dest="useBing", action="store_true", help="Use Bing as the search engine") engines.add_option("-A", "--search-engine-aol", dest="useAOL", action="store_true", help="Use AOL as the search engine") # arguments to edit your search patterns search_items = optparse.OptionGroup(parser, "Search options", "Arguments that will control the search criteria") search_items.add_option("-L", "--links", dest="amountToSearch", type=int, metavar="HOW-MANY-LINKS", help="Specify how many links to try and search on Google") search_items.add_option("-M", "--multi", dest="searchMultiplePages", action="store_true", help="Search multiple pages of Google") search_items.add_option("-E", "--exclude-none", dest="noExclude", action="store_true", help="Do not exclude URLs because they do not have a GET(query) parameter in them") search_items.add_option("-W", "--webcache", dest="parseWebcache", action="store_true", help="Parse webcache URLs for the redirect in them") search_items.add_option("--x-forward", dest="forwardedForRandomIP", action="store_true", help="Add a header called 'X-Forwarded-For' with three random IP addresses") search_items.add_option("--time-sec", dest="controlTimeout", metavar="SECONDS", type=int, help="Control the sleep time to the WhoIS lookup to prevent errors") # obfuscation options anon = optparse.OptionGroup(parser, "Anonymity arguments", "Arguments that help with anonymity and hiding identity") anon.add_option("--proxy", dest="proxyConfig", metavar="PROXY-STRING", help="Use a proxy to do the scraping, will not auto configure to the API's") anon.add_option("--proxy-file", dest="proxyFileRand", metavar="FILE-PATH", help="Grab a random proxy from a given file of proxies") anon.add_option("--random-agent", dest="useRandomAgent", action="store_true", help="Use a random user-agent from the etc/agents.txt file") anon.add_option("--agent", dest="usePersonalAgent", metavar="USER-AGENT", help="Use your own personal user-agent"), anon.add_option("--tor", dest="useTor", action="store_true", help="Use Tor connection as the proxy and set the firefox browser settings to mimic Tor") # miscellaneous options misc = optparse.OptionGroup(parser, "Misc Options", "These options affect how the program will run") misc.add_option("--verbose", dest="runInVerbose", action="store_true", help="Run the application in verbose mode (more output)") misc.add_option("--batch", dest="runInBatch", action="store_true", help="Skip the questions and run in default batch mode") misc.add_option("--update", dest="updateZeus", action="store_true", help="Update to the latest development version") misc.add_option("--hide", dest="hideBanner", action="store_true", help="Hide the banner during running") misc.add_option("--version", dest="showCurrentVersion", action="store_true", help="Show the current version and exit") misc.add_option("-T", "--x-threads", dest="amountOfThreads", metavar="THREAD-AMOUNT", type=int, help="Specify how many threads you want to pass") misc.add_option("--show-success", dest="showSuccessRate", action="store_true", help="Calculate the dorks success rate and output the calculation in human readable form") parser.add_option_group(mandatory) parser.add_option_group(attacks) parser.add_option_group(search_items) parser.add_option_group(anon) parser.add_option_group(engines) parser.add_option_group(misc) opt, _ = parser.parse_args() if opt.showCurrentVersion: print(VERSION_STRING) exit(0) # run the setup on the program setup(verbose=opt.runInVerbose) if not opt.hideBanner: print(BANNER) start_up() if opt.showSqlmapArguments: logger.info(set_color( "there are a total of {} arguments understood by sqlmap API, " "they include:".format(len(SQLMAP_API_OPTIONS)) )) print("\n") for arg in SQLMAP_API_OPTIONS: print( "[*] {}".format(arg) ) print("\n") logger.info(set_color( "for more information about sqlmap arguments, see here '{}'...".format( SQLMAP_MAN_PAGE_URL ) )) shutdown() if opt.showNmapArgs: logger.info(set_color( "there are a total of {} arguments understood by nmap, they include:".format( len(NMAP_API_OPTS) ) )) print("\n") for arg in NMAP_API_OPTS: print( "[*] {}".format(arg) ) print("\n") logger.info(set_color( "for more information on what the arguments do please see here '{}'...".format( NMAP_MAN_PAGE_URL ) )) shutdown() # update the program if opt.updateZeus: logger.info(set_color( "update in progress..." )) update_zeus() shutdown() if opt.runInVerbose: being_run = find_running_opts(opt) logger.debug(set_color( "running with options '{}'...".format(being_run), level=10 )) logger.info(set_color( "log file being saved to '{}'...".format(get_latest_log_file(CURRENT_LOG_FILE_PATH)) )) def __run_attacks_main(**kwargs): """ main method to run the attacks """ log_to_use = kwargs.get("log", None) if log_to_use is None: options = (opt.dorkToUse, opt.useRandomDork, opt.dorkFileToUse) log_to_use = URL_LOG_PATH if any(o for o in options) else SPIDER_LOG_PATH try: urls_to_use = get_latest_log_file(log_to_use) except TypeError: urls_to_use = None else: urls_to_use = log_to_use if urls_to_use is None: logger.error(set_color( "unable to run attacks appears that no file was created for the retrieved data...", level=40 )) shutdown() options = [ opt.runSqliScan, opt.runPortScan, opt.adminPanelFinder, opt.runXssScan, opt.performWhoisLookup, opt.performClickjackingScan, opt.searchGithub, opt.pgpLookup ] if any(options): with open(urls_to_use) as urls: for i, url in enumerate(urls.readlines(), start=1): current = i if "webcache" in url: logger.warning(set_color( "ran into unexpected webcache URL skipping...", level=30 )) current -= 1 else: logger.info(set_color( "currently running on '{}' (target #{})...".format( url.strip(), current ), level=25 )) 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, pgp=opt.pgpLookup, xss=opt.runXssScan, whois=opt.performWhoisLookup, admin=opt.adminPanelFinder, clickjacking=opt.performClickjackingScan, github=opt.searchGithub, verbose=opt.runInVerbose, batch=opt.runInBatch, auto_start=opt.autoStartSqlmap, xforward=opt.forwardedForRandomIP, sqlmap_args=opt.sqlmapArguments, nmap_args=opt.nmapArguments, show_all=opt.showAllConnections, do_threading=opt.threadPanels, tamper_script=opt.tamperXssPayloads, timeout=opt.controlTimeout, proxy=proxy_to_use, agent=agent_to_use, conf_file=opt.sqlmapConfigFile, threads=opt.amountOfThreads ) print("\n") proxy_to_use, agent_to_use = config_headers( proxy=opt.proxyConfig, proxy_file=opt.proxyFileRand, p_agent=opt.usePersonalAgent, rand_agent=opt.useRandomAgent, verbose=opt.runInVerbose ) search_engine = config_search_engine( verbose=opt.runInVerbose, ddg=opt.useDDG, aol=opt.useAOL, bing=opt.useBing, enum=opt.fileToEnumerate ) try: # use a personal dork as the query if opt.dorkToUse is not None and not opt.searchMultiplePages: logger.info(set_color( "starting dork scan with query '{}'...".format(opt.dorkToUse) )) try: selenium_search.parse_search_results( opt.dorkToUse, search_engine, verbose=opt.runInVerbose, proxy=proxy_to_use, agent=agent_to_use, pull_all=opt.noExclude, parse_webcache=opt.parseWebcache, forward_for=opt.forwardedForRandomIP, tor=opt.useTor, batch=opt.runInBatch, show_success=opt.showSuccessRate ) except InvalidProxyType: supported_proxy_types = ["socks5", "socks4", "https", "http"] logger.fatal(set_color( "the provided proxy is not valid, specify the protocol and try again, supported " "proxy protocols are {} (IE socks5://127.0.0.1:9050)...".format(", ".join(supported_proxy_types)), level=50 )) except Exception as e: if "Permission denied:" in str(e): logger.fatal(set_color( "your permissions are not allowing Zeus to run, " "try running me with sudo...", level=50 )) shutdown() else: logger.exception(set_color( "ran into exception '{}'...".format(e), level=50 )) request_issue_creation() pass __run_attacks_main() # search multiple pages of Google elif opt.dorkToUse is not None or opt.useRandomDork and opt.searchMultiplePages: if opt.dorkToUse is not None: dork_to_use = opt.dorkToUse elif opt.useRandomDork: dork_to_use = get_random_dork() else: dork_to_use = None if dork_to_use is None: logger.warning(set_color( "there has been no dork to specified to do the searching, defaulting to random dork...", level=30 )) dork_to_use = get_random_dork() dork_to_use = dork_to_use.strip() if opt.amountToSearch is None: logger.warning(set_color( "did not specify amount of links to find defaulting to 75...", level=30 )) link_amount_to_search = 75 else: link_amount_to_search = opt.amountToSearch logger.info(set_color( "searching Google using dork '{}' for a total of {} links...".format( dork_to_use, link_amount_to_search ) )) try: selenium_search.search_multiple_pages( dork_to_use, link_amount_to_search, proxy=proxy_to_use, agent=agent_to_use, verbose=opt.runInVerbose, xforward=opt.forwardedForRandomIP, batch=opt.runInBatch, show_success=opt.showSuccessRate ) except Exception as e: if "Error 400" in str(e): logger.fatal(set_color( "failed to connect to search engine...".format(e), level=50 )) elif "Error 503" in str(e): logger.fatal(set_color( "Google has blocked your IP address from doing anymore searches via API, " "you can still search using headless browsers (-d )...", level=50 )) else: logger.exception(set_color( "failed with unexpected error '{}'...".format(e), level=50 )) shutdown() __run_attacks_main() # use a file full of dorks as the queries elif opt.dorkFileToUse is not None: with io.open(opt.dorkFileToUse, encoding="utf-8") as dorks: for dork in dorks.readlines(): dork = dork.strip() logger.info(set_color( "starting dork scan with query '{}'...".format(dork) )) try: selenium_search.parse_search_results( dork, search_engine, verbose=opt.runInVerbose, proxy=proxy_to_use, agent=agent_to_use, pull_all=opt.noExclude, parse_webcache=opt.parseWebcache, tor=opt.useTor, batch=opt.runInBatch ) except Exception as e: logger.exception(set_color( "ran into exception '{}'...".format(e), level=50 )) request_issue_creation() pass __run_attacks_main() # use a random dork as the query elif opt.useRandomDork: random_dork = get_random_dork().strip() if opt.runInVerbose: logger.debug(set_color( "choosing random dork from etc/dorks.txt...", level=10 )) logger.info(set_color( "using random dork '{}' as the search query...".format(random_dork) )) try: selenium_search.parse_search_results( random_dork, search_engine, verbose=opt.runInVerbose, proxy=proxy_to_use, agent=agent_to_use, pull_all=opt.noExclude, parse_webcache=opt.parseWebcache, tor=opt.useTor, batch=opt.runInBatch ) __run_attacks_main() except Exception as e: logger.exception(set_color( "ran into exception '{}' and cannot continue, saved to current log file...".format(e), level=50 )) request_issue_creation() pass # spider a given webpage for all available URL's elif opt.spiderWebSite: problem_identifiers = ["http://", "https://"] if not URL_REGEX.match(opt.spiderWebSite): err_msg = "URL did not match a true URL{}..." if not any(m in opt.spiderWebSite for m in problem_identifiers): err_msg = err_msg.format(" issue seems to be that http:// " "or https:// is not present in the URL") else: err_msg = err_msg.format("") raise InvalidInputProvided( err_msg ) else: if URL_QUERY_REGEX.match(opt.spiderWebSite): question_msg = ( "it is recommended to not use a URL that has a GET(query) parameter in it, " "would you like to continue" ) if not opt.runInBatch: is_sure = prompt( question_msg, opts="yN" ) else: is_sure = prompt( question_msg, opts="yN", default="y" ) if is_sure.lower().startswith("y"): pass else: shutdown() blackwidow.blackwidow_main(opt.spiderWebSite, agent=agent_to_use, proxy=proxy_to_use, verbose=opt.runInVerbose, forward=opt.forwardedForRandomIP) __run_attacks_main() # enumerate a file and run attacks on the URL's provided elif opt.fileToEnumerate is not None: logger.info(set_color( "found a total of {} URL's to enumerate in given file...".format( len(open(opt.fileToEnumerate).readlines()) ) )) __run_attacks_main(log=opt.fileToEnumerate) else: logger.critical(set_color( "failed to provide a mandatory argument, you will be redirected to the help menu...", level=50 )) time.sleep(2) zeus_help_menu_command = shlex.split("python zeus.py --help") subprocess.call(zeus_help_menu_command) except IOError as e: if "Invalid URL" in str(e): logger.exception(set_color( "URL provided is not valid, schema appears to be missing...", level=50 )) request_issue_creation() shutdown() elif "HTTP Error 429: Too Many Requests" in str(e): logger.fatal(set_color( "WhoIs doesn't like it when you send to many requests at one time, " "try updating the timeout with the --time-sec flag (IE --time-sec 10)", level=50 )) shutdown() elif "No such file or directory" in str(e): logger.exception(e) logger.fatal(set_color( "provided file does not exist, make sure you have the full path...", level=50 )) else: logger.exception(set_color( "Zeus has hit an unexpected error and cannot continue, error code '{}'...".format(e), level=50 )) request_issue_creation() except KeyboardInterrupt: logger.error(set_color( "user aborted process...", level=40 )) except UnboundLocalError: logger.warning(set_color( "do not interrupt the browser when selenium is running, " "it will cause Zeus to crash...", level=30 )) except Exception as e: if "url did not match a true url" in str(e).lower(): logger.error(set_color( "you did not provide a URL that is capable of being processed, " "the URL provided to the spider needs to contain protocol as well " "ie. 'http://google.com' (it is advised not to add the GET parameter), " "fix the URL you want to scan and try again...", level=40 )) shutdown() elif "Service geckodriver unexpectedly exited" in str(e): logger.fatal(set_color( "it seems your firefox version is not compatible with the geckodriver version, " "please re-install Zeus and try again...", level=50 )) shutdown() elif "Max retries exceeded with url" in str(e): logger.fatal(set_color( "you have hit the max retries, to continue using Zeus " "it is recommended to use a proxy (--proxy/--proxy-file) " "along with a new user-agent (--random-agent/--agent).", level=50 )) shutdown() else: logger.exception(set_color( "ran into exception '{}' exception has been saved to log file...".format(e), level=50 )) request_issue_creation() # fix the log file before shutting down incase you want to look at it fix_log_file() shutdown()