Android Mobile Application Security Testing Write-Up
Introduction
Mobile banking applications handle highly sensitive data — credentials, transactions, and personal information. But what happens when basic security controls are poorly implemented?
In this series, we analyze the intentionally vulnerable WaTF Bank application, which simulates a real-world mobile banking environment with over 30 vulnerabilities based on the OWASP Mobile Top 10 (2016) as defined at the time of its release (2020). The application is available for both Android and iOS; however, this walkthrough focuses on the Android implementation.
The goal is to demonstrate how these issues can be identified and exploited in practice.
Note: This walkthrough was originally written as a single long article and has been split into a multi-part series for better readability and structure. While some practices and tools may have evolved, the concepts and vulnerabilities demonstrated remain highly relevant for understanding common mobile security issues.
In Part 1, we will cover:
- Bypassing root/emulator detection
- Identifying excessive permissions
- Exploiting exported components
- Intercepting sensitive data via intent sniffing
⚠️ Disclaimer
- This article contains spoilers
- Try to identify vulnerabilities before reading the solutions
- Not all vulnerabilities may be covered
- The vulnerabilities are not presented in the order they were discovered or exploited
- Tool setup is not included (references are provided)
Mobile Application Security
Before diving in, here are the key areas of mobile application security (based on OWASP Mobile Top 10 (2016)):
- Interaction with the Mobile Platform
- Data Storage and Privacy
- Network Communication
- Authentication and Authorization
- Cryptography
- Code Quality and Exploit Mitigation
- Anti-Tampering and Anti-Reversing
Detailed information can be found in OWASP’s MASVS and MASTG, if you are interested to learn more about Mobile App Security. The vulnerabilities demonstrated in this series fall within these categories.
Environment
The environment can be set up in Windows, Linux or Mac, so you are free to use anything that suits you. In my case:
Host machine: for better performance, I used my Windows 10 computer as it is (instead of using virtual machine for example), and I installed several tools that will be used for analysis and testing.
Target/testing device: an emulated Nexus 5X device running Android 9.0 API 28 x86, created with Android Virtual Device (AVD), the official Android Emulator distributed with Android Studio.
Installing the App on Testing Device (two ways):
1. Opening the WaTF Bank project in Android Studio and Running it (this compiles the code, installs the application and runs it on the selected emulator).
2. Directly installing it from the .apk file which is also provided, using the Android Debug Bridge (adb). This is included by default with the SDK Platform Tools which come with Android Studio as well.
It can be installed with the following command (the emulator must be running):
adb install watf-bank.apkFinally, a python backend server comes with the app as well. Two ways to run it are provided on the project’s GitHub page. I would personally recommend running it using Docker, to avoid messing with any current python installations, as it uses deprecated and old version libraries.
Without further ado, let’s start the app!😄
Bypassing Root / Emulator Detection
When the app opens in a Rooted or Emulator device, the following screen appears, preventing its usage.
It must be determined what kind of detection mechanism is implemented by inspecting the source code. The code can be inspected directly (as it is provided) or by decompiling the apk. However, it may not be always available, so I will show an example of the latter.
Here the apk is decompiled, using the tool jadx-gui (any similar tool can work as well). The next step is to search the code to see which class would probably run first and inspect places like “onCreate()” method or look for keywords like root, rooted, emulator, detection etc.
Press enter or click to view image in full size
In this case, no code obfuscation techniques are implemented. It is very easy to find that there’s a method called “isRooted()”. The detection mechanism is also very simple, it just searches for the su executable in various possible locations and returns true if it’s found.
Press enter or click to view image in full size
Here, Frida will be used to change the result of the function during the runtime to false and bypass the detection. The following JavaScript script is an example to bypass it:
setImmediate(function() { //prevent timeout
console.log("[*] Starting script"); Java.perform(function() {
var c = Java.use("com.WaTF.WaTFBank.Login"); //providing the Login activity
c.isRooted.implementation = function() { //changing the implementation of isRooted()
console.log("[*] isRooted() called");
console.log("[*] Detection bypassed!");
return false;
};
});
});
To run it with Frida:
frida -U -f com.WaTF.WaTFBank -l script.js// -U connect to USB device (the emulator in this case)
// -f TARGET
// -l SCRIPTRemember! The frida-server must be running on the testing device!
After successfully executing the script, the Login screen appears, showing the Username and Password fields, as well as an IP and Port field in which the backend server’s IP must be filled. The application also asks for permissions.
Another way, would be to simply find the occurrences of the su executable and rename them, however this may not be as easy as it sounds, because write permissions to the system directories will be needed and gaining them differs according to the version of Android.
Get George Petropoulos’s stories in your inbox
Join Medium for free to get updates from this writer.
In this case, it is possible by:
- launching the emulator with writable system
- restarting adb with root permissions
- remounting the partition
emulator.exe -avd Nexus_5X_API_28_x86 -writable-systemadb rootadb remountThe next step would be to connect through the terminal to the testing device (e.g.: with adb shell command), find su and rename it.
Excessive App Permissions
There are not many things to discuss here. To identify what permissions the app requests, you can inspect the AndroidManifest.xml of the app, as well as, check them when the app runs and requests them on runtime (after bypassing root detection) or use tools like drozer, to enumerate them.
The manifest file can be found in the app’s project or by decompiling the apk.
Press enter or click to view image in full size
Exported Activity
To quickly gather information about the attack surface of the app, drozer’s module was used.
Remember! Drozer Agent must be installed and running on the testing device, for drozer to connect to it!
run app.package.attacksurface com.WaTF.WaTFBankThis information can be found in the AndroidManifest.xml as well.
Here we can see that 3 components are exported.
When a component is exported, it means that it is accessible and can be launched by other application components.
In this case, the Login activity of the app is exported. This can be identified using drozer to gather more information about the activities of the app, or by inspecting the manifest file.
The activity is exported by default because an intent filter is used, even though there is no explicit declaration. Since Android 12 this changed, according to the documentation, and the android:exported value must be explicitly declared, otherwise the app cannot be installed. In this case, however, the version is Android 9. Moreover, it’s the launching activity of the app, so it would be exported anyway, for the app to work. We’ll see the example, to understand that this could happen with any exported activity, even if it was not the launchable one.
To launch the activity:
// Drozer
run app.activity.start --component com.WaTF.WaTFBank com.WaTF.WaTFBank.Login// Activity Manager
am start -n com.WaTF.WaTFBank/.LoginBroadcast Receiver Flaw
As seen previously, there was an exported broadcast receiver. Here, to gather more information about it, drozer will be used:
run app.broadcast.info -a com.WaTF.WaTFBank -iThe functionality of the receiver can be further inspected from the source code.
Press enter or click to view image in full size
With this info, it is possible to craft an arbitrary sms message and send it without the user’s permission.
Command in Drozer to send a message:
run app.broadcast.send --component com.WaTF.WaTFBank com.WaTF.WaTFBank.Receiver --action com.WaTF.WaTFBank.SEND_SMS --extra string tel 6901234567 --extra string username John --extra string toAccount 1111111111 --extra string amount "1000000. Your account may be at risk! Follow this link notasuspiciouslink.com"Command using activity manager (am), after connecting to the testing device with adb shell:
am broadcast -a com.WaTF.WaTFBank.SEND_SMS -n com.WaTF.WaTFBank/com.WaTF.WaTFBank.Receiver --es tel 6901234567 --es username John --es toAccount 1111111111 --es amount "1000000. Your account may be at risk! Follow this link notasuspiciouslink.com"Both commands result in the following message:
Intent Sniffing
Continuing with the Broadcast Receiver, the app broadcasts intents without setting a required permission or specifying the destination package. As a result, it is possible for any app running on the device to monitor these intents. In this case, any SMS that is sent by the app.
An Intent is a messaging object you can use to request an action from another app component, as stated in the Documentation.
In the application’s code, the TransferResult class sends an SMS message after a successful transfer. It achieves that with the “sendSMSMessage()” method as shown below.
Press enter or click to view image in full size
Using drozer’s intent sniffing module, the action to sniff is assigned.
run app.broadcast.sniff --action com.WaTF.WaTFBank.SEND_SMSNow, in order to sniff the data broadcasted, a Transfer must be made through the app.
To make a Transfer, you’ll have to Login with one of the accounts provided in the project. Then make a money Transfer to one of the accounts.
When the transfer completes, data is captured by drozer’s module. This shows the amount transferred, the username and the phone number that the sms was sent.
Conclusion
In this first part, we explored several fundamental security weaknesses in the WaTF Bank application, including bypassing root/emulator detection, analyzing excessive permissions, and exploiting exposed components such as activities and broadcast receivers.
In the next part, we will continue the analysis by exploring additional vulnerabilities and more advanced exploitation techniques, further highlighting the security risks present in mobile applications that do not follow best practices.
Thanks for reading, see you in Part 2!😊