Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
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("--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("--append", action="store_true", dest="appendHosts",
help="When specified, append discovered hosts to the host file.")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't have shorthands

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.")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! That's perfect thank you

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)))
18 changes: 18 additions & 0 deletions lib/jsonize.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ 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 = []
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))
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(), )
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question, why .rstrip() why not just strip it? (idc either way, just curious)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reasons. I had problems with copy-pasta and dangling spaces / CR-LF vs LF endings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay that makes sense. Thought there was some magic I didn't know about lol.

return api_tokens

Expand Down