Flutter mobile app reversing using automated scripts and tools in bulk.
Introduction
Google’s open source Flutter has quickly become one of the most popular development toolkits for building cross platform mobile applications. In this article we will examine vulnerabilities in fultter mobile apps through reverse engineering.
Flutter is an open-source UI framework developed by Google for building natively compiled mobile, web, and desktop applications from a single codebase.
Python script with Selenium was used to bulk download applications from APKCombo.com website.
from selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.keys import Keysimport time# Set up the WebDriverdriver = webdriver.Chrome()# Ensure chromedriver is in your PATH# Define the APKCombo URLapkcombo_url ="https://apkcombo.com/"# Open the APKCombo websitedriver.get(apkcombo_url)time.sleep(2)# Wait for the page to load# Define a list of app names you want to downloadapp_names = ["WhatsApp","Facebook","Instagram"] # Add your app names herefor app_name in app_names:# Find the search box and search for the app search_box = driver.find_element(By.NAME, "q") search_box.clear() search_box.send_keys(app_name) search_box.send_keys(Keys.RETURN) time.sleep(3)# Click on the first app result first_result = driver.find_element(By.CSS_SELECTOR, ".title > a") first_result.click() time.sleep(3)# Locate the download button and click on it download_button = driver.find_element(By.CSS_SELECTOR, ".download-btn") download_button.click() time.sleep(5)# Adjust if there are multiple steps to complete download# Go back to the search page to download the next app driver.back() time.sleep(2)# Close the driverdriver.quit()
Inspect
Bash script was used to verify whether the applications were built with Flutter
#!/bin/bash# Directory containing APK filesAPK_DIR="./apks"# Output file for resultsOUTPUT_FILE="flutter_check_results.txt"# Clear output file if it exists>"$OUTPUT_FILE"# Loop through all APK files in the directoryfor apk in"$APK_DIR"/*.apk; doecho"Checking $apk for Flutter files..."# Extract APK contents to a temporary directory TEMP_DIR=$(mktemp-d)unzip-q"$apk"-d"$TEMP_DIR"# Check for Flutter-specific filesiffind"$TEMP_DIR"-name"libflutter.so"|grep-q"libflutter.so"; thenecho"$apk: Built with Flutter"|tee-a"$OUTPUT_FILE"elseecho"$apk: Not built with Flutter"|tee-a"$OUTPUT_FILE"fi# Clean up temporary directoryrm-rf"$TEMP_DIR"doneecho"Flutter verification complete. Results saved in $OUTPUT_FILE."
B(l)utter was used to reverse engineer each Flutter application.
Bash script was used to automate the process.
To reverse engineer Flutter applications using B(l)utter, you can automate the process with a Bash script. The B(l)utter tool provides functionality to extract Dart code from Flutter APKs.
Here's an example Bash script that automates the reverse engineering of multiple Flutter APKs using B(l)utter:
#!/bin/bash# Directory containing APK filesAPK_DIR="./apks"# Output directory for extracted Dart codeOUTPUT_DIR="./flutter_extracted"# Create output directory if it doesn't existmkdir-p"$OUTPUT_DIR"# Clear previous results>"$OUTPUT_DIR/reverse_engineering_results.txt"# Loop through all APK files in the directoryfor apk in"$APK_DIR"/*.apk; doecho"Processing $apk..."# Extract the package name from the APK filename pkg_name=$(basename"$apk".apk)# Create a directory for the extracted code pkg_output_dir="$OUTPUT_DIR/$pkg_name"mkdir-p"$pkg_output_dir"# Use B(l)utter to reverse engineer the APK# Make sure you have B(l)utter installed and in your PATHblutterextract"$apk"-o"$pkg_output_dir"# Check if the extraction was successfulif [ $? -eq0 ]; thenecho"$apk: Extraction successful"|tee-a"$OUTPUT_DIR/reverse_engineering_results.txt"elseecho"$apk: Extraction failed"|tee-a"$OUTPUT_DIR/reverse_engineering_results.txt"fidoneecho"Reverse engineering complete. Results saved in $OUTPUT_DIR/reverse_engineering_results.txt."
A Flutter Mobile Application Reverse Engineering Tool by Compiling Dart AOT Runtime. It Directly analyzes the libapp.so to extract Dart objects directly from binary and generates Frida scripts to dump data in a running Flutter application.
Scan (Gitleak)
Gitleaks is an open-source secret scanner for git repositories, files, and directories.
Gitleaks was used to scan the pp.txt file of each application. A Bash script was used to automate the process.
#!/bin/bash# Directory containing the pp.txt filesPP_DIR="./pp_files"# Output directory for Gitleaks reportsOUTPUT_DIR="./gitleaks_reports"# Create output directory if it doesn't existmkdir-p"$OUTPUT_DIR"# Clear previous results>"$OUTPUT_DIR/gitleaks_summary.txt"# Loop through all pp.txt files in the directoryfor pp_file in"$PP_DIR"/pp.txt; do# Check if the pp.txt file existsif [[ !-f"$pp_file" ]]; thenecho"No pp.txt files found in $PP_DIR."exit1fiecho"Scanning $pp_file with Gitleaks..."# Extract the application name from the pp.txt filename app_name=$(basename"$(dirname "$pp_file")") # Assuming pp.txt is in app-name directory# Run Gitleaks against the pp.txt file and output resultsgitleaksdetect--source"$pp_file"--report-path"$OUTPUT_DIR/${app_name}_gitleaks_report.json"--report-formatjson# Check if the scan was successfulif [ $? -eq0 ]; thenecho"$pp_file: Scan completed successfully"|tee-a"$OUTPUT_DIR/gitleaks_summary.txt"elseecho"$pp_file: Scan failed"|tee-a"$OUTPUT_DIR/gitleaks_summary.txt"fidoneecho"Gitleaks scanning complete. Summary saved in $OUTPUT_DIR/gitleaks_summary.txt."
A Bash script was created to search for keywords within the pp.txt file of each application.
#!/bin/bash# Directory containing the pp.txt filesPP_DIR="./pp_files"# Output file for search resultsOUTPUT_FILE="scan_keyword_results.txt"# Clear previous results>"$OUTPUT_FILE"# Loop through all pp.txt files in the directoryfor pp_file in"$PP_DIR"/*/pp.txt; do# Check if the pp.txt file existsif [[ -f"$pp_file" ]]; thenecho"Searching for 'Scan(2)' in $pp_file..."# Search for the keyword and append results to the output fileifgrep-q"Scan(2)""$pp_file"; thenecho"$pp_file: Found 'Scan(2)'">>"$OUTPUT_FILE"elseecho"$pp_file: 'Scan(2)' not found">>"$OUTPUT_FILE"fielseecho"No pp.txt files found in $PP_DIR."exit1fidoneecho"Search complete. Results saved in $OUTPUT_FILE."
Analyze
The results from the scan phase were used to analyze the ASM folder and the pp.txt file.
Types of Sensitive Data
Password
Passwords that are inserted directly into software application’s source code are referred to as hardcoded or embedded passwords.
API Key
Requests connected to a project can be authenticated using the API key, which is a special identification.
Some developers may choose to leave it on public shares or hardcode them.
Private Key
A cryptographic variable called a private key is used to encrypt and decrypt data along with an algorithm.
Only those who are allowed to decrypt the material or the key generator should have access to private keys.
Access Token
Access tokens grant users access to a website, application, or API and are utilized in token-based authentication.
The token acts as the user's entry ticket, so once their identity has been verified, they won't need to enter their credentials again for the duration of the token.
Example: AWS Access Token, JWT, etc
API Endpoint
When an application programming interface (API) is visible to ecosystems outside of its immediate environment, it's known as API exposure.
ripgrep is a command line tool that searches files for patterns that was given.
rg '<pattern>'
rg 'private'
AWS Configuration
aws configure
# Command to get IAM information
aws sts get-caller-identity
Hooking function with Frida
Frida is a powerful dynamic instrumentation tool that lets you interact with running applications in real time, which is useful for extracting sensitive information, like secret keys, from mobile applications.
If you have the function address, you can hook it directly using Frida’s Interceptor API, which allows you to hook functions at specific memory addresses rather than by class and function names.
hook_by_address.js
// Define the function address as a hexadecimal valueconstfunctionAddress=ptr("0xYOUR_FUNCTION_ADDRESS_HERE"); // Replace with the actual address// Attach to the function at the specified addressInterceptor.attach(functionAddress, {onEnter:function (args) {console.log("Function entered at address:", functionAddress);// If the secret key or sensitive data is in one of the arguments, capture it// Example: args[0] if the key is the first argumentconsole.log("Secret Key Argument:", args[0].readUtf8String());// Modify args if needed, e.g., args[0] = ptr("0xNEW_VALUE"); },onLeave:function (retval) {// Capture the return value if it holds the secret key or sensitive dataconsole.log("Secret Key Return Value:",retval.readUtf8String());// Optionally modify the return value if needed// retval.replace(ptr("0xMODIFIED_RETURN_VALUE")); }});