Modules

Presentation

Mirage provides a modular framework for analyzing the security of wireless communication protocols, that’s why the available attacks and actions are implemented as modules. A module is a software component of Mirage, easy to run and use, allowing to perform a specific action or attack. It has multiple input parameters and generates some output parameters. Every parameter is named and allow to provide a specific information to the module in order to customize its execution.

The modules can be combined thanks to a chaining operator (|). If you want more details about this operator, please refer to this documentation.

Finally, some modules provide complex behaviour, such as ble_master or ble_mitm. In order to easily customize their behaviour, Mirage allows to use scenarios which are special classes allowing to modify the execution of a module without modifying its source code.

List of Bluetooth Low Energy modules

The following modules allows to manipulate Bluetooth Low Energy communications :

Name

Description

Documentation

ble_info

This module displays useful informations about a given interface.

here

ble_connect

This module connects to a Peripheral’s device as Central.

here

ble_master

This module simulates a Central device.

here

ble_slave

This module simulates a Peripheral device.

here

ble_discover

This module allows to dump the ATT/GATT database of a Peripheral.

here

ble_adv

This module sends advertisements.

here

ble_scan

This module discovers Advertisers and Peripherals by scanning advertisements.

here

ble_pair

This module performs a pairing with a Peripheral or a Central.

here

ble_mitm

This module performs a Man-in-the-Middle attack.

here

ble_sniff

This module sniffs the advertisements and new / existing connections.

here

ble_jam

This module jams the advertisements or connections.

here

ble_hijack

This module hijacks an existing connection.

here

ble_crack

This module cracks a Temporary Key.

here

ble_monitor

This module monitors an HCI communication.

here

List of Enhanced ShockBurst modules

The following modules allows to manipulate Enhanced ShockBurst communications :

Name

Description

Documentation

esb_info

This module displays useful informations about a given interface.

here

esb_scan

This module performs a scan in order to discover Enhanced ShockBurst devices.

here

esb_sniff

This module sniffs the Enhanced ShockBurst frames.

here

esb_inject

This module allows to inject some Enhanced ShockBurst frames.

here

esb_ptx

This module simulates an Enhanced ShockBurst PTX (transmitter).

here

esb_prx

This module simulates an Enhanced ShockBurst PRX (receiver).

here

esb_mitm

This module performs a Man-in-the-Middle targeting Logitech Unifying devices.

here

List of Mosart modules

The following modules allows to manipulate Mosart communications :

Name

Description

Documentation

mosart_info

This module displays useful informations about a given interface.

here

mosart_scan

This module performs a scan in order to discover Mosart devices.

here

mosart_sniff

This module sniffs the Mosart frames.

here

mosart_inject

This module allows to inject some Mosart frames.

here

mosart_keylogger

This module analyzes the keystrokes packets transmitted by a Mosart keyboard.

here

mosart_keyinjector

This module allows to transmit some keystrokes packets to a Mosart dongle.

here

List of WiFi modules

The following modules allows to manipulate WiFi communications :

Name

Description

Documentation

wifi_info

This module displays useful informations about a given interface.

here

wifi_deauth

This module performs a deauthentication attack.

here

wifi_rogueap

This module creates a basic rogue access point (not connectible).

here

wifi_scan

This module discovers Access Points and WiFi stations.

here

List of Zigbee modules

The following modules allows to manipulate Zigbee communications :

Name

Description

Documentation

zigbee_info

This module displays useful informations on a given interface.

here

zigbee_deauth

This module performs a deauthentication attack.

here

zigbee_floodassoc

This module performs an association flooding attack.

here

zigbee_inject

This module allows to inject some Zigbee frames.

here

zigbee_scan

This module scans for Zigbee devices on all channels.

here

zigbee_sniff

This module sniffs Zigbee communications on a given channel.

here

List of Infrared Radiations modules

The following modules allows to manipulate Infrared Radiations signals :

Name

Description

Documentation

ir_info

This module displays useful informations about a given interface.

here

ir_sniff

This module sniffs the Infrared Radiations signals.

here

ir_inject

This module injects the Infrared Radiations signals.

here

Writing your own module

Generating a new module

Writing your own module is relatively simple. Indeed, Mirage allows to easily generates a basic source code using the create_module command. You can launch it from the Command Line Interface :

 ~~> create_module
[QUESTION] Module's name : newmod
[QUESTION] Module's description : Test module
[QUESTION] Module's type : test
[QUESTION] Module's technology [ble] :
[QUESTION] Module's dependencies (separated by commas) : ble_sniff,ble_scan
[QUESTION] Input parameter #1 (name) : INTERFACE
[QUESTION] Input parameter #1 (default value) : hci0
[QUESTION] Input parameter #2 (name) : PARAM1
[QUESTION] Input parameter #2 (default value) : value1
[QUESTION] Input parameter #3 (name) :
[SUCCESS] Module newmod successfully generated : /home/user/.mirage/modules/newmod.py

Or you can launch it from the Direct Mode :

$ mirage --create_module
[QUESTION] Module's name : newmod
[QUESTION] Module's description : Test module
[QUESTION] Module's type : test
[QUESTION] Module's technology [ble] :
[QUESTION] Module's dependencies (separated by commas) : ble_sniff,ble_scan
[QUESTION] Input parameter #1 (name) : INTERFACE
[QUESTION] Input parameter #1 (default value) : hci0
[QUESTION] Input parameter #2 (name) : PARAM1
[QUESTION] Input parameter #2 (default value) : value1
[QUESTION] Input parameter #3 (name) :
[SUCCESS] Module newmod successfully generated : /home/user/.mirage/modules/newmod.py

It will generates the following code :

from mirage.core import module
from mirage.libs import utils,ble

class newmod(module.WirelessModule):
        def init(self):
                self.technology = "ble"
                self.type = "test"
                self.description = "Test module"
                self.args = {'INTERFACE': 'hci0', 'PARAM1': 'value1'}
                self.dependencies = ["ble_sniff","ble_scan"]

        def run(self):
                # Enter your code here.
                return self.ok({})

Understanding the module environment

As you can see in the generated code, every module inherits from WirelessModule (mirage.core.module.WirelessModule) and implements two main methods :

  • init : this method allows to initialize the module. It is mainly used in order to fill the required attributes, such as technology (indicating the technology used by this module), type (indicating the type of module), description (short string describing the role of the module) args (dictionnary indicating the named input parameters and their default values) and dependencies (array containing the name of the modules needed in order to execute the module).

  • run : this method implements the behaviour of the module : it is executed when the module is launched. If you need to execute some actions before or after this method, you can use two optional methods named prerun (launched before run) and postrun (launched after run).

In the run method, you have to terminate the method execution by returning a dictionary composed of two fields, success and output. Two helpers methods allows you to easily generate the right output format :

  • ok (mirage.core.module.Module.ok) : this method returns the dictionary indicating that the module execution is successful. The dictionary of output parameters can be passed as an argument in order to output some specific values.

  • nok (mirage.core.module.Module.ok) : this method returns the dictionary indicating that the module execution has failed.

return self.ok({"INTERFACE":"hci0"}) # The module execution is successful, it returns an output parameter named INTERFACE
# or
return self.ok() # The module execution is successful, it returns no parameters
# or
return self.nok() # The module execution is not successful

Manipulating the input parameters

Every module can use some input parameters. These parameters are defined in the args dictionnary, which is an attribute of the module class, and can be modified by the user.

# The arguments are defined in the init method, here there are six arguments :
# - INTERFACE : this parameter has a default value (the string "hci0")
# - ARGUMENT_A : this parameter has no default value
# - ARGUMENT_B : this parameter has a default value (the string "yes")
# - ARGUMENT_C : this parameter has a default value (the string "0x1234")
# - ARGUMENT_D : this parameter has a default value (the string "aa:bb:cc:dd:ee:ff")
# - ARGUMENT_E : this parameter has a default value (the string "one,two,three")

def init(self):
        # [...]
        self.args = {
                        "INTERFACE":"hci0",
                        "ARGUMENT_A":"",
                        "ARGUMENT_B":"yes",
                        "ARGUMENT_C":"0x1234",
                        "ARGUMENT_D":"aa:bb:cc:dd:ee:ff",
                        "ARGUMENT_E":"one,two,three"
                        }

As you can see, every input parameter is provided as a string, allowing to easily manipulate it from the command line. However, this string is generally used to transmit a “typed” value to the module (e.g. a boolean or an integer) : Mirage provides multiple functions to check and convert the value provided. These functions are defined in the utils library (mirage.libs.utils).

For example, the function booleanArg (mirage.libs.utils.booleanArg) allows you to easily convert a specific argument to a boolean. The expected values are :

  • “yes”,”true”,”1” indicate True

  • “no”,”false”,”0” indicate False (every other string is converted to False)

For example, if you want to convert the input parameter named ARGUMENT_B to a boolean, you can use :

booleanValue = utils.booleanArg(self.args["ARGUMENT_B"])
# booleanValue contains True

You can also convert a string to an integer value using the function integerArg (mirage.libs.utils.integerArg). The provided value can be an hexadecimal number (e.g. “0x1a2b” or “aabb112233”) or a decimal number (e.g. “42” or “-23”). The following example converts the input parameter ARGUMENT_C to an integer :

integerValue = utils.integerArg(self.args["ARGUMENT_C"])
# integerValue contains 4660

Note

Please note that some functions of the library utils (e.g. isNumber or isHexadecimal) allows to check if the provided string is a decimal or an hexadecimal number. You can refer to (the corresponding documentation) for more details.

It’s quite common to manipulate some MAC or BD addresses during the analysis of a wireless communications, that’s why Mirage provides another conversion function, named addressArg (mirage.libs.utils.addressArg), in order to convert it to an address usable by Mirage. Mirage converts addresses to uppercase string in order to facilitate the comparison between them.

The following example converts the input parameter ARGUMENT_D to an address :

addressValue = utils.addressArg(self.args["ARGUMENT_D"])
# addressValue contains "AA:BB:CC:DD:EE:FF"

Finally, some input parameters can contain a list of strings, splitted by commas (e.g. “one,two,three” <=> [“one”,”two”,”three”]). You can easily convert them to a list of string thanks to the function listArg (mirage.libs.utils.listArg). The following eaxample converts the input parameter ARGUMENT_E to a list of string :

listValue = utils.listArg(self.args["ARGUMENT_E"])
# listValue contains ["one","two","three"]

Interacting with the user

Mirage provides various functions in order to print datas to the user or asking him some informations. These functions are mainly defined in the io library (mirage.libs.io). It allows to standardize the behaviour of modules in terms of user interaction.

Four different functions can be used to display a message, according to the type of information you want to print. First, you can print an information message using the info function (mirage.libs.io.info) :

io.info("This is an information message.")

Second, you can display a warning message thanks to the warning function (mirage.libs.io.warning) :

io.warning("This is a warning message.")

Then, you can print a failure message using the fail function (mirage.libs.io.fail):

io.fail("This is a failure message.")

Finally, you can print a success message using the success function (mirage.libs.io.success) :

io.success("This is a success message.")

Warning

Please keep in mind that the user can control the verbosity level of Mirage. You can refer to this page of the documentation if you need more information about this feature.

You can also display a table using the function named chart (mirage.libs.io.chart) :

io.chart(["A","B","A xor B"],[
                               ["False","False","False"],
                               ["True","False","True"],
                               ["False", "True", "True"],
                               ["True", "True", "False"]
                              ],title="XOR Table")

The corresponding output is :

┌XOR Table──────┬─────────┐
│ A     │ B     │ A xor B │
├───────┼───────┼─────────┤
│ False │ False │ False   │
│ True  │ False │ True    │
│ False │ True  │ True    │
│ True  │ True  │ False   │
└───────┴───────┴─────────┘

The progress function (mirage.libs.io.progress) displays a progress bar (it must be called multiple times in order to display the loading process) :

for i in range(10):
        io.progress(i*10,total=100,suffix="fuzzing...")
        utils.wait(seconds=1)

This example displays something like :

())))))))))))))))))))))))))))))))))))))))__________________) fuzzing...

Finally, you may need to ask your user to answer a question or provide an input. The ask function (mirage.libs.io.ask) is dedicated to this operation :

answer = io.ask("Enter your age", default=25,final=": ")

This function returns the string entered by the user and this example displays the following line :

[QUESTION] Enter your age [25] : 26

Launching tasks in background

Mirage allows modules to run some tasks in background, in a daemon child process. This task may be persistent and can continue its execution after the end of the module execution (some commands allow to manipulate these background tasks in the command-line interpreter, please refer to the corresponding documentation if needed). It provides a simple API included in the utils library (mirage.libs.utils).

Let’s imagine you have written a new method named helloworld :

def helloworld(self):
        while True:
                print("HelloWorld")

If you want to register it as a background task, you can use the function named addTask (mirage.libs.utils.addTask) :

task = utils.addTask(function=self.helloworld)

This function returns the name of the task created. Using this information, you can start the task using startTask (mirage.libs.utils.startTask) :

utils.startTask(task)

You can also restart it using restartTask (mirage.libs.utils.restartTask) :

utils.restartTask(task)

Or stop it using stopTask (mirage.libs.utils.stopTask) :

utils.stopTask(task)

Manipulating a module from another one

If you want to manipulate a module from another one, you can use some helpers functions included in utils (mirage.libs.utils). First of all, include this library using the following line :

from mirage.libs import utils

Then, you can use the loadModule (mirage.libs.utils.loadModule) function in order to load a specific module :

mod = utils.loadModule("ble_info")

You can provide some input parameters using the following syntax :

mod["INTERFACE"] = "hci1"

Finally you can execute the module using the following method :

output = mod.execute()

The returned value is a dictionary composed of two main fields:

  • success : boolean indicating if the execution was successful

  • output : dictionary including the output parameters of the module

if output["success"]:
        io.success("Successful execution !")
        io.info("Output parameters :")
        for outputParameterName,outputParameterValue in output.items():
                io.info(outputParameterName + " = " + outputParameterValue)
else:
        io.fail("An error occured during the execution !")

Integrating scenarios in your module

If you want to integrate scenarios in your module, please refer to the following documentation.