Security News

Cybersecurity news aggregator

📡
MEDIUM Vulnerabilities Reddit r/netsec

Reversing the FT100 BLE fitness bracelet

  • What: Analysis of security flaws in FT100 BLE fitness bracelet
  • Impact: Potential risks in device firmware and communication
Read Full Article →

Reversing the FT100 BLE Fitness Bracelet Posted on 2026-03-12 • 3500 words Tags: BLE , Hardware hacking , Physical security The device The device is called FT100 Fitness Bracelet by TWENTYFIVESEVEN and it offers some basic features such as heart rate and blood pressure monitoring, step count, music control, notifications handling, a find device functionality, and OTA firmware update. It features a display and a soft touch button, and it is possible to use the manufacturer’s app to configure the device via BLE connection. FT100 Fitness Bracelet The watch features a PHY6202 MCU, 512Kb of ROM, 16Kb of RAM and a whopping 50mAh battery (that I promptly swapped with an external power supply as it was giving me troubles). The companion app Lefun Health allows interaction with device’s functionalities. It offers “data aggregation” features such as recording sport activity or sleep quality. The app itself works ok-ish but it has frequent ads, it often asks for additional permissions and it tries to push to users some unrelated AI services made by Lefun. It’s not my favorite app, but it is good enough to work with. Lefun App What I’ll do in this article is try to instrument the android companion app to access BLE traffic between smartphone and our smartwatch to attempt reverse engineering the communication. Sniffing the BLE traffic with frida In order to target the functionalities on the FT100 it is necessary to gain visibility of the BLE traffic exchanged between the phone and the bracelet. So we need to work toward that. To be completely fair, there are multiple ways that could be used to dump the BLE traffic in this exact situation, with android HCI Snoop log being my usual choice. However I’ve been working on mobile applications a lot more recently, I’ve recently checked out this article which uses a similar technique and I figured this project would be a nice way to get practice working with frida , so I decided to go for this way. This method could also come in handy when working on a non-rooted android device as usually there is no access to HCI logs, and a frida gadget could be patched into the apk, bypassing the need for root access. The first step here would be to perform a static analysis of the apk. Using jadx I decompiled the apk and started digging for clues on the right point to hook into. During static analysis, the obvious first attempt was to look for and attempt hooking android default functions to handle BLE GATT events in BluetoothGattCallback class. Ideally we would need at least access to onCharacteristicChanged() and onCharacteristicWrite() to intercept characteristic writes and GATT notifications traffic. But simply hooking these functions I was only able to intercept traffic for writes. After further analysis I realized the app was not using the base Android BluetoothGattCallback , but instead some of these functions would be overridden by anonymous classes. So I needed to reliably hook the override implementations used at runtime in order to access incoming data. The turning point was identifying BluetoothDevice.connectGatt() as the universal entry point for every BLE connection. This method always receives the actual callback instance as an argument. By hooking all overloads of connectGatt() and dynamically extracting the runtime callback class ( callback.$className ), it became possible to hook the correct implementation of the target functions regardless of anonymous inner classes as observed in jadx . var BluetoothDevice = Java . use ( "android.bluetooth.BluetoothDevice" ); var hookedCallbacks = {}; BluetoothDevice . connectGatt . overloads . forEach ( function ( overload ) { overload . implementation = function () { logSection ( "connectGatt intercepted" ); for ( var i = 0 ; i < arguments . length ; i ++) { console . log ( "arg[" + i + "] = " + arguments [ i ]); } // Callback is always argument index 2 in all overloads var callback = arguments [ 2 ]; if ( callback ) { var className = callback . $className ; console . log ( "[+] Real callback class: " + className ); hookGattCallback ( className ); } return overload . apply ( this , arguments ); }; }); Then inside the hookGattCallback function each interesting function can be hooked explicitly like this: if ( Callback . onCharacteristicChanged ) { Callback . onCharacteristicChanged . overload ( "android.bluetooth.BluetoothGatt" , "android.bluetooth.BluetoothGattCharacteristic" ) . implementation = function ( gatt , characteristic ) { var value = characteristic . getValue (); logSection ( "BLE RX (Notification)" ); console . log ( "Device : " + gatt . getDevice (). getAddress ()); console . log ( "Service: " + characteristic . getService (). getUuid ()); console . log ( "Char : " + characteristic . getUuid ()); console . log ( "Data : " + bytesToHex ( value )); console . log ( "ASCII : " + bytesToAscii ( value )); return this . onCharacteristicChanged ( gatt , characteristic ); }; } With this method it was possible to achieve full visibil...

Share this article