Chaining operator

The main goal of Mirage is to provide a modular environment in order to analyse the wireless communication protocols commonly used by IoT devices. As a consequence, it provides multiple offensive modules, allowing to easily perform attacks or actions on the targeted protocol.

Some modules can be combined in order to perform complex attack workflows. Indeed, several attacks are composed of the same type of actions. For example, cloning a device or launching a fuzzing attack imply the use of similar actions, such as scanning the RF environment or connecting to the device. While it is still possible to use existing modules in a new module, a common need is to sequentially execute existing modules to compose customised attack workflows without writing a module. So, Mirage provides a chaining operator (|) inspired by the pipe operator, commonly used in UNIX environments.

It operates in a similar manner, allowing a data pipeline to be created between two modules. Every module can be customised by passing named parameters as inputs and can generate named parameters as outputs: as a result, the chaining operator allows to sequentially execute two modules and propagate the outputs from the first module to the inputs of the second one, according to their name. If a module included in the pipeline fails, sequential execution is interrupted.

In order to illustrate this point, let us mention an example of such an execution.


The execution illustrated by this figure uses a chaining operator | in order to combine two modules :

  • ble_connect: allowing to connect to a Bluetooth Low Energy device

  • ble_discover: allowing to easily discover and export the GATT services and characteristics of a device.

It can be launched from the command line interface by typing the following commands:

$ mirage
--> load ble_connect|ble_discover
[INFO] Module ble_connect loaded !
[INFO] Module ble_discover loaded !
<< ble_connect|ble_discover >>--> set ble_connect1.INTERFACE hci1
<< ble_connect|ble_discover >>--> set ble_discover2.ATT_FILE out.ini
<< ble_connect|ble_discover >>--> run

You can also launch this sequence of modules in direct mode :

$ mirage "ble_connect|ble_discover" ble_connect1.INTERFACE=hci1  ble_discover2.ATT_FILE=out.ini


In order to avoid a confusion between the pipe operator from the Bash environment and the chaining operator of Mirage, it is mandatory to write the module sequence as a string in Direct Mode.

Firstly, the module ble_connect is executed with the default parameters and an interface provided by the user. If its execution is successful, it generates an output parameter INTERFACE and pipe it forward into the next module, ble_discover. Secondly, this last module is executed with some default values as inputs and a customized value provided by the user (linked to the parameter called ATT_FILE).

Multiple pipes can be used in order to combine more than two modules. Some of the output parameters generated by a module included in the sequential execution may not be used by the next module : in that case, the corresponding values are memorized and can be used by another module later in the sequential execution if it has a matching input parameter.

If an input parameters is provided by the user, directly as an argument of the module or by using a configuration file allowing to change the input parameters’ default values, it will be replaced by the previous module output parameters’ value if a sequential execution of multiple modules has been launched. As a consequence, the operator has the highest priority for modifying an input parameter’s value.

As you can see, if you use multiple modules, every parameters is prefixed by the module’s name and its position in the pipeline.

Several modules make use of the classes Emitter and/or Receiver (their role and usage is described here). As a result, if a given interface is used by a module, every following module included in the pipeline using an Emitter or a Receiver based on the same interface doesn’t re-instantiate these classes but automatically reuses the existing ones (according to the design pattern called Register).

This mechanism allows to split a complex attack workflow into simpler actions, leading to a powerful and modular approach.