Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8277b0e
Autosploit automation (#132)
Selora Apr 9, 2018
4506271
PR #133 cleaned - Autosploit Automation (#134)
Selora Apr 10, 2018
c389114
renaming to match the default modules
Ekultek Apr 11, 2018
98fd562
updating to match master
Apr 11, 2018
a919e55
updating to match master
Apr 11, 2018
0a8f552
fixes a typo in an
Apr 11, 2018
d6bccb0
completely reworked the OSX installer. It will now install on OSX suc…
Apr 11, 2018
a5408c8
merging the double install method into a single section
Apr 11, 2018
7c3ca6a
changing execution permissions on a couple files
Apr 11, 2018
80f7f84
minor update to catch a an error
Apr 11, 2018
f61b4fe
minor update to make it 'prettier'
Apr 11, 2018
df0e293
editing the results, no hardcoded '*'s now, they have their own variable
Apr 11, 2018
94035b8
minor update to edit a misspelling
Apr 11, 2018
c2936af
pretty update
Apr 11, 2018
e31bb3e
fixes a minor typo
Apr 11, 2018
75d4e50
added TODO's for reference
Jun 7, 2018
b998ad8
changing from services to systemctl (issue #138)
Jun 8, 2018
158087b
creating a check to see if you are admin or not. Will close if you ar…
Jun 8, 2018
41a938e
fixes the bug in sorting relevant modules (issue #141)
Jun 8, 2018
145611d
some updates for everything, PEPing and bashing syntax
Jun 8, 2018
ce914aa
the dryrun_autosploit script will now work successfully
Jun 8, 2018
d39cc7e
ready for production
Ekultek Jun 8, 2018
d842769
Merge branch 'master' into dev-beta
Ekultek Jun 8, 2018
5a0cf98
minor update for spacing
Ekultek Jun 8, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Autosploit automation (#132)
* API keys token file reading error fix
When reading an API key file, tokens are not stripped properly. Non-standard endlines causes an error with shodan/censys/etc APIs

* Added a dry-run flag.
When running without the terminal and the --dry-run flag, msfconsole will not be run.
A report will still be produced.

* Sanitized whitelist comparision with host file.
All leading and trailing whitespaces should be removed before comparing IPs

* Added an --exploit-file-to-use option.

Load exploits directly from the specified file, do not prompt for
exploit-file selection if this option is specified.

* Added --append/--overwrite to search engines.
Specifying either will skip the prompt after a search query.
--overwrite will start with a blank file but will append futher searches
ex: with -s -c --overwrite, both shodan and censys results will be appended to a clean file.

* Search all fix for append/overwrite flags.
Search results is not prompted anymore

* Modified the Exploiter output.

Added a tally at the end.
Suppressed much of the output during a dry-run.

* Bugfix, --exploit-file-to-use

Output an error message to the console if the specified exploit file does not exists.

* Added short arguments for --append/--overwrite

* Closing program if invalid file is passed to --exploit-file-to-use
  • Loading branch information
Selora authored and Ekultek committed Apr 9, 2018
commit 8277b0edb5cd743f8fc5a0b2b4748b65ca77e9e3
5 changes: 3 additions & 2 deletions api_calls/censys.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ class CensysAPIHook(object):
Censys API hook
"""

def __init__(self, identity=None, token=None, query=None, proxy=None, agent=None, **kwargs):
def __init__(self, identity=None, token=None, query=None, proxy=None, agent=None, save_mode=None, **kwargs):
self.id = identity
self.token = token
self.query = query
self.proxy = proxy
self.user_agent = agent
self.host_file = HOST_FILE
self.save_mode = save_mode

def censys(self):
"""
Expand All @@ -38,7 +39,7 @@ def censys(self):
json_data = req.json()
for item in json_data["results"]:
discovered_censys_hosts.add(str(item["ip"]))
write_to_file(discovered_censys_hosts, self.host_file)
write_to_file(discovered_censys_hosts, self.host_file, mode=self.save_mode)
return True
except Exception as e:
raise AutoSploitAPIConnectionError(str(e))
5 changes: 3 additions & 2 deletions api_calls/shodan.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ class ShodanAPIHook(object):
Shodan API hook, saves us from having to install another dependency
"""

def __init__(self, token=None, query=None, proxy=None, agent=None, **kwargs):
def __init__(self, token=None, query=None, proxy=None, agent=None, save_mode=None, **kwargs):
self.token = token
self.query = query
self.proxy = proxy
self.user_agent = agent
self.host_file = HOST_FILE
self.save_mode = save_mode

def shodan(self):
"""
Expand All @@ -38,7 +39,7 @@ def shodan(self):
json_data = json.loads(req.content)
for match in json_data["matches"]:
discovered_shodan_hosts.add(match["ip_str"])
write_to_file(discovered_shodan_hosts, self.host_file)
write_to_file(discovered_shodan_hosts, self.host_file, mode=self.save_mode)
return True
except Exception as e:
raise AutoSploitAPIConnectionError(str(e))
Expand Down
5 changes: 3 additions & 2 deletions api_calls/zoomeye.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ class ZoomEyeAPIHook(object):
so we're going to use some 'lifted' credentials to login for us
"""

def __init__(self, query=None, proxy=None, agent=None, **kwargs):
def __init__(self, query=None, proxy=None, agent=None, save_mode=None, **kwargs):
self.query = query
self.host_file = HOST_FILE
self.proxy = proxy
self.user_agent = agent
self.user_file = "{}/etc/text_files/users.lst".format(os.getcwd())
self.pass_file = "{}/etc/text_files/passes.lst".format(os.getcwd())
self.save_mode = save_mode

@staticmethod
def __decode(filepath):
Expand Down Expand Up @@ -81,7 +82,7 @@ def zoomeye(self):
discovered_zoomeye_hosts.add(ip)
else:
discovered_zoomeye_hosts.add(str(item["ip"][0]))
write_to_file(discovered_zoomeye_hosts, self.host_file)
write_to_file(discovered_zoomeye_hosts, self.host_file, mode=self.save_mode)
return True
except Exception as e:
raise AutoSploitAPIConnectionError(str(e))
Expand Down
18 changes: 15 additions & 3 deletions autosploit/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
EXPLOIT_FILES_PATH,
START_SERVICES_PATH
)
from lib.jsonize import load_exploits
from lib.jsonize import (
load_exploits,
load_exploit_file
)


def main():
Expand Down Expand Up @@ -73,8 +76,17 @@ def main():
info("attempting to load API keys")
loaded_tokens = load_api_keys()
AutoSploitParser().parse_provided(opts)
misc_info("checking if there are multiple exploit files")
loaded_exploits = load_exploits(EXPLOIT_FILES_PATH)

loaded_exploits = []
if not opts.exploitFile:
misc_info("checking if there are multiple exploit files")
loaded_exploits = load_exploits(EXPLOIT_FILES_PATH)
else:
loaded_exploits = load_exploit_file(opts.exploitFile)
misc_info("Loaded {} exploits from {}.".format(
len(loaded_exploits),
opts.exploitFile))

AutoSploitParser().single_run_args(opts, loaded_tokens, loaded_exploits)
else:
warning("no arguments have been parsed, defaulting to terminal session. press 99 to quit and help to get help")
Expand Down
41 changes: 34 additions & 7 deletions lib/cmdline/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ def optparser():
help="use shodan.io as the search engine to gather hosts")
se.add_argument("-a", "--all", action="store_true", dest="searchAll",
help="search all available search engines to gather hosts")
save_results_args = se.add_mutually_exclusive_group(required=False)
save_results_args.add_argument("-O", "--overwrite", action="store_true", dest="overwriteHosts",
help="When specified, start from scratch by overwriting the host file with new search results.")
save_results_args.add_argument("-A", "--append", action="store_true", dest="appendHosts",
help="When specified, append discovered hosts to the host file.")

req = parser.add_argument_group("requests", "arguments to edit your requests")
req.add_argument("--proxy", metavar="PROTO://IP:PORT", dest="proxyConfig",
Expand All @@ -59,6 +64,10 @@ def optparser():
help="set the configuration for MSF (IE -C default 127.0.0.1 8080)")
exploit.add_argument("-e", "--exploit", action="store_true", dest="startExploit",
help="start exploiting the already gathered hosts")
exploit.add_argument("-d", "--dry-run", action="store_true", dest="dryRun",
help="Do not launch metasploit's exploits. Do everything else. msfconsole is never called.")
exploit.add_argument("-f", "--exploit-file-to-use", metavar="PATH", dest="exploitFile",
help="Run AutoSploit with provided exploit JSON file.")

misc = parser.add_argument_group("misc arguments", "arguments that don't fit anywhere else")
misc.add_argument("--ruby-exec", action="store_true", dest="rubyExecutableNeeded",
Expand Down Expand Up @@ -134,32 +143,49 @@ def single_run_args(opt, keys, loaded_modules):
lib.output.error("caught IOError '{}' check the file path and try again".format(str(e)))
sys.exit(0)

search_save_mode = None
if opt.overwriteHosts:
# Create a new empty file, overwriting the previous one.
# Set the mode to append afterwards
# This way, successive searches will start clean without
# overriding each others.
open(lib.settings.HOST_FILE, mode="w").close()
search_save_mode = "a"
elif opt.appendHosts:
search_save_mode = "a"

if opt.searchCensys:
lib.output.info(single_search_msg.format("Censys"))
api_searches[2](
keys["censys"][1], keys["censys"][0],
opt.searchQuery, proxy=headers[0], agent=headers[1]
opt.searchQuery, proxy=headers[0], agent=headers[1],
save_mode=search_save_mode
).censys()
if opt.searchZoomeye:
lib.output.info(single_search_msg.format("Zoomeye"))
api_searches[0](
opt.searchQuery, proxy=headers[0], agent=headers[1]
opt.searchQuery, proxy=headers[0], agent=headers[1],
save_mode=search_save_mode
).zoomeye()
if opt.searchShodan:
lib.output.info(single_search_msg.format("Shodan"))
api_searches[1](
keys["shodan"][0], opt.searchQuery, proxy=headers[0], agent=headers[1]
keys["shodan"][0], opt.searchQuery, proxy=headers[0], agent=headers[1],
save_mode=search_save_mode
).shodan()
if opt.searchAll:
lib.output.info("searching all search engines in order")
api_searches[0](
opt.searchQuery, proxy=headers[0], agent=headers[1]
opt.searchQuery, proxy=headers[0], agent=headers[1],
save_mode=search_save_mode
).zoomeye()
api_searches[1](
keys["shodan"][0], opt.searchQuery, proxy=headers[0], agent=headers[1]
keys["shodan"][0], opt.searchQuery, proxy=headers[0], agent=headers[1],
save_mode=search_save_mode
).shodan()
api_searches[2](
keys["censys"][1], keys["censys"][0], opt.searchQuery, proxy=headers[0], agent=headers[1]
keys["censys"][1], keys["censys"][0], opt.searchQuery, proxy=headers[0], agent=headers[1],
save_mode=search_save_mode
).censys()
if opt.startExploit:
hosts = open(lib.settings.HOST_FILE).readlines()
Expand All @@ -170,5 +196,6 @@ def single_run_args(opt, keys, loaded_modules):
loaded_modules,
hosts,
ruby_exec=opt.rubyExecutableNeeded,
msf_path=opt.pathToFramework
msf_path=opt.pathToFramework,
dryRun=opt.dryRun
).start_exploit()
43 changes: 36 additions & 7 deletions lib/exploitation/exploiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@ def whitelist_wash(hosts, whitelist_file):
"""
remove IPs from hosts list that do not appear in WHITELIST_FILE
"""
whitelist_hosts = open(whitelist_file).readlines()
whitelist_hosts = [x.strip() for x in open(whitelist_file).readlines() if x.strip()]
lib.output.info('Found {} entries in whitelist.txt, scrubbing'.format(str(len(whitelist_hosts))))
washed_hosts = []
#return supplied hosts if whitelist file is empty
if len(whitelist_hosts) == 0:
return hosts
else:
for host in hosts:
if host in whitelist_hosts:
if host.strip() in whitelist_hosts:
washed_hosts.append(host)

return washed_hosts


class AutoSploitExploiter(object):

sorted_modules = []
Expand All @@ -41,6 +42,7 @@ def __init__(self, configuration, all_modules, hosts=None, **kwargs):
self.single = kwargs.get("single", None)
self.ruby_exec = kwargs.get("ruby_exec", False)
self.msf_path = kwargs.get("msf_path", None)
self.dry_run = kwargs.get("dryRun", False)

def view_sorted(self):
"""
Expand Down Expand Up @@ -82,16 +84,24 @@ def start_exploit(self):
"Failure Logs",
"All Logs"])

lib.output.info("Launching exploits against {hosts_len} hosts:".format(hosts_len=len(self.hosts)))
lib.output.info("{}".format((linesep+"\t").join(self.hosts)))

win_total = 0
fail_total = 0

for host in self.hosts:
current_host_path = path.join(current_run_path, host.strip())
makedirs(current_host_path)

for mod in self.mods:
lib.output.info(
"launching exploit '{}' against host '{}'".format(
mod.strip(), host.strip()
if not self.dry_run:
lib.output.info(
"launching exploit '{}' against host '{}'".format(
mod.strip(), host.strip()
)
)
)

cmd_template = (
"sudo {use_ruby} {msf_path} -r {rc_script_path} -q"
)
Expand Down Expand Up @@ -141,13 +151,18 @@ def start_exploit(self):
rc_script_path=current_rc_script_path
)

output = lib.settings.cmdline(cmd)
output = [""]
if not self.dry_run:
output = lib.settings.cmdline(cmd)

ansi_escape = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
msf_output_lines = linesep.join([ansi_escape.sub('', x) for x in output if re.search('\[.\]', x)])
msf_wins = linesep.join([ansi_escape.sub('', x) for x in output if re.search('\[\+\]', x)])
msf_fails = linesep.join([ansi_escape.sub('', x) for x in output if re.search('\[-\]', x)])

win_total += len(msf_wins)
fail_total += len(msf_fails)

csv_file = csv.writer(f, quoting=csv.QUOTE_ALL)
csv_file.writerow([rhost,
today_printable,
Expand All @@ -157,3 +172,17 @@ def start_exploit(self):
msf_wins,
msf_fails,
msf_output_lines])

print()
lib.output.info("********RESULTS**********")

if self.dry_run:
lib.output.info("\tDRY RUN!")
lib.output.info("\t0 exploits run against {} hosts.".format(len(self.hosts)))
else:
lib.output.info("\t{} exploits run against {} hosts.".format(len(self.mods), len(self.hosts)))
lib.output.info("\t{} exploit successful (Check report.csv to validate!).".format(win_total))
lib.output.info("\t{} exploit failed.".format(fail_total))

lib.output.info("\tExploit run saved to {}".format(str(current_run_path)))
lib.output.info("\tReport saved to {}".format(str(report_path)))
21 changes: 21 additions & 0 deletions lib/jsonize.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ def random_file_name(acceptable=string.ascii_letters, length=7):
return ''.join(list(retval))


def load_exploit_file(path, node="exploits"):
"""
load exploits from a given file
"""

selected_file_path = path

retval = []
try:
with open(selected_file_path) as exploit_file:
# loading it like this has been known to cause Unicode issues later on down
# the road
_json = json.loads(exploit_file.read())
for item in _json[node]:
# so we'll reload it into a ascii string before we save it into the file
retval.append(str(item))
except IOError as e:
lib.settings.close(e)
return retval


def load_exploits(path, node="exploits"):
"""
load exploits from a given path, depending on how many files are loaded into
Expand Down
35 changes: 24 additions & 11 deletions lib/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,33 @@ def check_services(service_name):
return True



def write_to_file(data_to_write, filename, mode="a+"):
def write_to_file(data_to_write, filename, mode=None):
"""
write data to a specified file, if it exists, ask to overwrite
"""
global stop_animation

if os.path.exists(filename):
stop_animation = True
is_append = lib.output.prompt("would you like to (a)ppend or (o)verwrite the file")
if is_append == "o":
mode = "w"
elif is_append != "a":
lib.output.warning("invalid input provided ('{}'), appending to file".format(is_append))
if not mode:
stop_animation = True
is_append = lib.output.prompt("would you like to (a)ppend or (o)verwrite the file")
if is_append.lower() == "o":
mode = "w"
elif is_append.lower() == "a":
mode = "a+"
else:
lib.output.error("invalid input provided ('{}'), appending to file".format(is_append))
lib.output.error("Search results NOT SAVED!")

if mode == "w":
lib.output.warning("Overwriting to {}".format(filename))
if mode == "a":
lib.output.info("Appending to {}".format(filename))

else:
# File does not exists, mode does not matter
mode = "w"

with open(filename, mode) as log:
if isinstance(data_to_write, (tuple, set, list)):
for item in list(data_to_write):
Expand All @@ -132,7 +145,7 @@ def write_to_file(data_to_write, filename, mode="a+"):
return filename


def load_api_keys(path="{}/etc/tokens".format(CUR_DIR)):
def load_api_keys(unattended=False, path="{}/etc/tokens".format(CUR_DIR)):

"""
load the API keys from their .key files
Expand All @@ -156,8 +169,8 @@ def load_api_keys(path="{}/etc/tokens".format(CUR_DIR)):
else:
lib.output.info("{} API token loaded from {}".format(key.title(), API_KEYS[key][0]))
api_tokens = {
"censys": (open(API_KEYS["censys"][0]).read(), open(API_KEYS["censys"][1]).read()),
"shodan": (open(API_KEYS["shodan"][0]).read(), )
"censys": (open(API_KEYS["censys"][0]).read().rstrip(), open(API_KEYS["censys"][1]).read().rstrip()),
"shodan": (open(API_KEYS["shodan"][0]).read().rstrip(), )
}
return api_tokens

Expand Down