React Native Application Pentest
Introduction
React Native, introduced by Facebook in 2015, is a JavaScript-based framework for developing native applications on platforms like Android and iOS. Its cross-platform capabilities have made it increasingly popular in mobile application development. This blog delves into the nuances of pentesting React Native applications.
React Native Application Architecture
React Native applications are crafted using JavaScript and JSX. A central concept is the “component,” which represents segments of the user interface, akin to “activities” in Java-based Android apps. These components are both composable and reusable, facilitating efficient development.
Reverse Engineering React Native Applications
To analyze a React Native application’s structure:
- Decompile the APK: Use tools like APKTool to extract the application’s contents.
- Identify Key Files: Look for the
index.android.bundle
file in the/assets
directory. This file houses the application’s JavaScript code in a minified format.
The Mystery of index.android.bundle File
The index.android.bundle
is pivotal as it contains the entire core logic of the application. Pentesters can search this file for hardcoded credentials, tokens, and other sensitive information.
Hermes Engine
Hermes is an open-source JavaScript engine optimized for React Native. It enhances performance by compiling JavaScript into bytecode ahead of time. However, this introduces challenges in reverse engineering due to the bytecode format.
Presenting hbctool
To address the challenges posed by Hermes bytecode, the blog introduces hbctool. This tool aids in decompiling Hermes bytecode, allowing pentesters to analyze the underlying JavaScript code effectively.
Now Let’s Dive into Solving the Challenge to Explore React Native Architecture
After downloading our APK and opening it using JADX for static analysis, we found the following path: /assets
under the Resources
folder:
We found all the logic code written in JavaScript, and it was minified to save memory and add some obfuscation. We can unminify this file using the online tool: unminify.
Copying the whole code to this website to unminify it:
Now, we can download the unminified code to our VSCode to investigate it further.
NOTE: We are lucky because this code doesn’t use the Hermes engine, making it easier to analyze. If it used Hermes, you could decode it using hbctool
as mentioned above.
After opening the code in VSCode and doing some searching, I found this piece of code:
1
2
3
function y(t){
t == f("flag{N0t_Th4t_E4sy}") ("616b66607c37333662363f3036613f365643f3f663366306161643335613632653e653f667a") ? j("Correct Flag") : j("Wrong Flag");
}
Of course, I tried to submit this flag, but it wasn’t the real flag, so I investigated more about this function and followed its parameters:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
f = function (t) {
var n = function (n) {
return ((o = t),
o.split("").map(function (t) {
return t.charCodeAt(0);
})).reduce(function (t, n) {
return t ^ n;
}, n);
var o;
};
return function (t) {
return t
.match(/.{1,2}/g)
.map(function (t) {
return parseInt(t, 16);
})
.map(n)
.map(function (t) {
return String.fromCharCode(t);
})
.join("");
};
};
Analysis of the Code
Inner Function n:
- Takes a single integer
n
(which is a part of the decrypted hex string). - Converts the key (
t
) to ASCII codes usingt.charCodeAt(0)
. - XORs all the ASCII values of the key characters with
n
. - Returns the XORed value.
Outer Function:
- Splits the hex string into chunks of two characters each (
.{1,2}
). - Converts each hex chunk to an integer using
parseInt(t, 16)
. - Passes each integer to the inner function
n
. - Converts the result to a character using
String.fromCharCode()
. - Joins all characters to produce the decoded string.
After analyzing the code, let’s create a simple script to replicate this decryption process:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
key = "flag{N0t_Th4t_E4sy}"
hex_string = "616b66607c3733366236f3036613f3765643f3f66336630616143335613632653e653f667a"
def decode_string(key, hex_string):
key_codes = [ord(ch) for ch in key]
hex_codes = [int(hex_string[i:i+2], 16) for i in range(0, len(hex_string), 2)]
decoded_chars = []
for hex_code in hex_codes:
decoded_char = hex_code
for key_code in key_codes:
decoded_char ^= key_code
decoded_chars.append(chr(decoded_char))
return ''.join(decoded_chars)
decoded_flag = decode_string(key, hex_string)
print("Decoded Flag:", decoded_flag)
Running the script successfully reveals the correct flag:
For a detailed exploration, refer to the full blog post: Payatu Blog
THANKS FOR READING ❤️