Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 133 additions & 13 deletions src/mobile-pentesting/android-app-pentesting/react-native-application.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,22 @@ To confirm if the application was built on the React Native framework, follow th

3. Use the command `find . -print | grep -i ".bundle$"` to search for the JavaScript file.

Note: If you are given an Android App Bundle (.aab) instead of an APK, generate a universal APK first and then extract the bundle:

```bash
# 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 **analyze it to find sensitive information and vulnerabilities**.
Expand Down Expand Up @@ -42,12 +58,37 @@ To search for sensitive credentials and endpoints, follow these steps:

3. It was fortunate that sensitive hard-coded credentials were found in the JavaScript code during the recon process.

### Quick secrets/endpoint hunting in bundles

These simple greps often surface interesting indicators even in minified JS:

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

### Change JS code and rebuild

In this case changing the code is easy. You just need to rename the app to use the extension `.zip` and extract it. Then you can **modify the JS code inside this bundle and rebuild the app**. This should be enough to allow you to **inject code** in the app for testing purpses.
In this case changing the code is easy. You just need to rename the app to use the extension `.zip` and extract it. Then you can **modify the JS code inside this bundle and rebuild the app**. This should be enough to allow you to **inject code** in the app for testing purposes.


## Hermes bytecode
## Hermes bytecode

If the bundle contains **Hermes bytecode**, you **won't be able to access the Javascript code** of the app (not even to the minified version).

Expand All @@ -58,35 +99,114 @@ file index.android.bundle
index.android.bundle: Hermes JavaScript bytecode, version 96
```

However, you can use the tools **[hbctool](https://github.com/bongtrop/hbctool)**, **[hermes-dec](https://github.com/P1sec/hermes-dec)** or **[hermes_rs](https://github.com/Pilfer/hermes_rs)** to **disassemble the bytecode** and also to **decompile it to some pseudo JS code**. To do this, for example these commands:
However, you can use the tools **[hbctool](https://github.com/bongtrop/hbctool)**, updated forks of hbctool that support newer bytecode versions, **[hasmer](https://github.com/lucasbaizer2/hasmer)**, **[hermes_rs](https://github.com/Pilfer/hermes_rs)** (Rust library/APIs), or **[hermes-dec](https://github.com/P1sec/hermes-dec)** to **disassemble the bytecode** and also to **decompile it to some pseudo JS code**. For example:

```bash
# 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
hbc-decompiler ./index.android.bundle /tmp/my_output_file.js
```

### Change code and rebuild
Tip: The open-source Hermes project also ships developer tools such as `hbcdump` in specific Hermes releases. If you build the matching Hermes version used to produce the bundle, `hbcdump` can dump functions, string tables, and bytecode for deeper analysis.

### Change code and rebuild (Hermes)

Ideally you should be able to modify the disassembled code (changing a comparison, or a value or whatever you need to modify) and then **rebuild the bytecode** and rebuild the app.

- The original **[hbctool](https://github.com/bongtrop/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](https://github.com/P1sec/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](https://github.com/lucasbaizer2/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:

Ideally you should be able to modify the dissasembled code (changing a comparison, or a value or whatever you need to modify) and then **rebuild the bytecode** and then rebuild the app.
```bash
# 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.

The tool **[hbctool](https://github.com/bongtrop/hbctool)** supports dissasembling the bundle and building it back after the changes have been performed, however it **only supports old versions** of Hermes bytecode.
# 3) Reassemble into a new bundle
hbctool asm ./hasm assets/index.android.bundle

The tool **[hermes-dec](https://github.com/P1sec/hermes-dec)** doesn't support rebuilding the bytecode.
# 4) Repack the APK and resign
zip -r ../patched.apk *
# Align/sign as usual (see Android signing section in HackTricks)
```

The tool **[hermes_rs](https://github.com/Pilfer/hermes_rs)** supports rebuilding the bytecode, but it's actually a library and nto a CLI tool.
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.

## Dyanmic Analysis
## Dynamic Analysis

You could try to dynamically analyze the app would be to use Frida to enable the developer mode of the React app and use **`react-native-debugger`** to attach to it. However, for this you need the source code of the app apparently. You can find more info about this in [https://newsroom.bedefended.com/hooking-react-native-applications-with-frida/](https://newsroom.bedefended.com/hooking-react-native-applications-with-frida/).

### Enabling Dev Support in release with Frida (caveats)

Some apps accidentally ship classes that make Dev Support togglable. If present, you can try forcing `getUseDeveloperSupport()` to return true:

```javascript
// 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.

### Network interception in RN apps

React Native Android typically relies on OkHttp under the hood (via the `Networking` native module). To intercept/observe traffic on a non-rooted device during dynamic tests:
- Use system proxy + trust user CA or use other generic Android TLS bypass techniques.
- RN-specific tip: if the app bundles Flipper in release by mistake (debug tooling), the Flipper Network plugin can expose requests/responses.

For generic Android interception and pinning bypass techniques refer to:

{{#ref}}
make-apk-accept-ca-certificate.md
{{#endref}}

{{#ref}}
frida-tutorial/objection-tutorial.md
{{#endref}}

## 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`.

- 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
```

## References

- [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

{{#include ../../banners/hacktricks-training.md}}