From 05eef3c6adc93098572e5e24d7c161e92f0f1482 Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 29 Oct 2018 10:28:22 -0500 Subject: [PATCH 01/14] fixes issue #224 and issue #221, deperecates some code that has to do with macOS which is no longer needed, removes the error_file after it has been used (if it has been used) --- autosploit/main.py | 4 +--- etc/scripts/start_services.sh | 11 ----------- lib/creation/issue_creator.py | 5 +++++ lib/exploitation/exploiter.py | 6 +++++- lib/term/terminal.py | 7 ++++++- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/autosploit/main.py b/autosploit/main.py index 151c15e..d68f003 100644 --- a/autosploit/main.py +++ b/autosploit/main.py @@ -69,9 +69,7 @@ def main(): ) if choice.lower().startswith("y"): try: - if "darwin" in platform_running.lower(): - cmdline("{} darwin".format(START_SERVICES_PATH)) - elif "linux" in platform_running.lower(): + if "linux" in platform_running.lower(): cmdline("{} linux".format(START_SERVICES_PATH)) else: close("your platform is not supported by AutoSploit at this time", status=2) diff --git a/etc/scripts/start_services.sh b/etc/scripts/start_services.sh index 161888a..b34f143 100755 --- a/etc/scripts/start_services.sh +++ b/etc/scripts/start_services.sh @@ -11,21 +11,10 @@ function startPostgreSQLLinux () { sudo systemctl start postgresql > /dev/null 2>&1 } -function startApacheOSX () { - sudo apachectl start > /dev/null 2>&1 -} - -function startPostgreSQLOSX () { - brew services restart postgresql > /dev/null 2>&1 -} - function main () { if [ $1 == "linux" ]; then startApacheLinux; startPostgreSQLLinux; - elif [ $1 == "darwin" ]; then - startApacheOSX; - startPostgreSQLOSX; else echo "[*] invalid operating system"; fi diff --git a/lib/creation/issue_creator.py b/lib/creation/issue_creator.py index b67041b..4b0cb95 100644 --- a/lib/creation/issue_creator.py +++ b/lib/creation/issue_creator.py @@ -1,4 +1,5 @@ import re +import os import sys import json import platform @@ -165,5 +166,9 @@ def request_issue_creation(path, arguments, error_message): lib.output.error( "someone has already created this issue here: {}".format(find_url(identifier)) ) + try: + os.remove(path) + except: + pass else: lib.output.info("the issue has been logged to a file in path: '{}'".format(path)) \ No newline at end of file diff --git a/lib/exploitation/exploiter.py b/lib/exploitation/exploiter.py index 1c675c5..23f0add 100644 --- a/lib/exploitation/exploiter.py +++ b/lib/exploitation/exploiter.py @@ -78,7 +78,11 @@ def start_exploit(self, sep="*" * 10): today_printable = datetime.datetime.today().strftime("%Y-%m-%d_%Hh%Mm%Ss") current_run_path = path.join(lib.settings.RC_SCRIPTS_PATH, today_printable) - makedirs(current_run_path) + try: + makedirs(current_run_path) + except OSError: + current_run_path = path.join(lib.settings.RC_SCRIPTS_PATH, today_printable + "(1)") + makedirs(current_run_path) report_path = path.join(current_run_path, "report.csv") with open(report_path, 'w') as f: diff --git a/lib/term/terminal.py b/lib/term/terminal.py index cccf24c..e2d7dc8 100644 --- a/lib/term/terminal.py +++ b/lib/term/terminal.py @@ -274,7 +274,12 @@ def custom_host_list(self, mods): option 3 must be provided """ provided_host_file = lib.output.prompt("enter the full path to your host file", lowercase=False) - self.exploit_gathered_hosts(mods, hosts=provided_host_file) + if provided_host_file == "": + lib.output.error("you provided a blank hosts file, did you mean to?") + lib.output.info("defaulting to default hosts file (press CNTRL-C to go back and try again)") + self.exploit_gathered_hosts(mods, hosts=self.host_path) + else: + self.exploit_gathered_hosts(mods, hosts=provided_host_file) def terminal_main_display(self, loaded_mods): """ From 9eaee79a177ca052ddf04e0351adcefd9db624a9 Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 29 Oct 2018 10:28:42 -0500 Subject: [PATCH 02/14] bumps the version number --- lib/banner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/banner.py b/lib/banner.py index a53debe..2385de1 100644 --- a/lib/banner.py +++ b/lib/banner.py @@ -1,7 +1,7 @@ import os import random -VERSION = "2.2.3" +VERSION = "2.2.4" def banner_1(line_sep="#--", space=" " * 30): From 6b37171877ec2586dbe773f503696b498c1470ae Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 29 Oct 2018 10:52:04 -0500 Subject: [PATCH 03/14] added to the end of the rc file, this will complete the exit if an exploit works --- lib/exploitation/exploiter.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/exploitation/exploiter.py b/lib/exploitation/exploiter.py index 23f0add..ad9e33c 100644 --- a/lib/exploitation/exploiter.py +++ b/lib/exploitation/exploiter.py @@ -131,14 +131,14 @@ def start_exploit(self, sep="*" * 10): "set rhost {rhost}\n" "set rhosts {rhosts}\n" "run -z\n" - "exit\n" + "exit -y\n" ) - module_name=mod.strip() - workspace=self.configuration[0] - lhost=self.configuration[1] - lport=self.configuration[2] - rhost=host.strip() + module_name = mod.strip() + workspace = self.configuration[0] + lhost = self.configuration[1] + lport = self.configuration[2] + rhost = host.strip() current_rc_script_path = path.join(current_host_path, mod.replace("/", '-').strip()) with open(current_rc_script_path, 'w') as f: From e5a45a084c7c328eb0d2e42fa5a2d6b323688409 Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 29 Oct 2018 11:30:00 -0500 Subject: [PATCH 04/14] minor updates --- .gitignore | 1 - lib/banner.py | 2 +- lib/cmdline/cmd.py | 20 +++++++++++++++++++- lib/settings.py | 20 ++++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 46e0072..302e263 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ uid.p etc/tokens/* autosploit_out/* venv/* -etc/json/* diff --git a/lib/banner.py b/lib/banner.py index 2385de1..8622824 100644 --- a/lib/banner.py +++ b/lib/banner.py @@ -1,7 +1,7 @@ import os import random -VERSION = "2.2.4" +VERSION = "2.2.5" def banner_1(line_sep="#--", space=" " * 30): diff --git a/lib/cmdline/cmd.py b/lib/cmdline/cmd.py index 467cd5a..817ea96 100644 --- a/lib/cmdline/cmd.py +++ b/lib/cmdline/cmd.py @@ -141,7 +141,25 @@ def single_run_args(opt, keys, loaded_modules): "You should take this ethical lesson into consideration " "before you continue with the use of this tool:\n\n{}\n".format(ethic)) if opt.downloadModules is not None: - print "downloading MODULES!" + import re + + modules_to_download = opt.downloadModules + links_list = "{}/etc/text_files/links.txt".format(lib.settings.CUR_DIR) + possibles = open(links_list).readlines() + for module in modules_to_download: + searcher = re.compile("{}".format(module)) + for link in possibles: + if searcher.search(link) is not None: + filename = lib.settings.download_modules(link.strip()) + download_filename = "{}.json".format(link.split("/")[-1].split(".")[0]) + download_path = "{}/etc/json".format(os.getcwd()) + current_files = os.listdir(download_path) + if download_filename not in current_files: + full_path = "{}/{}".format(download_path, download_filename) + lib.jsonize.text_file_to_dict(filename, filename=full_path) + lib.output.info("downloaded into: {}".format(download_path)) + else: + lib.output.warning("file already downloaded, skipping") if opt.exploitList: try: lib.output.info("converting {} to JSON format".format(opt.exploitList)) diff --git a/lib/settings.py b/lib/settings.py index 5fa540b..e3981aa 100644 --- a/lib/settings.py +++ b/lib/settings.py @@ -328,3 +328,23 @@ def save_error_to_file(error_info, error_message, error_class): "Traceback (most recent call):\n " + error_info.strip() + "\n{}: {}".format(error_class, error_message) ) return file_path + + +def download_modules(link): + import re + import requests + import tempfile + + lib.output.info('downloading: {}'.format(link)) + retval = "" + req = requests.get(link) + content = req.content + split_data = content.split(" ") + searcher = re.compile("exploit/\w+/\w+") + storage_file = tempfile.NamedTemporaryFile(delete=False) + for item in split_data: + if searcher.search(item) is not None: + retval += item + "\n" + with open(storage_file.name, 'a+') as tmp: + tmp.write(retval) + return storage_file.name From ffc71a38e99cfbe78d2109fea1f25d5af455a554 Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 29 Oct 2018 11:30:45 -0500 Subject: [PATCH 05/14] oops --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 302e263..46e0072 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ uid.p etc/tokens/* autosploit_out/* venv/* +etc/json/* From 4fa0e20a46a6cb5ccaeb187e4f234adc92c0e901 Mon Sep 17 00:00:00 2001 From: Steven Aldinger Date: Sun, 2 Dec 2018 12:07:31 -0500 Subject: [PATCH 06/14] update dependencies in docker (#269) --- .github/.translations/README-de.md | 5 +++-- .github/.translations/README-fr.md | 5 +++-- Docker/Dockerfile | 24 +++++++++++++++--------- README.md | 5 +++-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/.translations/README-de.md b/.github/.translations/README-de.md index e158bb1..4d28d97 100644 --- a/.github/.translations/README-de.md +++ b/.github/.translations/README-de.md @@ -152,7 +152,7 @@ misc arguments: Falls du AutoSploit auf einem System mit macOS ausführen willst, musst du das Programm trotz der Kompatibilität mit macOS in einer virtuellen Maschine ausführen, sodass es erfolgreich ausgeführt werden kann. Um dies zu tun, sind folgende Schritte nötig; ```bash -sudo -s << '_EOF' +sudo -s << '_EOF' pip2 install virtualenv --user git clone https://github.com/NullArray/AutoSploit.git virtualenv @@ -173,12 +173,13 @@ AutoSploit benötigt die folgenden Python 2.7 Module: ``` requests psutil +beautifulsoup4 ``` Wenn dir auffällt, dass du diese nicht installiert hast, kannst du sie über Pip installieren, wie nachfolgend gezeigt. ```bash -pip install requests psutil +pip install requests psutil beautifulsoup4 ``` oder diff --git a/.github/.translations/README-fr.md b/.github/.translations/README-fr.md index 05e28e1..e9c4c50 100644 --- a/.github/.translations/README-fr.md +++ b/.github/.translations/README-fr.md @@ -3,7 +3,7 @@ Comme vous pouvez l'imaginer au vu du nom de ce projet, AutoSploit automatise l'exploitation d'hôtes distantes connectées à internet. Les adresses des hôtes à attaquer sont collectées automatiquement grâce à l'aide de Shodan, Censys et Zoomeye. Vous pouvez également utiliser vos propres listes de cibles. Les modules Metasploit disponibles ont été sélectionnés afin de faciliter l'obtention d'exécution de code à distance ( Remote Code Execution, ou RCE ), qui permettent ensuite de créer des sessions terminal inversées ( reverse shell ) ou meterpreter ( via metasploit ). -**Ne soyez pas stupides** +**Ne soyez pas stupides** Recevoir les connexions de vos victimes directement sur votre ordinateur n'est pas vraiment une bonne idée. Vous devriez considérer l'option de dépenser quelques euros dans un VPS ( ou VPN ). @@ -127,12 +127,13 @@ AutoSploit exige la présence des modules Python2.7 suivants. ``` requests psutil +beautifulsoup4 ``` Si vous ne les avez pas, vous pouvez les installer avec les commandes ci-dessous ( dans le dossier d'AutoSploit ): ```bash -pip install requests psutil +pip install requests psutil beautifulsoup4 ``` ou diff --git a/Docker/Dockerfile b/Docker/Dockerfile index ae03a59..ad482c8 100644 --- a/Docker/Dockerfile +++ b/Docker/Dockerfile @@ -1,17 +1,23 @@ FROM kalilinux/kali-linux-docker -RUN apt update && apt install -y postgresql \ - apache2 \ - python-pip \ - python-dev \ - build-essential \ - git \ - metasploit-framework +RUN apt update \ + && apt install -y \ + apache2 \ + build-essential \ + git \ + metasploit-framework \ + postgresql \ + python-dev \ + python-pip + +RUN git clone https://github.com/NullArray/AutoSploit.git \ + && pip install -r AutoSploit/requirements.txt -RUN git clone https://github.com/NullArray/AutoSploit.git && pip install requests psutil COPY database.yml /root/.msf4/database.yml + WORKDIR AutoSploit + EXPOSE 80 443 4444 ENTRYPOINT ["python", "autosploit.py"] -#ENTRYPOINT ["bash"] +# ENTRYPOINT ["bash"] diff --git a/README.md b/README.md index 7b88d63..1721b4e 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ misc arguments: If you want to run AutoSploit on a macOS system, AutoSploit is compatible with macOS, however, you have to be inside a virtual environment for it to run successfully. To do this, do the following; ```bash -sudo -s << '_EOF' +sudo -s << '_EOF' pip2 install virtualenv --user git clone https://github.com/NullArray/AutoSploit.git virtualenv @@ -173,12 +173,13 @@ AutoSploit depends on the following Python2.7 modules. ``` requests psutil +beautifulsoup4 ``` Should you find you do not have these installed get them with pip like so. ```bash -pip install requests psutil +pip install requests psutil beautifulsoup4 ``` or From 37cf7ab43bd606642fd22f7631797014923af133 Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 31 Dec 2018 19:42:15 -0600 Subject: [PATCH 07/14] complete rewrite of terminal, now has history and commands along with the ability to run external commands, fixes all issues with bug tag --- README.md | 28 +- api_calls/censys.py | 2 +- api_calls/shodan.py | 2 +- api_calls/zoomeye.py | 2 +- autosploit/main.py | 4 +- etc/text_files/gen | 8 + lib/banner.py | 2 +- lib/cmdline/cmd.py | 12 +- lib/settings.py | 125 +++++++- lib/term/terminal.py | 728 +++++++++++++++++++++++++------------------ 10 files changed, 563 insertions(+), 350 deletions(-) create mode 100644 etc/text_files/gen diff --git a/README.md b/README.md index 1721b4e..45c7189 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ sudo -s << EOF git clone https://github.com/NullArray/AutoSploit.git cd AutoSploit chmod +x install.sh -./installsh +./install.sh cd AutoSploit/Docker docker network create -d bridge haknet docker run --network haknet --name msfdb -e POSTGRES_PASSWORD=s3cr3t -d postgres @@ -64,7 +64,7 @@ chmod +x install.sh ./install.sh ``` -If you want to run AutoSploit on a macOS system, AutoSploit is compatible with macOS, however, you have to be inside a virtual environment for it to run successfully. To do this, do the following; +AutoSploit is compatible with macOS, however, you have to be inside a virtual environment for it to run successfully. In order to accomplish this employ/perform the below operations via the terminal or in the form of a shell script. ```bash sudo -s << '_EOF' @@ -149,21 +149,6 @@ misc arguments: --whitelist PATH only exploit hosts listed in the whitelist file ``` -If you want to run AutoSploit on a macOS system, AutoSploit is compatible with macOS, however, you have to be inside a virtual environment for it to run successfully. To do this, do the following; - -```bash -sudo -s << '_EOF' -pip2 install virtualenv --user -git clone https://github.com/NullArray/AutoSploit.git -virtualenv -source /bin/activate -cd -pip2 install -r requirements.txt -chmod +x install.sh -./install.sh -python autosploit.py -_EOF -``` ## Dependencies _Note_: All dependencies should be installed using the above installation method, however, if you find they are not: @@ -173,13 +158,12 @@ AutoSploit depends on the following Python2.7 modules. ``` requests psutil -beautifulsoup4 ``` Should you find you do not have these installed get them with pip like so. ```bash -pip install requests psutil beautifulsoup4 +pip install requests psutil ``` or @@ -192,9 +176,11 @@ Since the program invokes functionality from the Metasploit Framework you need t ## Acknowledgements -Special thanks to [Ekultek](https://github.com/Ekultek) without whoms contributions to the project version 2.0 would have been a lot less spectacular. +Special thanks to [Ekultek](https://github.com/Ekultek) without whoms contributions to the project, version 2.0 would have been a lot less spectacular. + +Thanks to [Khast3x](https://github.com/khast3x) for setting up Docker support. -And thanks to [Khast3x](https://github.com/khast3x) for setting up Docker support. +Last but certainly not least. Thanks to all who have submitted Pull Requests, bug reports, useful and productive contributions in general. ### Active Development diff --git a/api_calls/censys.py b/api_calls/censys.py index 2a91842..1c29d3e 100644 --- a/api_calls/censys.py +++ b/api_calls/censys.py @@ -24,7 +24,7 @@ def __init__(self, identity=None, token=None, query=None, proxy=None, agent=None self.host_file = HOST_FILE self.save_mode = save_mode - def censys(self): + def search(self): """ connect to the Censys API and pull all IP addresses from the provided query """ diff --git a/api_calls/shodan.py b/api_calls/shodan.py index ff8b68f..5db7a1a 100644 --- a/api_calls/shodan.py +++ b/api_calls/shodan.py @@ -25,7 +25,7 @@ def __init__(self, token=None, query=None, proxy=None, agent=None, save_mode=Non self.host_file = HOST_FILE self.save_mode = save_mode - def shodan(self): + def search(self): """ connect to the API and grab all IP addresses associated with the provided query """ diff --git a/api_calls/zoomeye.py b/api_calls/zoomeye.py index eea3b01..6bc2232 100644 --- a/api_calls/zoomeye.py +++ b/api_calls/zoomeye.py @@ -54,7 +54,7 @@ def __get_auth(self): token = json.loads(req.content) return token - def zoomeye(self): + def search(self): """ connect to the API and pull all the IP addresses that are associated with the given query diff --git a/autosploit/main.py b/autosploit/main.py index d68f003..3839ab5 100644 --- a/autosploit/main.py +++ b/autosploit/main.py @@ -117,8 +117,8 @@ def main(): loaded_exploits = load_exploits(EXPLOIT_FILES_PATH) info("attempting to load API keys") loaded_tokens = load_api_keys() - terminal = AutoSploitTerminal(loaded_tokens) - terminal.terminal_main_display(loaded_exploits) + terminal = AutoSploitTerminal(loaded_tokens, loaded_exploits) + terminal.terminal_main_display(loaded_tokens) except Exception as e: import traceback diff --git a/etc/text_files/gen b/etc/text_files/gen new file mode 100644 index 0000000..f2d3a1b --- /dev/null +++ b/etc/text_files/gen @@ -0,0 +1,8 @@ +usage of AutoSploit for attacking targets without prior mutual consent is illegal in pretty much every sense of the word. it is the end user's responsibility to obey all applicable local, state, and federal laws. developers assume no liability and are not responsible for any misuse or damage caused by this program. please take these considerations into mind: + + - use AutoSploit on a VPS through a proxy or Tor + - keep calm and wipe the logs or use tools to do so + - never connect from your local IP address + - keep a low profile, the point of hacking is not to get caught + +we do not condone hacking of any sort, the above are tips to keep in mind for ethical purposes. having said that, knowledge is not illegal, and anybody that tells you learning is wrong is a fool. get as much out of this program as we got from writing it. \ No newline at end of file diff --git a/lib/banner.py b/lib/banner.py index 8622824..5567e5b 100644 --- a/lib/banner.py +++ b/lib/banner.py @@ -1,7 +1,7 @@ import os import random -VERSION = "2.2.5" +VERSION = "3.0" def banner_1(line_sep="#--", space=" " * 30): diff --git a/lib/cmdline/cmd.py b/lib/cmdline/cmd.py index 817ea96..4ea9cb7 100644 --- a/lib/cmdline/cmd.py +++ b/lib/cmdline/cmd.py @@ -187,33 +187,33 @@ def single_run_args(opt, keys, loaded_modules): keys["censys"][1], keys["censys"][0], opt.searchQuery, proxy=headers[0], agent=headers[1], save_mode=search_save_mode - ).censys() + ).search() if opt.searchZoomeye: lib.output.info(single_search_msg.format("Zoomeye")) api_searches[0]( opt.searchQuery, proxy=headers[0], agent=headers[1], save_mode=search_save_mode - ).zoomeye() + ).search() 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], save_mode=search_save_mode - ).shodan() + ).search() if opt.searchAll: lib.output.info("searching all search engines in order") api_searches[0]( opt.searchQuery, proxy=headers[0], agent=headers[1], save_mode=search_save_mode - ).zoomeye() + ).search() api_searches[1]( keys["shodan"][0], opt.searchQuery, proxy=headers[0], agent=headers[1], save_mode=search_save_mode - ).shodan() + ).search() api_searches[2]( keys["censys"][1], keys["censys"][0], opt.searchQuery, proxy=headers[0], agent=headers[1], save_mode=search_save_mode - ).censys() + ).search() if opt.startExploit: hosts = open(lib.settings.HOST_FILE).readlines() if opt.whitelist: diff --git a/lib/settings.py b/lib/settings.py index e3981aa..2488f09 100644 --- a/lib/settings.py +++ b/lib/settings.py @@ -6,6 +6,7 @@ import platform import getpass import tempfile +import readline import distutils.spawn from subprocess import ( PIPE, @@ -16,9 +17,56 @@ import lib.output import lib.banner +import lib.jsonize + +class AutoSploitCompleter(object): + + """ + object to create an auto completer for the terminal + """ + + def __init__(self, opts): + self.opts = sorted(opts) + self.possibles = [] + + def complete_text(self, text, state): + if state == 0: + if text: + self.possibles = [m for m in self.opts if m.startswith(text)] + else: + self.possibles = self.opts[:] + try: + return self.possibles[state] + except IndexError: + return None + + +TERMINAL_HELP_MESSAGE = """ +COMMAND: SUMMARY: +--------- -------- +view/show Show the already gathered hosts +mem[ory]/history Display the command history +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 +tokens/reset Reset API tokens if needed +help/? Display this help +""" + +# current directory CUR_DIR = "{}".format(os.getcwd()) +# home +HOME = "{}/.autosploit_home".format(os.path.expanduser("~")) + +# backup the current hosts file +HOST_FILE_BACKUP = "{}/backups".format(HOME) + +# autosploit command history file path +HISTORY_FILE_PATH = "{}/.history".format(HOME) + # path to the file containing all the discovered hosts HOST_FILE = "{}/hosts.txt".format(CUR_DIR) try: @@ -49,7 +97,7 @@ PLATFORM_PROMPT = "\n{}@\033[36mPLATFORM\033[0m$ ".format(getpass.getuser()) # the prompt that will be used most of the time -AUTOSPLOIT_PROMPT = "\n\033[31m{}\033[0m@\033[36mautosploit\033[0m# ".format(getpass.getuser()) +AUTOSPLOIT_PROMPT = "\033[31m{}\033[0m@\033[36mautosploit\033[0m# ".format(getpass.getuser()) # all the paths to the API tokens API_KEYS = { @@ -74,7 +122,7 @@ TOKEN_PATH = "{}/etc/text_files/auth.key".format(CUR_DIR) # location of error files -ERROR_FILES_LOCATION = "{}/.autosploit_errors".format(os.path.expanduser("~")) +ERROR_FILES_LOCATION = "{}/.autosploit_errors".format(HOME) # terminal options AUTOSPLOIT_TERM_OPTS = { @@ -83,14 +131,58 @@ 99: "quit" } +# global variable for the search animation stop_animation = False -def validate_ip_addr(provided): +def load_external_commands(): + """ + create a list of external commands from provided directories + """ + paths = ["/bin", "/usr/bin"] + loaded_externals = [] + for f in paths: + for cmd in os.listdir(f): + if not os.path.isdir("{}/{}".format(f, cmd)): + loaded_externals.append(cmd) + return loaded_externals + + +def backup_host_file(current, path): + """ + backup the current hosts file + """ + import datetime + import shutil + + if not os.path.exists(path): + os.makedirs(path) + new_filename = "{}/hosts_{}_{}.txt".format( + path, + lib.jsonize.random_file_name(length=17), + str(datetime.datetime.today()).split(" ")[0] + ) + shutil.copyfile(current, new_filename) + return new_filename + + +def auto_completer(keywords): + """ + function to initialize the auto complete utility + """ + completer = AutoSploitCompleter(keywords) + readline.set_completer(completer.complete_text) + readline.parse_and_bind('tab: complete') + + +def validate_ip_addr(provided, home_ok=False): """ validate an IP address to see if it is real or not """ - not_acceptable = ("0.0.0.0", "127.0.0.1", "255.255.255.255") + if not home_ok: + not_acceptable = ("0.0.0.0", "127.0.0.1", "255.255.255.255") + else: + not_acceptable = ("255.255.255.255",) if provided not in not_acceptable: try: socket.inet_aton(provided) @@ -186,7 +278,7 @@ def load_api_keys(unattended=False, path="{}/etc/tokens".format(CUR_DIR)): return api_tokens -def cmdline(command): +def cmdline(command, is_msf=True): """ send the commands through subprocess """ @@ -200,7 +292,10 @@ def cmdline(command): stdout_buff = [] for stdout_line in iter(proc.stdout.readline, b''): stdout_buff += [stdout_line.rstrip()] - print("(msf)>> {}".format(stdout_line).rstrip()) + if is_msf: + print("(msf)>> {}".format(stdout_line).rstrip()) + else: + print("{}".format(stdout_line).rstrip()) return stdout_buff @@ -331,6 +426,9 @@ def save_error_to_file(error_info, error_message, error_class): def download_modules(link): + """ + download new module links + """ import re import requests import tempfile @@ -348,3 +446,18 @@ def download_modules(link): with open(storage_file.name, 'a+') as tmp: tmp.write(retval) return storage_file.name + + +def find_similar(command, internal, external): + """ + find commands similar to the one provided + """ + retval = [] + first_char = command[0] + for inter in internal: + if inter.startswith(first_char): + retval.append(inter) + for exter in external: + if exter.startswith(first_char): + retval.append(exter) + return retval diff --git a/lib/term/terminal.py b/lib/term/terminal.py index e2d7dc8..472ed1f 100644 --- a/lib/term/terminal.py +++ b/lib/term/terminal.py @@ -1,13 +1,18 @@ import os -import sys +import datetime import lib.settings import lib.output import lib.errors -import lib.exploitation.exploiter +import lib.jsonize import api_calls.shodan import api_calls.zoomeye import api_calls.censys +import lib.exploitation.exploiter +try: + raw_input +except: + input = raw_input class AutoSploitTerminal(object): @@ -16,359 +21,460 @@ class AutoSploitTerminal(object): class object for the main terminal of the program """ - def __init__(self, tokens): + internal_terminal_commands = [ + # viewing gathered hosts + "view", "show", + # displaying memory + "mem", "memory", "history", + # attacking targets + "exploit", "run", "attack", + # search API's + "search", "api", "gather", + # quit the terminal + "exit", "quit", + # single hosts + "single", + # custom hosts list + "custom", "personal", + # display help + "?", "help", + # display external commands + "external", + # reset API tokens + "reset", "tokens", + # easter eggs! + "idkwhatimdoing", "ethics", "skid" + ] + external_terminal_commands = lib.settings.load_external_commands() + api_call_pointers = { + "shodan": api_calls.shodan.ShodanAPIHook, + "zoomeye": api_calls.zoomeye.ZoomEyeAPIHook, + "censys": api_calls.censys.CensysAPIHook + } + + def __init__(self, tokens, modules): + self.history = [] + self.quit_terminal = False self.tokens = tokens - self.usage_path = lib.settings.USAGE_AND_LEGAL_PATH - self.sep = "-" * 30 - self.host_path = lib.settings.HOST_FILE + self.history_dir = "{}/{}".format(lib.settings.HISTORY_FILE_PATH, datetime.date.today()) + self.full_history_path = "{}/autosploit.history".format(self.history_dir) + self.modules = modules try: - open(lib.settings.HOST_FILE).readlines() - except IOError: - lib.output.warning("no hosts file present, you need to gather some hosts") - self.host_path = lib.settings.HOST_FILE - - @staticmethod - def help_menu_full(): - seperator = "-" * 30 - help_choices = [ - (0, "usage"), - (1, "view"), - (2, "single"), - (3, "exit"), - (4, "gather"), - (5, "exploit"), - (6, "custom"), - ] - print("\n{}\nPossible help choices:".format(seperator)) - for i, _ in enumerate(help_choices): - print("{}- `help {}`".format(" " * 3, help_choices[i][1])) - print("{}\n".format(seperator)) + self.loaded_hosts = open(lib.settings.HOST_FILE).readlines() + except: + lib.output.warning("no hosts file present") + self.loaded_hosts = [] - def usage_and_legal(self): + def reflect_memory(self, max_memory=100): """ - shows a display of the output and legal information that resides - in the etc/text_files/general file. - - option 1 must be provided to display + reflect the command memory out of the history file """ - lib.output.info("preparing to display usage and legal") - with open(self.usage_path) as usage: - print(usage.read().strip()) + if os.path.exists(self.history_dir): + tmp = [] + try: + with open(self.full_history_path) as history: + for item in history.readlines(): + tmp.append(item.strip()) + except: + pass + if len(tmp) == 0: + lib.output.warning("currently no history") + elif len(tmp) > max_memory: + import shutil + + history_file_backup_path = "{}.{}.old".format( + self.full_history_path, + lib.jsonize.random_file_name(length=12) + ) + shutil.copy(self.full_history_path, history_file_backup_path) + os.remove(self.full_history_path) + open(self.full_history_path, 'a+').close() + lib.output.misc_info("history file to large, backed up under '{}'".format(history_file_backup_path)) + else: + for cmd in tmp: + self.history.append(cmd) - def help(self, command): + def do_display_history(self): """ - print the help of the commands + display the history from the history files """ - help_dict = { - "usage": self.usage_and_legal, - "view": self.view_gathered_hosts, - "single": self.add_single_host, - "exit": self.quit, - "gather": self.gather_hosts, - "exploit": self.exploit_gathered_hosts, - "custom": self.custom_host_list - } - for key in help_dict.keys(): - if command == key: - lib.output.info("help found for provided argument:") - print(self.sep) - print(help_dict[key].__doc__) - print(self.sep) - break - else: - lib.output.warning("unable to find help for provided command '{}'".format(command)) - lib.output.info("available helps '{}'".format( - ", ".join([k for k in help_dict.keys()]) - )) + for i, item in enumerate(self.history, start=1): + if len(list(str(i))) == 2: + spacer1, spacer2 = " ", " " + elif len(list(str(i))) == 3: + spacer1, spacer2 = " ", " " + else: + spacer1, spacer2 = " ", " " + print("{}{}{}{}".format(spacer1, i, spacer2, item)) - def view_gathered_hosts(self): + def get_choice(self): """ - print a list of all available hosts in the hosts.txt file - - option 5 must be provided + get the provided choice and return a tuple of options and the choice """ - lib.output.info("loading gathered hosts from '{}'".format(self.host_path)) + original_choice = raw_input(lib.settings.AUTOSPLOIT_PROMPT) try: - with open(self.host_path) as hosts: - for host in hosts.readlines(): - # should take care of some Unicode errors that occur - lib.output.info(str(host.strip())) - except IOError: - lib.output.warning("hosts file doesn't exist, looks like you haven't gathered any") + choice_checker = original_choice.split(" ")[0] + except: + choice_checker = original_choice + if choice_checker in self.internal_terminal_commands: + retval = ("internal", original_choice) + elif choice_checker in self.external_terminal_commands: + retval = ("external", original_choice) + else: + retval = ("unknown", original_choice) + return retval - def add_single_host(self): + def do_display_external(self): """ - add a singular host to the hosts.txt file and check if the host - will resolve to a true IP address, if it is not a true IP address - you will be re-prompted for an IP address - - option 4 must be provided + display all external commands """ - provided = False - while not provided: - new_host = lib.output.prompt("enter the host IP you wish to add", lowercase=False) - if not lib.settings.validate_ip_addr(new_host): - lib.output.warning("provided host does not appear to be a true IP, try again") - else: - with open(self.host_path, "a+") as hosts: - hosts.write(new_host + os.linesep) - lib.output.info("successfully wrote provided host to {}".format(self.host_path)) - break + print(" ".join(self.external_terminal_commands)) - def quit(self, status): + def do_terminal_command(self, command): """ - quits the terminal and exits the program entirely - - option 99 must be provided + run a terminal command """ - lib.output.error("aborting terminal session") - assert isinstance(status, int) - sys.exit(status) + lib.settings.cmdline(command, is_msf=False) - def gather_hosts(self, query, given_choice=None, proxy=None, agent=None): + def do_token_reset(self, api, token, username): """ - gather hosts from either Shodan, Zoomeye, Censys, or multiple - by providing a comma between integers. - - option 2 must be provided + reset the API tokens """ - choice_dict = { - 1: api_calls.shodan.ShodanAPIHook, - 2: api_calls.zoomeye.ZoomEyeAPIHook, - 3: api_calls.censys.CensysAPIHook - } - searching = False - if given_choice is None: - lib.output.info("please choose an API to gather from (choosing two or more " - "separate by comma IE; 1,2)") - for i, api in enumerate(lib.settings.API_URLS.keys(), start=1): - print("{}. {}".format(i, api.title())) - choice = raw_input(lib.settings.AUTOSPLOIT_PROMPT) + if api.lower() == "censys": + lib.output.info("resetting censys API credentials") + with open(lib.settings.API_KEYS["censys"][0], 'w') as token_: + token_.write(token) + with open(lib.settings.API_KEYS["censys"][1], 'w') as username_: + username_.write(username) else: - choice = given_choice - while not searching: - # TODO[2]:// bug in the animation, if the user chooses one search engine to search - # the animation does not stop when the user chooses a single search engine, instead - # the user will see the animation continuously until they either: - # A) exit the terminal - # B) search another search engine - try: - # something in here needs to change (see TODO[2]) - choice = int(choice) - if choice == 1: - choice_dict[choice]( - self.tokens["shodan"][0], query, proxy=proxy, agent=agent - ).shodan() - break - elif choice == 2: - choice_dict[choice](query, proxy=proxy, agent=agent).zoomeye() - break - elif choice == 3: - choice_dict[choice]( - self.tokens["censys"][1], self.tokens["censys"][0], query, - proxy=proxy, agent=agent - ).censys() - break - else: - lib.output.warning("invalid option provided, going back to main menu") - break - except (ValueError, KeyError): - if "," in choice: - for i in choice.split(","): - if int(i) in choice_dict.keys(): - self.gather_hosts(query, given_choice=int(i), proxy=proxy, agent=agent) - else: - lib.output.warning("invalid option, skipping") - break - break - else: - lib.output.warning("must be integer between 1-{} not string".format(len(lib.settings.API_URLS.keys()))) - self.gather_hosts(query, proxy=proxy, agent=agent) - except Exception as e: - lib.settings.stop_animation = True - lib.output.error("unable to search API got error: {}".format(str(e))) - break + with open(lib.settings.API_KEYS["shodan"][0], 'w') as token_: + token_.write(token) + lib.output.warning("program must be restarted for the new tokens to initialize") - def exploit_gathered_hosts(self, loaded_mods, hosts=None): + def do_api_search(self, requested_api_data, query, tokens, proxy=None, agent=None): """ - exploit already gathered hosts from the hosts.txt file - - option 6 must be provided + search the API's for hosts """ - ruby_exec = False - msf_path = None - whitelist_file = lib.output.prompt("specify full path to a whitelist file, otherwise hit enter", lowercase=False) - if hosts is None: - if whitelist_file is not "" and not whitelist_file.isspace(): - # If whitelist is specified, return a washed hosts list - host_file = lib.exploitation.exploiter.whitelist_wash(open(self.host_path).readlines(), whitelist_file) - else: - try: - host_file = open(self.host_path).readlines() - except Exception: - sys.stdout.flush() - lib.output.error("no host file is present, did you gather hosts?") - return - else: - if whitelist_file is not "" and not whitelist_file.isspace(): - # If whitelist is specified, return a washed hosts list - host_file = lib.exploitation.exploiter.whitelist_wash(open(hosts).readlines(), whitelist_file) + acceptable_api_names = ("shodan", "censys", "zoomeye") + api_checker = lambda l: all(i.lower() in acceptable_api_names for i in l) + + try: + if len(query) < 1: + query = "".join(query) else: - host_file = open(hosts).readlines() - if not lib.settings.check_for_msf(): - msf_path = lib.output.prompt( - "it appears that MSF is not in your PATH, provide the full path to msfconsole" - ) - ruby_exec = True - lib.output.info( - "you will need to do some configuration to MSF.\n" - "please keep in mind that sending connections back to " - "your local host is probably not a smart idea." - ) - configuration = ( - lib.output.prompt("enter your workspace name", lowercase=False), - lib.output.prompt("enter your LHOST", lowercase=False), - lib.output.prompt("enter your LPORT", lowercase=False) - ) - exploiter = lib.exploitation.exploiter.AutoSploitExploiter( - configuration, - loaded_mods, - hosts=host_file, - ruby_exec=ruby_exec, - msf_path=msf_path - ) + query = " ".join(query) + except: + query = query + + if query == "" or query.isspace(): + lib.output.warning("looks like you forgot the query") + return try: - sorted_mods = exploiter.sort_modules_by_query() - choice = lib.output.prompt( - "a total of {} modules have been sorted by relevance, would you like to display them[y/N]".format( - len(sorted_mods) - ) + api_list = requested_api_data.split(",") + except: + api_list = [requested_api_data] + prompt_for_save = len(open(lib.settings.HOST_FILE).readlines()) != 0 + if prompt_for_save: + save_mode = lib.output.prompt( + "would you like to [a]ppend or [o]verwrite the file[a/o]", lowercase=True ) + if save_mode.startswith("o"): + backup = lib.settings.backup_host_file(lib.settings.HOST_FILE, lib.settings.HOST_FILE_BACKUP) + lib.output.misc_info("current host file backed up under: '{}'".format(backup)) + save_mode = "w" + else: + if not any(save_mode.startswith(s) for s in ("a", "o")): + lib.output.misc_info("provided option is not valid, defaulting to 'a'") + save_mode = "a+" + else: + save_mode = "a+" - if not choice.lower().strip().startswith("y"): - mods = lib.output.prompt("use relevant modules[y/N]") - if mods.lower().startswith("n"): - lib.output.info( - "starting exploitation with all loaded modules (total of {})".format(len(loaded_mods))) - exploiter.start_exploit() - elif mods.lower().startswith("y"): - lib.output.info("starting exploitation with sorted modules (total of {})".format(len(sorted_mods))) - exploiter.start_exploit() + proxy = lib.output.prompt("enter your proxy or press enter for none", lowercase=False) + if proxy.isspace() or proxy == "": + proxy = {"http": "", "https": ""} + else: + proxy = {"http": proxy, "https": proxy} + agent = lib.output.prompt("use a [r]andom User-Agent or the [d]efault one[r/d]", lowercase=True) + if agent.startswith("r"): + agent = {"User-Agent": lib.settings.grab_random_agent()} + elif agent.startswith("d"): + agent = {"User-Agent": lib.settings.DEFAULT_USER_AGENT} + else: + lib.output.warning("invalid option, using default") + agent = {"User-Agent": lib.settings.DEFAULT_USER_AGENT} + for api in api_list: + res = api_checker([api]) + if not res: + lib.output.error( + "API: '{}' is not a valid API, will be skipped".format(api) + ) else: - exploiter.view_sorted() - mods = lib.output.prompt("use relevant modules[y/N]") - if mods.lower().startswith("n"): - lib.output.info( - "starting exploitation with all loaded modules (total of {})".format(len(loaded_mods))) - exploiter.start_exploit() - elif mods.lower().startswith("y"): - lib.output.info("starting exploitation with sorted modules (total of {})".format(len(sorted_mods))) - exploiter.start_exploit() - except AttributeError: - lib.output.warning("unable to sort modules by relevance") + with open(lib.settings.QUERY_FILE_PATH, "a+") as tmp: + tmp.write(query) + lib.output.info( + "starting search on API {} using query: '{}'".format(api, query) + ) + try: + self.api_call_pointers[api.lower()]( + token=tokens["shodan"][0] if api == "shodan" else tokens["censys"][0], + identity=tokens["censys"][1] if api == "censys" else "", + query=query, + save_mode=save_mode, + proxy=proxy, + agent=agent + ).search() + except lib.errors.AutoSploitAPIConnectionError as e: + lib.settings.stop_animation = True + lib.output.error("error searching API: '{}', error message: '{}'".format(api, str(e))) + lib.settings.stop_animation = True + + def do_display_usage(self): + """ + display the full help menu + """ + print(lib.settings.TERMINAL_HELP_MESSAGE) - def custom_host_list(self, mods): + def do_view_gathered(self): """ - provided a custom host list that will be used for exploitation + view the gathered hosts + """ + if len(self.loaded_hosts) != 0: + for host in self.loaded_hosts: + lib.output.info(host) + else: + lib.output.warning("currently no gathered hosts") - option 3 must be provided + def do_add_single_host(self, ip): """ - provided_host_file = lib.output.prompt("enter the full path to your host file", lowercase=False) - if provided_host_file == "": - lib.output.error("you provided a blank hosts file, did you mean to?") - lib.output.info("defaulting to default hosts file (press CNTRL-C to go back and try again)") - self.exploit_gathered_hosts(mods, hosts=self.host_path) + add a single host to the host file + """ + validated_ip = lib.settings.validate_ip_addr(ip) + if not validated_ip: + lib.output.error("provided IP '{}' is invalid, try again".format(ip)) else: - self.exploit_gathered_hosts(mods, hosts=provided_host_file) + with open(lib.settings.HOST_FILE, "a+") as hosts: + hosts.write(ip + "\n") + lib.output.info("host '{}' saved to hosts file".format(ip)) - def terminal_main_display(self, loaded_mods): + def do_quit_terminal(self, save_history=True): """ - main output of the terminal + quit the terminal and save the command history """ + self.quit_terminal = True + if save_history: + if not os.path.exists(self.history_dir): + os.makedirs(self.history_dir) + lib.output.misc_info("saving history") + with open(self.full_history_path, "a+") as hist: + for item in self.history: + hist.write(item + "\n") + lib.output.info("exiting terminal session") - def __config_headers(): - proxy = lib.output.prompt("enter your proxy (blank for none)", lowercase=False) - agent = lib.output.prompt( - "do you want to use a (p)ersonal user agent, a (r)andom one, or (d)efault" + def do_exploit_targets(self, workspace_info): + """ + exploit the already gathered targets + """ + if workspace_info[-1] is not None: + lib.output.misc_info("doing whitewash on hosts file") + lib.exploitation.exploiter.whitelist_wash( + open(lib.settings.HOST_FILE).readlines(), + workspace_info[-1] ) - if proxy == "" or proxy.isspace(): - proxy = None - if agent.lower().startswith("p"): - agent = lib.output.prompt("enter your User-Agent", lowercase=False) - elif agent.lower().startswith("r"): - agent = lib.settings.grab_random_agent() - elif agent.lower().startswith("d"): - agent = None + else: + if not lib.settings.check_for_msf(): + msf_path = lib.output.prompt( + "metasploit is not in your PATH, provide the full path to it", lowercase=False + ) + ruby_exec = True else: - lib.output.warning("invalid argument, default will be selected") - agent = None - proxy, agent = lib.settings.configure_requests(proxy=proxy, agent=agent) - return proxy, agent + msf_path = None + ruby_exec = False + + sort_mods = lib.output.prompt( + "sort modules by relevance to last query[y/N]", lowercase=True + ) + + try: + if sort_mods.lower().startswith("y"): + mods_to_use = lib.exploitation.exploiter.AutoSploitExploiter( + None, None + ).sort_modules_by_query() + else: + mods_to_use = self.modules + except Exception: + lib.output.error("error sorting modules defaulting to all") + mods_to_use = self.modules - selected = False + view_modules = lib.output.prompt("view sorted modules[y/N]", lowercase=True) + if view_modules.startswith("y"): + for mod in mods_to_use: + lib.output.misc_info(mod.strip()) + lib.output.prompt("press enter to start exploitation phase") + lib.output.info("starting exploitation phase") + lib.exploitation.exploiter.AutoSploitExploiter( + configuration=workspace_info[0:3], + all_modules=mods_to_use, + hosts=open(lib.settings.HOST_FILE).readlines(), + msf_path=msf_path, + ruby_exec=ruby_exec + ).start_exploit() + + def do_load_custom_hosts(self, file_path): + """ + load a custom hosts file + """ + import shutil try: - while not selected: - for i in lib.settings.AUTOSPLOIT_TERM_OPTS.keys(): - print("{}. {}".format(i, lib.settings.AUTOSPLOIT_TERM_OPTS[i].title())) - choice = raw_input(lib.settings.AUTOSPLOIT_PROMPT) - # TODO[3] this is ugly so it needs to change + open("{}".format(file_path)).close() + except Exception: + lib.output.error("file does not exist, check the path and try again") + return + lib.output.warning("overwriting hosts file with provided, and backing up current") + backup_path = lib.settings.backup_host_file(lib.settings.HOST_FILE, lib.settings.HOST_FILE_BACKUP) + shutil.copy(file_path, lib.settings.HOST_FILE) + lib.output.info("host file replaced, backup stored under '{}'".format(backup_path)) + self.loaded_hosts = open(lib.settings.HOST_FILE).readlines() + + def terminal_main_display(self, tokens, extra_commands=None, save_history=True): + """ + terminal main display + """ + if extra_commands is not None: + for command in extra_commands: + self.external_terminal_commands.append(command) + self.reflect_memory() + while not self.quit_terminal: + try: + lib.settings.auto_completer(self.internal_terminal_commands) try: - choice = int(choice) - if choice == 99: - print(self.sep) - self.quit(0) - print(self.sep) - elif choice == 6: - print(self.sep) - self.exploit_gathered_hosts(loaded_mods) - print(self.sep) - elif choice == 5: - print(self.sep) - self.view_gathered_hosts() - print(self.sep) - elif choice == 4: - print(self.sep) - self.add_single_host() - print(self.sep) - elif choice == 3: - print(self.sep) - self.custom_host_list(loaded_mods) - print(self.sep) - elif choice == 2: - print(self.sep) - query = lib.output.prompt("enter your search query", lowercase=False) - try: - with open(lib.settings.QUERY_FILE_PATH, "w") as _query: - _query.write(query) - except AttributeError: - import tempfile # oooops - filename = tempfile.NamedTemporaryFile(delete=False).name - with open(filename, "w") as _query: - _query.write(query) - lib.settings.QUERY_FILE_PATH = filename - proxy, agent = __config_headers() - # possibly needs to change here (see TODO[2]) - self.gather_hosts(query, proxy=proxy, agent=agent) - print(self.sep) - elif choice == 1: - print(self.sep) - self.usage_and_legal() - else: - lib.output.warning("invalid option provided") - except ValueError: - if not choice == "help": - if "help" in choice: - try: - help_arg = choice.split(" ") - self.help(help_arg[-1]) - except: - lib.output.error("choice must be integer not string") + choice_type, choice = self.get_choice() + if choice_type == "unknown": + sims = lib.settings.find_similar( + choice, + self.internal_terminal_commands, + self.external_terminal_commands + ) + if len(sims) != 0: + max_sims_display = 7 + print( + "no command '{}' found, but there {} {} similar command{}".format( + choice, + "are" if len(sims) > 1 else "is", + len(sims), + "s" if len(sims) > 1 else "" + ) + ) + if len(sims) > max_sims_display: + print("will only display top {} results".format(max_sims_display)) + for i, cmd in enumerate(sims, start=1): + if i == max_sims_display: + break + print(cmd) + print("{}: command not found".format(choice)) else: - lib.output.warning("option must be integer not string") - elif choice == "help": - AutoSploitTerminal.help_menu_full() + print("{} command not found".format(choice)) + self.history.append(choice) + elif choice_type == "external": + self.do_terminal_command(choice) + self.history.append(choice) + else: + try: + choice_data_list = choice.split(" ") + if choice_data_list[-1] == "": + choice_data_list = None + except: + choice_data_list = None + if any(c in choice for c in ("help", "?")): + self.do_display_usage() + elif any(c in choice for c in ("external",)): + self.do_display_external() + elif any(c in choice for c in ("history", "mem", "memory")): + self.do_display_history() + elif any(c in choice for c in ("exit", "quit")): + self.do_quit_terminal(save_history=save_history) + elif any(c in choice for c in ("view", "gathered")): + self.do_view_gathered() + elif "single" in choice: + if choice_data_list is None or len(choice_data_list) == 1: + lib.output.error("must provide host IP after `single` keyword (IE single 89.65.78.123)") + else: + self.do_add_single_host(choice_data_list[-1]) + elif any(c in choice for c in ("exploit", "run", "attack")): + if len(choice_data_list) < 4: + lib.output.error( + "must provide at least LHOST, LPORT, workspace name with `{}` keyword " + "(IE {} 127.0.0.1 9076 default [whitelist-path])".format( + choice, choice + ) + ) + else: + if lib.settings.validate_ip_addr(choice_data_list[1], home_ok=True): + try: + workspace = ( + choice_data_list[1], choice_data_list[2], + choice_data_list[3], choice_data_list[4] + ) + except IndexError: + workspace = ( + choice_data_list[1], choice_data_list[2], + choice_data_list[3], None + ) + self.do_exploit_targets(workspace) + else: + lib.output.warning( + "heuristics could not validate provided IP address, " + "did you type it right?" + ) + elif any(c in choice for c in ("personal", "custom")): + if 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]) + elif any(c in choice for c in ("search", "api", "gather")): + if len(choice_data_list) < 3: + lib.output.error( + "must provide a list of API names after `{}` keyword and query " + "(IE {} shodan,censys apache2)".format( + choice, choice + ) + ) + else: + self.do_api_search(choice_data_list[1], choice_data_list[2:], tokens) + elif any(c in choice for c in ("idkwhatimdoing", "ethics", "skid")): + import random + + if choice == "ethics" or choice == "idkwhatimdoing": + ethics_file = "{}/etc/text_files/ethics.lst".format(os.getcwd()) + other_file = "{}/etc/text_files/gen".format(os.getcwd()) + with open(ethics_file) as ethics: + ethic = random.choice(ethics.readlines()).strip() + lib.output.info("take this ethical lesson into consideration before proceeding:") + print("\n{}\n".format(ethic)) + lib.output.warning(open(other_file).read()) + else: + lib.output.warning("hack to learn, don't learn to hack") + elif any(c in choice for c in ("tokens", "reset")): + acceptable_api_names = ("shodan", "censys") - except KeyboardInterrupt: - print("\n") - self.terminal_main_display(loaded_mods) + if len(choice_data_list) < 3: + lib.output.error( + "must supply API name with `{}` keyword along with " + "new token (IE {} shodan mytoken123 [userID (censys)])".format( + choice, choice + ) + ) + else: + if choice_data_list[1].lower() in acceptable_api_names: + try: + api, token, username = choice_data_list[1], choice_data_list[2], choice_data_list[3] + except IndexError: + api, token, username = choice_data_list[1], choice_data_list[2], None + self.do_token_reset(api, token, username) + else: + lib.output.error("cannot reset {} API credentials".format(choice)) + self.history.append(choice) + except KeyboardInterrupt: + lib.output.warning("use the `exit/quit` command to end terminal session") + except IndexError: + pass \ No newline at end of file From 90d551683ce35b6e5fc298f41991a24cc1eadecd Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 31 Dec 2018 19:58:18 -0600 Subject: [PATCH 08/14] minor update when viewing hosts --- lib/term/terminal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/term/terminal.py b/lib/term/terminal.py index 472ed1f..508ac06 100644 --- a/lib/term/terminal.py +++ b/lib/term/terminal.py @@ -240,7 +240,7 @@ def do_view_gathered(self): """ if len(self.loaded_hosts) != 0: for host in self.loaded_hosts: - lib.output.info(host) + lib.output.info(host.strip()) else: lib.output.warning("currently no gathered hosts") From 3556b9e2e6412da43303b1f124c53342b51da7ff Mon Sep 17 00:00:00 2001 From: ekultek Date: Mon, 31 Dec 2018 20:00:27 -0600 Subject: [PATCH 09/14] adding 'external' to the commands help --- lib/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/settings.py b/lib/settings.py index 2488f09..12cc946 100644 --- a/lib/settings.py +++ b/lib/settings.py @@ -52,6 +52,7 @@ def complete_text(self, text, state): exit/quit Exit the terminal session single Load a single host into the file tokens/reset Reset API tokens if needed +external View loaded external commands help/? Display this help """ From c6b7c5c02e1b941c6d132164d21742b1591a875e Mon Sep 17 00:00:00 2001 From: ekultek Date: Tue, 1 Jan 2019 00:04:44 -0600 Subject: [PATCH 10/14] missing an option --- lib/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/settings.py b/lib/settings.py index 12cc946..37a1f6a 100644 --- a/lib/settings.py +++ b/lib/settings.py @@ -51,6 +51,7 @@ def complete_text(self, text, state): search/api/gather Search the API's for hosts exit/quit Exit the terminal session single Load a single host into the file +personal/custom Load a custom host file tokens/reset Reset API tokens if needed external View loaded external commands help/? Display this help From 4f88c18c6631a0eb0f66e3117cc6057097c2e9a3 Mon Sep 17 00:00:00 2001 From: ekultek Date: Tue, 1 Jan 2019 01:24:14 -0600 Subject: [PATCH 11/14] created a way to get help on a specific command by typing command help --- autosploit/main.py | 4 -- lib/term/terminal.py | 91 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/autosploit/main.py b/autosploit/main.py index 3839ab5..60396d3 100644 --- a/autosploit/main.py +++ b/autosploit/main.py @@ -109,10 +109,6 @@ def main(): 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 type `help` to view the help menus" - ) misc_info("checking if there are multiple exploit files") loaded_exploits = load_exploits(EXPLOIT_FILES_PATH) info("attempting to load API keys") diff --git a/lib/term/terminal.py b/lib/term/terminal.py index 508ac06..2be4cf0 100644 --- a/lib/term/terminal.py +++ b/lib/term/terminal.py @@ -138,7 +138,21 @@ def do_terminal_command(self, command): def do_token_reset(self, api, token, username): """ - reset the API tokens + Explanation: + ------------ + Reset the API tokens when needed, this will overwrite the existing + API token with a provided one + + Parameters: + ----------- + :param api: name of the API to reset + :param token: the token that will overwrite the current token + :param username: if resetting Censys this will be the user ID token + + Examples: + --------- + Censys -> reset/tokens censys + Shodan -> reset.tokens shodan """ if api.lower() == "censys": lib.output.info("resetting censys API credentials") @@ -151,9 +165,21 @@ def do_token_reset(self, api, token, username): token_.write(token) lib.output.warning("program must be restarted for the new tokens to initialize") - def do_api_search(self, requested_api_data, query, tokens, proxy=None, agent=None): + def do_api_search(self, requested_api_data, query, tokens): """ - search the API's for hosts + Explanation: + ------------ + Search the API with a provided query for potentially exploitable hosts. + + Parameters: + ----------- + :param requested_api_data: data to be used with the API tuple of info + :param query: the query to be searched + :param tokens: an argument dict that will contain the token information + + Examples: + --------- + search/api/gather shodan[,censys[,zoomeye]] windows 10 """ acceptable_api_names = ("shodan", "censys", "zoomeye") api_checker = lambda l: all(i.lower() in acceptable_api_names for i in l) @@ -246,7 +272,17 @@ def do_view_gathered(self): def do_add_single_host(self, ip): """ - add a single host to the host file + Explanation: + ------------ + Add a single host by IP address + + Parameters: + ----------- + :param ip: IP address to be added + + Examples: + --------- + single 89.76.12.124 """ validated_ip = lib.settings.validate_ip_addr(ip) if not validated_ip: @@ -272,7 +308,17 @@ def do_quit_terminal(self, save_history=True): def do_exploit_targets(self, workspace_info): """ - exploit the already gathered targets + Explanation: + ------------ + Exploit the already gathered hosts inside of the hosts.txt file + + Parameters: + ----------- + :param workspace_info: a tuple of workspace information + + Examples: + --------- + exploit/run/attack 127.0.0.1 9065 default [whitewash list] """ if workspace_info[-1] is not None: lib.output.misc_info("doing whitewash on hosts file") @@ -321,7 +367,19 @@ def do_exploit_targets(self, workspace_info): def do_load_custom_hosts(self, file_path): """ - load a custom hosts file + Explanation: + ----------- + Load a custom exploit file, this is useful to attack already gathered hosts + instead of trying to gather them again from the backup host files inside + of the `.autosploit_home` directory + + Parameters: + ----------- + :param file_path: the full path to the loadable hosts file + + Examples: + --------- + custom/personal /some/path/to/myfile.txt """ import shutil @@ -340,6 +398,12 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): """ terminal main display """ + lib.output.warning( + "no arguments have been passed, dropping into terminal session. " + "to get help type `help` to quit type `exit/quit` to get help on " + "a specific command type `command help`" + ) + if extra_commands is not None: for command in extra_commands: self.external_terminal_commands.append(command) @@ -385,7 +449,7 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): choice_data_list = None except: choice_data_list = None - if any(c in choice for c in ("help", "?")): + if choice == "?" or choice == "help": self.do_display_usage() elif any(c in choice for c in ("external",)): self.do_display_external() @@ -396,11 +460,16 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): elif any(c in choice for c in ("view", "gathered")): self.do_view_gathered() elif "single" in choice: + if "help" in choice_data_list: + print(self.do_add_single_host.__doc__) + if choice_data_list is None or len(choice_data_list) == 1: lib.output.error("must provide host IP after `single` keyword (IE single 89.65.78.123)") else: self.do_add_single_host(choice_data_list[-1]) elif any(c in choice for c in ("exploit", "run", "attack")): + if "help" in choice_data_list: + print(self.do_exploit_targets.__doc__) if len(choice_data_list) < 4: lib.output.error( "must provide at least LHOST, LPORT, workspace name with `{}` keyword " @@ -427,11 +496,16 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): "did you type it right?" ) elif any(c in choice for c in ("personal", "custom")): + if "help" in choice_data_list: + print(self.do_load_custom_hosts.__doc__) if 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]) elif any(c in choice for c in ("search", "api", "gather")): + if "help" in choice_data_list: + print(self.do_api_search.__doc__) + if len(choice_data_list) < 3: lib.output.error( "must provide a list of API names after `{}` keyword and query " @@ -457,6 +531,9 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): elif any(c in choice for c in ("tokens", "reset")): acceptable_api_names = ("shodan", "censys") + if "help" in choice_data_list: + print(self.do_token_reset.__doc__) + if len(choice_data_list) < 3: lib.output.error( "must supply API name with `{}` keyword along with " From e97e992d2e8ab2e21d467e0b838b3cb4fdc5b202 Mon Sep 17 00:00:00 2001 From: ekultek Date: Tue, 1 Jan 2019 01:30:13 -0600 Subject: [PATCH 12/14] fixing some exceptions to be more acceptable with pep coding style --- lib/exploitation/exploiter.py | 2 +- lib/jsonize.py | 2 +- lib/term/terminal.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/exploitation/exploiter.py b/lib/exploitation/exploiter.py index ad9e33c..a175611 100644 --- a/lib/exploitation/exploiter.py +++ b/lib/exploitation/exploiter.py @@ -29,7 +29,7 @@ def whitelist_wash(hosts, whitelist_file): washed_hosts.append(host) return washed_hosts - except Exception: + except IOError: lib.output.warning("unable to whitewash host list, does the file exist?") return hosts diff --git a/lib/jsonize.py b/lib/jsonize.py index 5613baf..bcf6ec1 100644 --- a/lib/jsonize.py +++ b/lib/jsonize.py @@ -58,7 +58,7 @@ def load_exploits(path, node="exploits"): try: selected_file = file_list[int(action) - 1] selected = True - except Exception: + except Except: lib.output.warning("invalid selection ('{}'), select from below".format(action)) selected = False else: diff --git a/lib/term/terminal.py b/lib/term/terminal.py index 2be4cf0..198aabf 100644 --- a/lib/term/terminal.py +++ b/lib/term/terminal.py @@ -385,7 +385,7 @@ def do_load_custom_hosts(self, file_path): try: open("{}".format(file_path)).close() - except Exception: + except IOError: lib.output.error("file does not exist, check the path and try again") return lib.output.warning("overwriting hosts file with provided, and backing up current") From 5206721dcbbc4d559af28174a28428d96d18cba9 Mon Sep 17 00:00:00 2001 From: ekultek Date: Tue, 1 Jan 2019 01:48:59 -0600 Subject: [PATCH 13/14] created a __reload functiont hat reloads the hosts from the host file, upped the length of the backup hosts file --- lib/settings.py | 2 +- lib/term/terminal.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/settings.py b/lib/settings.py index 37a1f6a..078c8b4 100644 --- a/lib/settings.py +++ b/lib/settings.py @@ -161,7 +161,7 @@ def backup_host_file(current, path): os.makedirs(path) new_filename = "{}/hosts_{}_{}.txt".format( path, - lib.jsonize.random_file_name(length=17), + lib.jsonize.random_file_name(length=22), str(datetime.datetime.today()).split(" ")[0] ) shutil.copyfile(current, new_filename) diff --git a/lib/term/terminal.py b/lib/term/terminal.py index 198aabf..a871e01 100644 --- a/lib/term/terminal.py +++ b/lib/term/terminal.py @@ -61,9 +61,12 @@ def __init__(self, tokens, modules): self.modules = modules try: self.loaded_hosts = open(lib.settings.HOST_FILE).readlines() - except: + except IOError: lib.output.warning("no hosts file present") - self.loaded_hosts = [] + self.loaded_hosts = open(lib.settings.HOST_FILE, "a+").readlines() + + def __reload(self): + self.loaded_hosts = open(lib.settings.HOST_FILE).readlines() def reflect_memory(self, max_memory=100): """ @@ -551,6 +554,7 @@ def terminal_main_display(self, tokens, extra_commands=None, save_history=True): else: lib.output.error("cannot reset {} API credentials".format(choice)) self.history.append(choice) + self.__reload() except KeyboardInterrupt: lib.output.warning("use the `exit/quit` command to end terminal session") except IndexError: From 3cefb7262a9c4781ca0685b1e1e0b1f60dd1fa5f Mon Sep 17 00:00:00 2001 From: NullArray Date: Wed, 2 Jan 2019 10:44:25 +0000 Subject: [PATCH 14/14] Update gen --- etc/text_files/gen | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/etc/text_files/gen b/etc/text_files/gen index f2d3a1b..6fe17af 100644 --- a/etc/text_files/gen +++ b/etc/text_files/gen @@ -1,8 +1,18 @@ -usage of AutoSploit for attacking targets without prior mutual consent is illegal in pretty much every sense of the word. it is the end user's responsibility to obey all applicable local, state, and federal laws. developers assume no liability and are not responsible for any misuse or damage caused by this program. please take these considerations into mind: +Usage of AutoSploit for attacking targets without prior mutual consent is illegal in pretty much every sense of the word. It is the +end user's responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible +for any misuse or damage caused by this program or any component thereof. - - use AutoSploit on a VPS through a proxy or Tor - - keep calm and wipe the logs or use tools to do so - - never connect from your local IP address - - keep a low profile, the point of hacking is not to get caught +Developers do not encourage nor condone any illegal activity; -we do not condone hacking of any sort, the above are tips to keep in mind for ethical purposes. having said that, knowledge is not illegal, and anybody that tells you learning is wrong is a fool. get as much out of this program as we got from writing it. \ No newline at end of file +In OffSec/RedTeam engagements it is important however to mind your operational security. With that in mind, please consider the following: + + - Use AutoSploit on a VPS through a proxy(chain) or Tor + - Keep calm and wipe/data-poison the logs or use tools to do so + - Never connect from your local IP address + - Keep a low profile, AutoSploit is loud + + +In closing, knowledge is not illegal and anybody that tells you learning is wrong is a fool. +Get as much out of this program as we got from writing it. Remember though, common sense and a sense of ethics go a long way. + +Thank you.