mirror of
https://github.com/Ekultek/Zeus-Scanner.git
synced 2026-03-11 08:55:51 +00:00
418 lines
No EOL
17 KiB
Python
Executable file
418 lines
No EOL
17 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
import io
|
|
import sys
|
|
import time
|
|
import shlex
|
|
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.core.parse import ZeusParser
|
|
from lib.core.errors import (
|
|
InvalidInputProvided,
|
|
InvalidProxyType,
|
|
ZeusArgumentException
|
|
)
|
|
from lib.core.common import (
|
|
start_up,
|
|
shutdown,
|
|
prompt
|
|
)
|
|
from lib.core.settings import (
|
|
setup,
|
|
logger,
|
|
set_color,
|
|
get_latest_log_file,
|
|
get_random_dork,
|
|
fix_log_file,
|
|
config_headers,
|
|
config_search_engine,
|
|
find_running_opts,
|
|
run_attacks,
|
|
CURRENT_LOG_FILE_PATH,
|
|
SPIDER_LOG_PATH,
|
|
URL_REGEX, URL_QUERY_REGEX,
|
|
URL_LOG_PATH,
|
|
BANNER
|
|
)
|
|
|
|
warnings.simplefilter("ignore")
|
|
|
|
if __name__ == "__main__":
|
|
|
|
# this will take care of most of the Unicode errors.
|
|
reload(sys)
|
|
sys.setdefaultencoding("utf-8")
|
|
sys.setrecursionlimit(1500)
|
|
|
|
opt = ZeusParser.cmd_parser()
|
|
|
|
ZeusParser().single_show_args(opt)
|
|
|
|
# verify all the arguments passed before we continue
|
|
# with the process
|
|
ZeusParser().verify_args()
|
|
|
|
# run the setup on the program
|
|
setup(verbose=opt.runInVerbose)
|
|
|
|
if not opt.hideBanner:
|
|
print(BANNER)
|
|
|
|
start_up()
|
|
|
|
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.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:
|
|
if not url.strip() == "http://" or url == "https://":
|
|
logger.info(set_color(
|
|
"currently running on '{}' (target #{})".format(
|
|
url.strip(), current
|
|
), level=25
|
|
))
|
|
logger.info(set_color(
|
|
"fetching target meta-data"
|
|
))
|
|
identified = main_header_check(
|
|
url, verbose=opt.runInVerbose, agent=agent_to_use,
|
|
proxy=proxy_to_use, xforward=opt.forwardedForRandomIP,
|
|
identify_plugins=opt.identifyPlugin, identify_waf=opt.identifyProtection,
|
|
show_description=opt.showPluginDescription
|
|
)
|
|
if not identified:
|
|
logger.error(set_color(
|
|
"target is refusing to allow meta-data dumping, skipping", level=40
|
|
))
|
|
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, force_ssl=opt.forceSSL
|
|
)
|
|
print("\n")
|
|
else:
|
|
logger.warning(set_color(
|
|
"malformed URL discovered, skipping", level=30
|
|
))
|
|
|
|
|
|
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(list(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 Zeus 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
|
|
))
|
|
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.fatal(set_color(
|
|
"provided file does not exist, make sure you have the full path", level=50
|
|
))
|
|
shutdown()
|
|
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.fatal(set_color(
|
|
"user aborted process", level=50
|
|
))
|
|
shutdown()
|
|
except UnboundLocalError:
|
|
logger.warning(set_color(
|
|
"do not interrupt the browser when selenium is running, "
|
|
"it will cause Zeus to crash", level=30
|
|
))
|
|
except ZeusArgumentException:
|
|
shutdown()
|
|
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() |