RCayre / mirage

Mirage is a powerful and modular framework dedicated to the security analysis of wireless communications.
https://homepages.laas.fr/rcayre/mirage-documentation
MIT License
261 stars 48 forks source link

Bluetooth HCI-Snoop #9

Closed whoot closed 3 years ago

whoot commented 4 years ago

Hey,

getting the Bluetooth HCI-Snoop log from the device is a nice idea, however I see some problems with your code.

Some additional background info The btsnoop hci log seems to be getting phased out of the user-accessible areas on a lot of phones (you need root access). Trying to enable the logging does not work - it is enabled in the developer options, but if you look into the config (_/etc/bluetooth/btstack.conf), the value of BtSnoopLogOutput is still false => Enabling the option is ignored.

This is the reason why running Mirage on such devices (like mine) will produce the following error message on runtime:

./mirage_launcher ble_monitor INTERFACE=adb0

[INFO] Module ble_monitor loaded !
[SUCCESS] ADB Device found: XXXXXREDACTEDXXXXX
[INFO] Trying to send adb shell commands ...
[SUCCESS] Yeah, we can send commands.
[INFO] Looking for HCI logs ...
[SUCCESS] Log found: 
[INFO] Calculating size ...
Traceback (most recent call last):
  File "./mirage_launcher", line 4, in <module>
    main()
  File "/opt/mirage/mirage/mirage.py", line 11, in main
    parser.run()
  File "/opt/mirage/mirage/core/argParser.py", line 141, in run
    self.launcher()
  File "/opt/mirage/mirage/core/argParser.py", line 120, in launcher
    self.appInstance.run()
  File "/opt/mirage/mirage/core/app.py", line 327, in run
    output = module["module"].execute()
  File "/opt/mirage/mirage/core/module.py", line 109, in execute
    output = self.run()
  File "/opt/mirage/mirage/modules/ble_monitor.py", line 57, in run
    self.receiver = self.getReceiver(interface=self.args['INTERFACE'])
  File "/opt/mirage/mirage/core/module.py", line 258, in getReceiver
    self.__class__.Receivers[interface] = self.__class__.ReceiversClass[self.technology](interface=interface)
  File "/opt/mirage/mirage/libs/ble.py", line 811, in __init__
    super().__init__(interface=interface, packetType=BLEPacket, deviceType=deviceClass)
  File "/opt/mirage/mirage/libs/wireless.py", line 156, in __init__
    self.device = self.deviceType.get(self.interface)
  File "/opt/mirage/mirage/libs/wireless_utils/device.py", line 34, in get
    cls.instances[interface].init()
  File "/opt/mirage/mirage/libs/ble_utils/adb.py", line 526, in init
    size = self._getSizeOfSnoopFile(location)
  File "/opt/mirage/mirage/libs/ble_utils/adb.py", line 270, in _getSizeOfSnoopFile
    return int(stdout.decode('ascii').replace('\n',''))
ValueError: invalid literal for int() with base 10: ''

--

The FIX

First of all, dont use harcoded paths for specific devices. Get the path of the log file from the bluetooth config:

def _getSnoopFileLocation(self):
  stdout, _, returncode = self._runADBCommand("cat /etc/bluetooth/bt_stack.conf | grep BtSnoopFileName")
  path = (stdout.decode('ascii').replace("\n","")).split('=')[1]
  _,_,returncode = self._runADBCommand("test -f "+path)
  if returncode == 0:
    return (True,path)
  return (path == "",path)

Also checking the socket is not enough. Check if the option is actually enabled:

def _isBtsnoopEnabled(self):
  stdout, _, returncode = self._runADBCommand("cat /etc/bluetooth/bt_stack.conf | grep BtSnoopLogOutput")
  snoopEnabled = (stdout.decode('ascii').replace("\n","")).split('=')[1]
  return (snoopEnabled == "true")

Including this checks, the output may look something like this:

./mirage_launcher ble_monitor INTERFACE=adb0

[INFO] Module ble_monitor loaded !
[SUCCESS] ADB Device found: XXXXXREDACTEDXXXXX
[INFO] Trying to send adb shell commands ...
[SUCCESS] Yeah, we can send commands.
[FAIL] HCI-Snooping not enabled, aborting...
[FAIL] An error occured during device initialization (interface : adb0)
[INFO] Mirage process terminated !

If you want, I can create a pull request.

RCayre commented 4 years ago

Hi, thank you for this issue. I have managed to reproduce the problem with one of my phone, however the fix is probably not enough. On some phones, the bt_stack.conf is not updated and doesn't contain BtSnoopFileName and BtSnoopLogOutput, so it's probably not a good idea to use only this file to know if the option is enabled and where the log file is. I think the best option could be to use the bt_stack.conf file if it's available, and the hardcoded / find way if it's not. There was also a mistake in my code (the found boolean was inverted, so it didn't stop the execution if the log file wasn't found). Can you tell me if the following code works with your phone ?

    def _getSnoopFileLocation(self):
        stdout, _, returncode = self._runADBCommand("cat /etc/bluetooth/bt_stack.conf | grep BtSnoopLogOutput")
        if returncode == 0:
            # The option has been found in the bt_stack.conf file
            snoopEnabled = ((stdout.decode('ascii').replace("\n","")).split('=')[1] == "true")
            stdout, _, returncode = self._runADBCommand("cat /etc/bluetooth/bt_stack.conf | grep BtSnoopFileName")
            if returncode == 0:
                path = (stdout.decode('ascii').replace("\n","")).split('=')[1]
                _,_,returncode = self._runADBCommand("test -f "+path)
                return (snoopEnabled and returncode == 0,path)
        else:
            # The option has not been found in the bt_stack.conf file
            possiblePaths = [
                    "/sdcard/btsnoop_hci.log",
                    "/data/log/bt/btsnoop_hci.log", # Samsung
                    "/sdcard/MIUI/debug_log/common/btsnoop_hci.log" # MIUI
                    ]

            for path in possiblePaths:
                _,_,returncode = self._runADBCommand("test -f "+path)
                if returncode == 0:
                    return (True,path)
            stdout, _, returncode = self._runADBCommand("find /sdcard/* -name btsnoop_hci.log | head -n1")
            print(stdout.decode('ascii').replace("\n","") != "")
            return (stdout.decode('ascii').replace("\n","") != "",stdout.decode('ascii').replace("\n",""))
whoot commented 4 years ago

Hey, thanks for the fix. Works as intended:

./mirage_launcher ble_monitor INTERFACE=adb0

[INFO] Module ble_monitor loaded !
[SUCCESS] ADB Device found: XXXXREDACTEDXXXXX
[INFO] Trying to send adb shell commands ...
[SUCCESS] Yeah, we can send commands.
[INFO] Looking for HCI logs ...
[FAIL] Log not found, aborting ...
[FAIL] An error occured during device initialization (interface : adb0)
[INFO] Mirage process terminated !
RCayre commented 3 years ago

Now merged.