diff --git a/api_calls/honeyscore_hook.py b/api_calls/honeyscore_hook.py index 93f7917..cc773c8 100644 --- a/api_calls/honeyscore_hook.py +++ b/api_calls/honeyscore_hook.py @@ -1,5 +1,4 @@ import requests -from bs4 import BeautifulSoup class HoneyHook(object): diff --git a/autosploit/main.py b/autosploit/main.py index 6543054..b72d1a5 100644 --- a/autosploit/main.py +++ b/autosploit/main.py @@ -24,6 +24,7 @@ EXPLOIT_FILES_PATH, START_SERVICES_PATH, save_error_to_file, + stop_animation ) from lib.jsonize import ( load_exploits, @@ -115,6 +116,10 @@ def main(): terminal = AutoSploitTerminal(loaded_tokens, loaded_exploits) terminal.terminal_main_display(loaded_tokens) except Exception as e: + global stop_animation + + stop_animation = True + import traceback print( @@ -128,4 +133,3 @@ def main(): error_class = str(e.__class__).split(" ")[1].split(".")[1].strip(">").strip("'") error_file = save_error_to_file(str(error_traceback), str(e), error_class) request_issue_creation(error_file, hide_sensitive(), str(e)) - diff --git a/dryrun_autosploit.sh b/drysploit.sh similarity index 85% rename from dryrun_autosploit.sh rename to drysploit.sh index 1089632..288a931 100644 --- a/dryrun_autosploit.sh +++ b/drysploit.sh @@ -1,15 +1,16 @@ #!/usr/bin/env bash +# +# this script dryruns autosploit. That's it, nothing special just a dry run +# + if [[ $# -lt 1 ]]; then echo "Syntax:" - echo -e "\t./dryrun_autosploit.sh [whitelist]" + echo -e "\t./drysploit.sh [whitelist]" exit 1 fi -echo -e "[!] Make sure you are not on your localhost while running this script, press enter to continue"; -read - WHITELIST=$2 SEARCH_QUERY=$1 LPORT=4444 diff --git a/etc/json/default_fuzzers.json b/etc/json/default_fuzzers.json index b606973..5eb3b82 100644 --- a/etc/json/default_fuzzers.json +++ b/etc/json/default_fuzzers.json @@ -1,6 +1,5 @@ { "exploits": [ - "auxiliary/fuzzers/dns/dns_fuzzer", "auxiliary/fuzzers/ftp/client_ftp", "auxiliary/fuzzers/ftp/ftp_pre_post", "auxiliary/fuzzers/http/http_form_field", diff --git a/etc/text_files/ethics.lst b/etc/text_files/ethics.lst index fcdf3ca..6fdd371 100644 --- a/etc/text_files/ethics.lst +++ b/etc/text_files/ethics.lst @@ -11,4 +11,5 @@ "My fear is that this has magnified the attack surface, and made it so that every exposed service on the internet will be scanned and probed on a near-constant basis by an entirely new set of attackers." "The release of tools like these exponentially expands the threat landscape by allowing a wider group of hackers to launch global attacks at will" "Good to know we’ve weaponized for the masses. Everyone can now be a script kiddie simply by plugging, playing and attacking." -"The fact that something is really easy, does not make unauthorized computer access any less a crime. And tools like this leave a forensic footprint that is miles wide. Yes, you can compromise poorly protected systems very easily with this tool, but you can also end up in a lot of trouble." \ No newline at end of file +"The fact that something is really easy, does not make unauthorized computer access any less a crime. And tools like this leave a forensic footprint that is miles wide. Yes, you can compromise poorly protected systems very easily with this tool, but you can also end up in a lot of trouble." +"I can't believe it's not skidware!" \ No newline at end of file diff --git a/etc/text_files/nmap_options.lst b/etc/text_files/nmap_options.lst new file mode 100644 index 0000000..4456386 --- /dev/null +++ b/etc/text_files/nmap_options.lst @@ -0,0 +1,108 @@ +-iL +-iR +--exclude +--excludefile +-sL +-sn +-Pn +-PS +-PA +-PU +-PY +-PE +-PP +-PM +-PO +-n +-R +--dns-servers +--system-dns +--traceroute +-sS +-sT +-sA +-sW +-sM +-sU +-sN +-sF +-sX +--scanflags +-sI +-sY +-sZ +-sO +-b +-p +--exclude-ports +-F +-r +--top-ports +--port-ratio +-sV +--version-intensity +--version-light +--version-all +--version-trace +-sC +--script +--script-args +--script-args-file +--script-trace +--script-updatedb +--script-help +-O +--osscan-limit +--osscan-guess +-T +--min-hostgroup +--max-hostgroup +--min-parallelism +--max-parallelism +--min-rtt-timeout +--max-rtt-timeout +--initial-rtt-timeout +--max-retries +--host-timeout +--scan-delay +--max-scan-delay +--min-rate +--max-rate +-f +--mtu +-D +-S +-e +-g +--source-port +--proxies +--data +--data-string +--data-length +--ip-options +--ttl +--spoof-mac +--badsum +-oN +-oX +-oS +-oG +-oA +-v +-d +--reason +--open +--packet-trace +--iflist +--append-output +--resume +--stylesheet +--webxml +--no-stylesheet +-6 +-A +--datadir +--send-eth/--send-ip +--privileged +--unprivileged +-V \ No newline at end of file diff --git a/install.sh b/install.sh index a92582a..a4ef8a9 100755 --- a/install.sh +++ b/install.sh @@ -88,7 +88,7 @@ function install () { installOSX; ;; *) - echo "Unable to detect operating system that is compatible with AutoSploit..."; + echo "Unable to detect an operating system that is compatible with AutoSploit..."; ;; esac echo ""; diff --git a/lib/banner.py b/lib/banner.py index 808fea0..2588164 100644 --- a/lib/banner.py +++ b/lib/banner.py @@ -1,7 +1,7 @@ import os import random -VERSION = "3.1.2" +VERSION = "3.1.5" def banner_1(line_sep="#--", space=" " * 30): diff --git a/lib/cmdline/cmd.py b/lib/cmdline/cmd.py index 493f3de..647e8b3 100644 --- a/lib/cmdline/cmd.py +++ b/lib/cmdline/cmd.py @@ -25,10 +25,10 @@ def optparser(): """ parser = argparse.ArgumentParser( - usage="python autosploit.py -[c|z|s|a] -[q] QUERY\n" - "{spacer}[-C] WORKSPACE LHOST LPORT [-e] [--whitewash] PATH\n" - "{spacer}[--ruby-exec] [--msf-path] PATH [-E] EXPLOIT-FILE-PATH\n" - "{spacer}[--rand-agent] [--proxy] PROTO://IP:PORT [-P] AGENT".format( + usage="python autosploit.py -c[z|s|a] -q QUERY [-O|A]\n" + "{spacer}[-C WORKSPACE LHOST LPORT] [-e] [--whitewash PATH] [-H]\n" + "{spacer}[--ruby-exec] [--msf-path] PATH [-E EXPLOIT-FILE-PATH]\n" + "{spacer}[--rand-agent] [--proxy PROTO://IP:PORT] [-P AGENT] [-D QUERY,QUERY,..]".format( spacer=" " * 28 ) ) @@ -42,8 +42,10 @@ def optparser(): 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( + "-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.") @@ -65,7 +67,7 @@ def optparser(): 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.") + help="msfconsole will never be called when this flag is passed") exploit.add_argument("-f", "--exploit-file-to-use", metavar="PATH", dest="exploitFile", help="Run AutoSploit with provided exploit JSON file.") exploit.add_argument("-H", "--is-honeypot", type=float, default=1000, dest="checkIfHoneypot", metavar="HONEY-SCORE", @@ -79,7 +81,7 @@ def optparser(): misc.add_argument("--ethics", action="store_true", dest="displayEthics", help=argparse.SUPPRESS) # easter egg! misc.add_argument("--whitelist", metavar="PATH", dest="whitelist", - help="only exploit hosts listed in the whitelist file") + help="only exploit hosts listed in the whitelist file") misc.add_argument("-D", "--download", nargs="+", metavar="SEARCH1 SEARCH2 ...", dest="downloadModules", help="download new exploit modules with a provided search flag") opts = parser.parse_args() diff --git a/lib/creation/ip_generator.py b/lib/creation/ip_generator.py new file mode 100644 index 0000000..40218af --- /dev/null +++ b/lib/creation/ip_generator.py @@ -0,0 +1,66 @@ +import socket +import itertools + +from multiprocessing import Pool + + +def generate_ip_range(selected_range): + """ + generate an IP address range from each provided node. + for example `10.0.1-10.1-10` will return a generator + object that has IP `10.0.1.1 - 10.0.10.10` in it + """ + octets = selected_range.split(".") + chunks = [map(int, octet.split("-")) for octet in octets] + ranges = [range(c[0], c[1] + 1) if len(c) == 2 else c for c in chunks] + for address in itertools.product(*ranges): + yield ".".join(map(str, address)) + + +def check_ip_alive(ip): + """ + efficiently check if an IP address is alive or not + by using the socket.gethostbyaddr function + """ + def is_valid_ip(ip): + try: + socket.inet_aton(ip) + return True + except: + return False + + try: + if not is_valid_ip(ip): + return False + else: + return socket.gethostbyaddr(ip) + except socket.herror: + return False + + +def check_ip_wrapper(generated_ips, limit=250): + """ + multiprocess the check_ip_alive function in order + to proces a large amount of IP addresses quickly + """ + alive_ips = [] + ips_to_use = [] + i = 0 + proc_pool = Pool(processes=35) + + for ip in generated_ips: + ips_to_use.append(ip) + i += 1 + if i == limit: + break + for ip in ips_to_use: + try: + result = proc_pool.apply_async(check_ip_alive, args=(ip,)).get() + if not result: + pass + else: + alive_ips.append(ip) + except Exception: + pass + proc_pool.close() + return alive_ips diff --git a/lib/creation/issue_creator.py b/lib/creation/issue_creator.py index 092b4a2..3b7f0c0 100644 --- a/lib/creation/issue_creator.py +++ b/lib/creation/issue_creator.py @@ -78,7 +78,7 @@ def check_version_number(current_version): try: req = requests.get("https://raw.githubusercontent.com/NullArray/AutoSploit/master/lib/banner.py") available_version = version_checker.search(req.content).group().split("=")[-1].split('"')[1] - if available_version != current_version: + if available_version > current_version: return False return True except Exception: @@ -168,15 +168,25 @@ def hide_sensitive(): args = sys.argv for item in sys.argv: if item in sensitive: - # TODO:/ we need to block the IP addresses in the -C argument - try: - item_index = args.index(item) + 1 - hidden = ''.join([x.replace(x, "*") for x in str(args[item_index])]) - args.pop(item_index) - args.insert(item_index, hidden) + if item in ["-C", "--config"]: + try: + item_index = args.index("-C") + 1 + except ValueError: + item_index = args.index("--config") + 1 + for _ in range(3): + hidden = ''.join([x.replace(x, '*') for x in str(args[item_index])]) + args.pop(item_index+_) + args.insert(item_index, hidden) return ' '.join(args) - except: - return ' '.join([item for item in sys.argv]) + else: + try: + item_index = args.index(item) + 1 + hidden = ''.join([x.replace(x, "*") for x in str(args[item_index])]) + args.pop(item_index) + args.insert(item_index, hidden) + return ' '.join(args) + except: + return ' '.join([item for item in sys.argv]) def request_issue_creation(path, arguments, error_message): diff --git a/lib/errors.py b/lib/errors.py index bab8acc..af2fc58 100644 --- a/lib/errors.py +++ b/lib/errors.py @@ -1 +1,7 @@ -class AutoSploitAPIConnectionError(Exception): pass \ No newline at end of file +class AutoSploitAPIConnectionError(Exception): pass + + +class NmapNotFoundException(Exception): pass + + +class NmapScannerError(Exception): pass \ No newline at end of file diff --git a/lib/exploitation/exploiter.py b/lib/exploitation/exploiter.py index b8a8786..f5863a8 100644 --- a/lib/exploitation/exploiter.py +++ b/lib/exploitation/exploiter.py @@ -89,14 +89,13 @@ def start_exploit(self, sep="*" * 10): report_path = path.join(current_run_path, "report.csv") with open(report_path, 'w') as f: csv_file = csv.writer(f, quoting=csv.QUOTE_ALL) - csv_file.writerow(['Target Host', - 'Date (UTC)', - 'MSF Module', - "LocalHost", - "Listening Port", - "Successful Logs", - "Failure Logs", - "All Logs"]) + csv_file.writerow( + [ + 'Target Host', 'Date (UTC)', 'MSF Module', + "LocalHost", "Listening Port", "Successful Logs", + "Failure Logs", "All Logs" + ] + ) lib.output.info("Launching exploits against {hosts_len} hosts:".format(hosts_len=len(self.hosts))) @@ -110,20 +109,24 @@ def start_exploit(self, sep="*" * 10): if self.check_honey: lib.output.misc_info("checking if {} is a honeypot".format(host)) honey_score = api_calls.honeyscore_hook.HoneyHook(host, self.shodan_token).make_request() - if honey_score >= self.compare_honey: + if honey_score < self.compare_honey: lib.output.warning( "honeypot score ({}) is above (or equal to) requested, skipping target".format(honey_score) ) skip = True skip_amount += 1 else: + lib.output.misc_info("{} does not appear to be a honeypot, continuing attack".format(host)) skip = False else: skip = False if not skip: current_host_path = path.join(current_run_path, host.strip()) - makedirs(current_host_path) + try: + makedirs(current_host_path) + except OSError: + pass for mod in self.mods: if not self.dry_run: @@ -187,12 +190,11 @@ def start_exploit(self, sep="*" * 10): ansi_escape = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]') msf_output_lines = [ansi_escape.sub('', x) for x in output if re.search('\[.\]', x)] - msf_wins = [x for x in msf_output_lines if re.search('\[\+\]', x) or - 'Meterpreter' in x or - 'Session' in x or - 'Sending stage' in x] - - msf_fails = [x for x in msf_output_lines if re.search('\[-\]', x)] + msf_wins = [ + x for x in msf_output_lines if re.search('\[\+\]', x) or + 'Meterpreter' in x or 'Session' in x or 'Sending stage' in x + ] + msf_fails = [x for x in msf_output_lines if re.search('\[-\]', x) and 'Background' not in x] if len(msf_wins): win_total += 1 @@ -200,14 +202,10 @@ def start_exploit(self, sep="*" * 10): fail_total += 1 csv_file = csv.writer(f, quoting=csv.QUOTE_ALL) - csv_file.writerow([rhost, - today_printable, - module_name, - lhost, - lport, - linesep.join(msf_wins), - linesep.join(msf_fails), - linesep.join(msf_output_lines)]) + csv_file.writerow([ + rhost, today_printable, module_name, lhost, lport, + linesep.join(msf_wins), linesep.join(msf_fails), linesep.join(msf_output_lines) + ]) print("") lib.output.info("{}RESULTS{}".format(sep, sep)) diff --git a/lib/scanner/__init__.py b/lib/scanner/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/scanner/nmap.py b/lib/scanner/nmap.py new file mode 100644 index 0000000..3daab79 --- /dev/null +++ b/lib/scanner/nmap.py @@ -0,0 +1,218 @@ +""" + +********************************************************************************************* +* NOTICE FROM AUTOSPLOIT DEVELOPERS * +********************************************************************************************* +* this is basically an exact copy of * +* `https://github.com/komand/python-nmap/blob/master/nmap/nmap.py` that has been modified * +* to better fit into autosploits development. There has been very minimal changes to it * +* and it still basically functions the exact same way * +********************************************************************************************* + + +ORIGINAL INFO: +-------------- +nmap.py - version and date, see below +Source code : https://bitbucket.org/xael/python-nmap +Author : +* Alexandre Norman - norman at xael.org +Contributors: +* Steve 'Ashcrow' Milner - steve at gnulinux.net +* Brian Bustin - brian at bustin.us +* old.schepperhand +* Johan Lundberg +* Thomas D. maaaaz +* Robert Bost +* David Peltier +Licence: GPL v3 or any later version for python-nmap +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program. If not, see . +************** +IMPORTANT NOTE +************** +The Nmap Security Scanner used by python-nmap is distributed +under it's own licence that you can find at https://svn.nmap.org/nmap/COPYING +Any redistribution of python-nmap along with the Nmap Security Scanner +must conform to the Nmap Security Scanner licence + +__author__ = 'Alexandre Norman (norman@xael.org)' +__version__ = '0.6.2' +__last_modification__ = '2017.01.07' +""" + +import os +import shlex +import subprocess + +from xml.etree import ElementTree + +import lib.jsonize +import lib.errors +import lib.output +import lib.settings + + +def write_xml_data(host, output): + if not os.path.exists(lib.settings.NMAP_XML_OUTPUT_BACKUP): + os.makedirs(lib.settings.NMAP_XML_OUTPUT_BACKUP) + file_path = "{}/{}_{}.xml".format( + lib.settings.NMAP_XML_OUTPUT_BACKUP, str(host), lib.jsonize.random_file_name(length=10) + ) + with open(file_path, 'a+') as results: + results.write(output) + return file_path + + +def find_nmap(search_paths): + for path in search_paths: + try: + _ = subprocess.Popen([path, '-V'], bufsize=10000, stdout=subprocess.PIPE, close_fds=True) + except OSError: + pass + else: + return path + raise lib.errors.NmapNotFoundException + + +def do_scan(host, nmap_path, ports=None, arguments=None): + if arguments is None: + arguments = "-sV" + arguments_list = shlex.split(arguments) + launch_arguments = [ + nmap_path, '-oX', '-', host, + '-p ' + ports if ports is not None else "", + ] + arguments_list + to_launch = [] + for item in launch_arguments: + if not item == "": + to_launch.append(item) + lib.output.info("launching nmap scan against {} ({})".format(host, " ".join(to_launch))) + process = subprocess.Popen( + launch_arguments, bufsize=10000, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + output, error = process.communicate() + output_data = bytes.decode(output) + nmap_error = bytes.decode(error) + nmap_error_tracestack = [] + nmap_warn_tracestack = [] + if len(nmap_error) > 0: + for line in nmap_error.split(os.linesep): + if len(line) != 0: + if lib.settings.NMAP_ERROR_REGEX_WARNING.search(line) is not None: + nmap_warn_tracestack.append(line + os.linesep) + else: + nmap_error_tracestack.append(line + os.linesep) + path = write_xml_data(host, output_data) + lib.output.misc_info("a copy of the output has been saved to: {}".format(path)) + return output_data, "".join(nmap_warn_tracestack), "".join(nmap_error_tracestack) + + +def parse_xml_output(output, warnings, error): + results = {} + try: + root = ElementTree.fromstring(output) + except Exception: + if len(error) != 0: + raise lib.errors.NmapScannerError(error) + else: + raise lib.errors.NmapScannerError(output) + results['nmap_scan'] = { + 'full_command_line': root.get('args'), + 'scan_information': {}, + 'scan_stats': { + 'time_string': root.find('runstats/finished').get('timestr'), + 'elapsed': root.find('runstats/finished').get('elapsed'), + 'hosts_up': root.find('runstats/hosts').get('up'), + 'down_hosts': root.find('runstats/hosts').get('down'), + 'total_hosts_scanned': root.find('runstats/hosts').get('total') + } + } + if len(error) != 0: + results['nmap_scan']['scan_information']['errors'] = error + if len(warnings) != 0: + results['nmap_scan']['scan_information']['warnings'] = warnings + for info in root.findall('scaninfo'): + results['nmap_scan']['scan_information'][info.get('protocol')] = { + 'method': info.get('type'), + 'services': info.get('services') + } + for attempted_host in root.findall('host'): + host = None + addresses = {} + vendors = {} + for address in attempted_host.findall("address"): + address_type = address.get('addrtype') + addresses[address_type] = address.get('addr') + if address_type == "ipv4": + host = addresses[address_type] + elif address_type == "mac" and address.get('vendor') is not None: + vendors[addresses[address_type]] = address.get('vendor') + if host is None: + host = attempted_host.find('address').get('addr') + hostnames = [] + if len(attempted_host.findall('hostnames/hostname')) != 0: + for current_hostnames in attempted_host.findall('hostnames/hostname'): + hostnames.append({ + 'hostname': current_hostnames.get('name'), + 'host_type': current_hostnames.get('type') + }) + else: + hostnames.append({ + 'hostname': None, + 'host_type': None + }) + + results['nmap_scan'][host] = {} + results['nmap_scan'][host]['hostnames'] = hostnames + results['nmap_scan'][host]['addresses'] = addresses + results['nmap_scan'][host]['vendors'] = vendors + + for status in attempted_host.findall('status'): + results['nmap_scan'][host]['status'] = { + 'state': status.get('state'), + 'reason': status.get('reason') + } + for uptime in attempted_host.findall('uptime'): + results['nmap_scan'][host]['uptime'] = { + 'seconds': uptime.get('seconds'), + 'lastboot': uptime.get('lastboot') + } + for discovered_port in attempted_host.findall('ports/port'): + protocol = discovered_port.get('protocol') + port_number = discovered_port.get('portid') + port_state = discovered_port.find('state').get('state') + port_reason = discovered_port.find('state').get('reason') + + # this is actually a thing!! + name = discovered_config = discovered_version = extra_information = discovered_product = stuff = "" + for discovered_name in discovered_port.findall('service'): + name = discovered_name.get('name') + if discovered_name.get('product'): + discovered_product = discovered_name.get('product') + if discovered_name.get('version'): + discovered_version = discovered_name.get('version') + if discovered_name.get('extrainfo'): + extra_information = discovered_name.get('extrainfo') + if discovered_name.get('conf'): + discovered_config = discovered_name.get('conf') + + for other_stuff in discovered_name.findall('cpe'): + stuff = other_stuff.text + if protocol not in results['nmap_scan'][host].keys(): + results['nmap_scan'][host][protocol] = list() + results['nmap_scan'][host][protocol].append({ + 'port': port_number, 'state': port_state, 'reason': port_reason, + 'name': name, 'product': discovered_product, 'version': discovered_version, + 'extrainfo': extra_information, 'conf': discovered_config, 'cpe': stuff + }) + + return results diff --git a/lib/settings.py b/lib/settings.py index 527807f..4b6666a 100644 --- a/lib/settings.py +++ b/lib/settings.py @@ -1,4 +1,5 @@ import os +import re import sys import time import socket @@ -50,11 +51,12 @@ def complete_text(self, text, state): exploit/run/attack Run the exploits on the already gathered hosts search/api/gather Search the API's for hosts exit/quit Exit the terminal session -single Load a single host into the file +single Load a single host into the file, or multiple hosts separated by a comma (1,2,3,..) personal/custom Load a custom host file tokens/reset Reset API tokens if needed external View loaded external commands ver[sion] View the current version of the program +clean/clear Clean the hosts.txt file of duplicate IP addresses help/? Display this help """ @@ -70,6 +72,20 @@ def complete_text(self, text, state): # autosploit command history file path HISTORY_FILE_PATH = "{}/.history".format(HOME) +# we'll save the scans output for future use +NMAP_XML_OUTPUT_BACKUP = "{}/nmap_scans".format(HOME) + +# regex to discover errors or warnings +NMAP_ERROR_REGEX_WARNING = re.compile("^warning: .*", re.IGNORECASE) + +# possible options in nmap +NMAP_OPTIONS_PATH = "{}/etc_text_files/nmap_opts.lst".format(CUR_DIR) + +# possible paths for nmap +NMAP_POSSIBLE_PATHS = ( + 'nmap', '/usr/bin/nmap', '/usr/local/bin/nmap', '/sw/bin/nmap', '/opt/local/bin/nmap' +) + # link to the checksums CHECKSUM_LINK = open("{}/etc/text_files/checksum_link.txt".format(CUR_DIR)).read() @@ -89,7 +105,8 @@ def complete_text(self, text, state): # one bash script to rule them all takes an argument via the operating system START_SERVICES_PATH = "{}/etc/scripts/start_services.sh".format(CUR_DIR) -RC_SCRIPTS_PATH = "{}/autosploit_out/".format(CUR_DIR) +# path where we will keep the rc scripts +RC_SCRIPTS_PATH = "{}/autosploit_out/".format(HOME) # path to the file that will contain our query QUERY_FILE_PATH = tempfile.NamedTemporaryFile(delete=False).name @@ -293,15 +310,18 @@ def cmdline(command, is_msf=True): split_cmd = [x.strip() for x in command.split(" ") if x] sys.stdout.flush() - - proc = Popen(split_cmd, stdout=PIPE, bufsize=1) stdout_buff = [] - for stdout_line in iter(proc.stdout.readline, b''): - stdout_buff += [stdout_line.rstrip()] - if is_msf: - print("(msf)>> {}".format(stdout_line).rstrip()) - else: - print("{}".format(stdout_line).rstrip()) + + try: + proc = Popen(split_cmd, stdout=PIPE, bufsize=1) + for stdout_line in iter(proc.stdout.readline, b''): + stdout_buff += [stdout_line.rstrip()] + if is_msf: + print("(msf)>> {}".format(stdout_line).rstrip()) + else: + print("{}".format(stdout_line).rstrip()) + except OSError as e: + stdout_buff += "ERROR: " + e return stdout_buff diff --git a/lib/term/terminal.py b/lib/term/terminal.py index 2d7081c..ce7f232 100644 --- a/lib/term/terminal.py +++ b/lib/term/terminal.py @@ -45,6 +45,8 @@ class object for the main terminal of the program "reset", "tokens", # show the version number "ver", "version", + # clean the hosts file of duplicate IP's + "clean", "clear", # easter eggs! "idkwhatimdoing", "ethics", "skid" ] @@ -64,7 +66,7 @@ def __init__(self, tokens, modules): self.modules = modules try: self.loaded_hosts = open(lib.settings.HOST_FILE).readlines() - except IOError: + except (IOError, Exception): lib.output.warning("no hosts file present") self.loaded_hosts = open(lib.settings.HOST_FILE, "a+").readlines() @@ -148,6 +150,25 @@ def do_terminal_command(self, command): """ lib.settings.cmdline(command, is_msf=False) + def do_clean_hosts(self): + """ + Clean the hosts.txt file of any duplicate IP addresses + """ + retval = set() + current_size = len(self.loaded_hosts) + for host in self.loaded_hosts: + retval.add(host) + cleaned_size = len(retval) + with open(lib.settings.HOST_FILE, 'w') as hosts: + for item in list(retval): + hosts.write(item) + if current_size != cleaned_size: + lib.output.info("cleaned {} duplicate IP address(es) (total of {})".format( + current_size - cleaned_size, cleaned_size + ) + ) + self.__reload() + def do_token_reset(self, api, token, username): """ Explanation: @@ -166,6 +187,12 @@ def do_token_reset(self, api, token, username): Censys -> reset/tokens censys Shodan -> reset.tokens shodan """ + import sys + + if sys.version_info > (3,): + token = token.encode("utf-8") + username = username.encode("utf-8") + if api.lower() == "censys": lib.output.info("resetting censys API credentials") with open(lib.settings.API_KEYS["censys"][0], 'w') as token_: @@ -261,7 +288,7 @@ def do_api_search(self, requested_api_data, query, tokens): proxy=proxy, agent=agent ).search() - except lib.errors.AutoSploitAPIConnectionError as e: + except (lib.errors.AutoSploitAPIConnectionError, Exception) as e: lib.settings.stop_animation = True lib.output.error("error searching API: '{}', error message: '{}'".format(api, str(e))) lib.settings.stop_animation = True @@ -287,6 +314,7 @@ def do_add_single_host(self, ip): Explanation: ------------ Add a single host by IP address + Or a list of single hosts separatedd by a comma Parameters: ----------- @@ -294,15 +322,16 @@ def do_add_single_host(self, ip): Examples: --------- - single 89.76.12.124 + single 89.76.12.124[,89.76.12.43,89.90.65.78,...] """ - validated_ip = lib.settings.validate_ip_addr(ip) - if not validated_ip: - lib.output.error("provided IP '{}' is invalid, try again".format(ip)) - else: - with open(lib.settings.HOST_FILE, "a+") as hosts: - hosts.write(ip + "\n") - lib.output.info("host '{}' saved to hosts file".format(ip)) + for item in ip.split(","): + validated_ip = lib.settings.validate_ip_addr(item) + if not validated_ip: + lib.output.error("provided IP '{}' is invalid, try again".format(ip)) + else: + with open(lib.settings.HOST_FILE, "a+") as hosts: + hosts.write(item + "\n") + lib.output.info("host '{}' saved to hosts file".format(item)) def do_quit_terminal(self, save_history=True): """ @@ -475,6 +504,8 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): self.do_view_gathered() elif any(c in choice for c in ("ver", "version")): self.do_show_version_number() + elif any(c in choice for c in ("clean", "clear")): + self.do_clean_hosts() elif "single" in choice: try: if "help" in choice_data_list: @@ -495,7 +526,7 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): lib.output.error( "must provide at least LHOST, LPORT, workspace name with `{}` keyword " "(IE {} 127.0.0.1 9076 default [whitelist-path] [honeycheck])".format( - choice.strip(), choice.strip() + choice.split(" ")[0].strip(), choice.split(" ")[0].strip() ) ) else: @@ -537,7 +568,7 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): print(self.do_load_custom_hosts.__doc__) except TypeError: pass - if len(choice_data_list) == 1: + if choice_data_list is not None and len(choice_data_list) == 1: lib.output.error("must provide full path to file after `{}` keyword".format(choice)) else: self.do_load_custom_hosts(choice_data_list[-1]) @@ -551,7 +582,7 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): lib.output.error( "must provide a list of API names after `{}` keyword and query " "(IE {} shodan,censys apache2)".format( - choice.strip(), choice.strip() + choice.split(" ")[0].strip(), choice.split(" ")[0].strip() ) ) else: @@ -582,7 +613,7 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): lib.output.error( "must supply API name with `{}` keyword along with " "new token (IE {} shodan mytoken123 [userID (censys)])".format( - choice.strip(), choice.strip() + choice.split(" ")[0].strip(), choice.split(" ")[0].strip() ) ) else: diff --git a/quicksploit.sh b/quicksploit.sh new file mode 100755 index 0000000..ecad084 --- /dev/null +++ b/quicksploit.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# +# this script quickly runs a query list of search keywords provided from a file on ALL of the +# available APIs. (Censys, Zoomeye, and Shodan) from there it will save all of them to the hosts.txt +# file and you can do as you will with that +# + +function doQuick() { + for item in $(cat $1); do python autosploit.py -A -a -f etc/json/default_modules.json -q $item; done +} + +function helpPage() { + echo "./quicksploit.sh FILENAME"; + exit 1; +} + +function main() { + if [[ $EUID -ne 0 ]]; then + echo "[!] must run script as root!"; + exit 1; + elif [[ ! -f $1 ]]; then + helpPage; + else + echo "[+] starting quicksploit searching!"; + doQuick $1; + fi +} + +main $@; \ No newline at end of file diff --git a/run_autosploit.sh b/runsploit.sh similarity index 72% rename from run_autosploit.sh rename to runsploit.sh index 9dfcdaf..df4c026 100755 --- a/run_autosploit.sh +++ b/runsploit.sh @@ -1,9 +1,15 @@ #!/bin/bash +# +# this script runs autosploit with default configs and default modules +# protip be on a VPS when you run this because it's gonna start an attack +# right away +# + if [[ $# -lt 1 ]]; then echo "Syntax:" - echo -e "\t./run_autosploit.sh PORT [WHITELIST]" + echo -e "\t./runsploit.sh PORT [WHITELIST]" exit 1 fi