With the widespread use of mobile applications and the growing concern for mobile app security, it has become crucial for developers to ensure the integrity and security of their Android applications.
One effective approach for identifying potential security vulnerabilities in Android apps is static analysis. Static analysis involves analyzing the source code of an application without executing it to detect potential issues and vulnerabilities early in the development process.
In this blog post, we will explore the OWASP (Open WorldWide Application Security Project) Top 10 list of the most critical security risks in Android applications, and how static analysis can help mitigate these risks.
We will also discuss some popular tools used for static analysis of Android applications that can aid developers in identifying security vulnerabilities and improving the overall security posture of their apps.
So, let’s dive in and understand how static analysis can play a critical role in securing Android apps!
Before delving deep into the blog, let us shed some light on two of the major terms that we should be aware of in the context of the static analysis of Android.
Android architecture
Android is an open-source mobile operating system that runs on a wide range of devices, including smartphones, tablets, smart TVs, and more. The architecture of Android is divided into four main components:
- Linux Kernel: Provides core functionalities such as device drivers, process management, memory management, and security.
- Android Runtime (ART): Responsible for executing and managing applications, compiling Java code into machine-readable format during installation.
- Libraries: Contains various libraries written in C and C++, such as SQLite for database management and OpenGL for graphics rendering.
- Application Framework: Provides higher-level tools and APIs for app developers to build user interfaces, handle user input, manage resources, and interact with system services. Includes components like Activities, Services, Broadcast Receivers, and Content Providers.
Apps developed on Android are based on this architecture, with the Java or Kotlin code running in the Android Runtime and leveraging the libraries and APIs provided by the Application Framework to create rich, interactive, and feature-rich mobile applications.
To know more about Mobile Application Penetration Testing, you can read our blog here.
What is Static Analysis in Android?
Static analysis in Android refers to a method of analyzing the source code or compiled code of an Android application without actually executing it.
It involves examining the code to identify potential issues, vulnerabilities, and code smells, as well as checking for compliance with coding standards and best practices. Static analysis is typically performed using specialized tools or plugins during the development process or as part of an automated build or continuous integration (CI) pipeline.
Static analysis in Android can help identify a wide range of issues, such as code quality problems, security vulnerabilities, performance bottlenecks, and potential crashes.
It can detect issues like unused variables, dead code, improper resource handling, memory leaks, and improper permissions usage. Static analysis can also detect security vulnerabilities such as improper input validation, insecure data storage, and potential code injection attacks.
Additionally, static analysis can check for adherence to coding standards, naming conventions, and other best practices, ensuring that the codebase is maintainable and efficient.
Static Analysis in Android- In Detail
In this analysis, the bugs to look at in the Android applications are – excessive permission, hardcoded credentials, weak cryptographic functions, workflow bypass, hidden features, improper log management, insecure storage, etc.
The Application that we are going to use in this blog will be [InsecureBankV2].
The tool required for static code analysis is [Jadx-gui].
This tool will decode the contents of the apk file and convert the dex file into human-readable code. Previously, the tools that were used for doing the same were apktool and dex2jar.
For pentesting Android application, it is preferred to use a physical device rather than using Genymotion as it has certain limitations for the free version and also messes up the virtual network if using other virtual machine software on the system.
So let’s get started!
Static Analysis Guide
We will start with a static code analysis of the InsecureBankV2 application.
- Open the apk file in Jadx-Gui.
Note – The first thing to always check while doing static analysis is the Android Manifest file, this file will provide an abstract-level understanding of application permissions and different components of the application.
The two basic vulnerabilities in Android Manifest files are Debuggable and Backup.
If the **android:debuggable=”true”** is in the android manifest file, then it provides the capability of running commands on behalf of the application by any other application or through an ADB shell.
In Layman’s terms, the application files can be accessed and activities can be triggered by ADB shell commands or through any application on the device.
- To test the vulnerability, run the application and connect the device with the ADB shell. The application data is usually located at “/data/data/<package_name>” (package name is present in the Android manifest file).
The normal user in the ADB shell can go to the directory but it cannot list or access the contents present in the directory.
To access the contents, the user needs from switch to application user to u0_a125 as in Android, every application has a unique username.
The impactful thing with debuggable is to launch activities through adb shell.
Note that the activities with exported=”true” can be accessed from outside the application.
Here we’ll access *”com.android.insecurebankv2.PostLogin”* as it has an exported property set to true.
The activity starts through adb shell using the following command:
am start -n <application_packagename>/<application_activityname>
For example:
athene_f:/data/data/com.android.insecurebankv2 $ am start -n com.android.insecurebankv2/com.android.insecurebankv2.PostLogin
The Login functionality is the bypass in this scenario and the application is ready to access with full functionality. Similarly, it is done with other exported activities as well.
If the **android:allowBackup=”true”** in the android manifest file, then it provides the capability to back up the application and all its data into a single file.
You can do it through the ADB shell using the following command:
adb backup -f backup.ab <application_package_name>
For example:
PS C:\Users\B0RN2R00T> adb backup -f insecurebank-backup.ab com.android.insecurebankv2
To extract the data from the .ab file after the backup of the application has been done, use the following command:
dd if=<backup-filename> bs=1 skip=24 | python -c “import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))” | tar -xvf –
The above command is explained below:
- dd: This command requires an input file and creates an output file as options are provided. So here it will create a file that will not include the first 24 bytes of data from the backup file. The rest of the data will be a zlib compressed file
- Python: The Python command will then convert the zlib file into the tar archive.
- tar: The commands below will decompress the tar archive back into its original file contents.
Example usage:
b0rn2r00t[11:50:47] /mnt/c/Users/B0RN2R00T/tmp $ dd if=insecurebank-backup.ab bs=1 skip=24 | python -c “import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))” | tar -xvf –
Now, the backup file has been extracted into respective folders and files, and digging into each file has to harvest sensitive information about the application that could not be accessed otherwise.
- Now let’s check some of the main codes of the application. The very first functionality of the application is logging in. The code responsible for the login functionality of the application is located in “com. android.insecurebankv2.DoLogin” as provided by the activity name in the Android manifest file.
Understanding the code requires fundamental knowledge of Java syntax. For example, how is a variable declared in Java? How are the functions declared and called in Java? How do the conditional statements work in Java? Among other things.
It is always suggested to have knowledge of the fundamentals of Java code before diving into static analysis to understand and find the maximum number of bugs in the code.
- After analyzing the DoLogin code, the most interesting part of the code is mentioned below:
HttpPost httppost2 = new HttpPost(DoLogin.this.protocol + DoLogin.this.serverip + “:” + DoLogin.this.serverport + “/devlogin”);
Here, it is creating the URL string which will be “http://< server-ip>:< server-port>/devlogin”. The *”/devlogin”* endpoint is a backdoor for developers to login to the application.
But, there is no proper authentication in place and the developer forgot to remove it from the production application which can lead to an endpoint disclosure to everyone who is using this application.
The reason it is not validating properly for authentication is because of the following code:
HttpPost httppost2 = new HttpPost(DoLogin.this.protocol + DoLogin.this.serverip + “:” + DoLogin.this.serverport + “/devlogin”);
List<NameValuePair> nameValuePairs = new ArrayList<>(2);
nameValuePairs.add(new BasicNameValuePair(“username”, DoLogin.this.username));
nameValuePairs.add(new BasicNameValuePair(“password”, DoLogin.this.password));
if (DoLogin.this.username.equals(“devadmin”)) {
httppost2.setEntity(new UrlEncodedFormEntity(nameValuePairs));
responseBody = httpclient.execute(httppost2);
}
In the above code, `DoLogin.this.username.equals(“devadmin”)` only checks if the username is **”devadmin”** and allows login through *”/devlogin”* endpoint with any random password value.
In Layman’s terms, anyone can gain unauthorized access to an application with the username **”devadmin”**.
After digging more into the code, it was observed that the credentials of users were stored on the device in **”mySharedPreferences.xml”** file using AES encryption specified in **”CryptoClass()”**.
private void saveCreds(String username, String password) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
// Access shared preferences and obtain editor
SharedPreferences.Editor editor = DoLogin.this.getSharedPreferences(“mySharedPreferences”, 0).edit();
// Save username and password locally
DoLogin.this.rememberme_username = username;
DoLogin.this.rememberme_password = password;
// Encode username in Base64
String base64Username = new String(Base64.encodeToString(DoLogin.this.rememberme_username.getBytes(), Base64.DEFAULT));
// Encrypt password using custom CryptoClass
CryptoClass crypt = new CryptoClass();
DoLogin.this.superSecurePassword = crypt.aesEncryptedString(DoLogin.this.rememberme_password);
// Commit changes to shared preferences
editor.commit();
}
The code that is responsible for encrypting the credentials is present in
“com.android.insecurebankv2.CryptoClass”
AES requires two things for encryption and decryption:
- Key: A secret value used for both encryption and decryption. It should be kept secure and known only to authorized parties.
- Initialization Vector (IV): A random or non-repeating value used to initialize the encryption process. It should be unique for each encryption operation to enhance security.
Note – Using a strong key and a unique IV in AES encryption and decryption is critical to ensuring the confidentiality and integrity of the encrypted data.
As seen in the code the iv value is 16 bytes of 0’s and the key string is **”This is the super-secret key 123″**. Now to decrypt the encrypted *”superSecurePassword”* stored in *”mySharedPreferences.xml”* file decryption code is required which is provided as below:
from Crypto.Cipher import AES
import base64
key = b”This is the super secret key 123″
ivBytes = b”\x00″ * 16
password = base64.b64decode(“DTrW2VXjSoFdg0e61fHxJg==”)
aes = AES.new(key, AES.MODE_CBC, ivBytes)
decrypted_password = aes.decrypt(password)
print(decrypted_password)
Now let us have a look into **View Statement** the code responsible for this functionality is present at **”com.android.insecurebankv2.ViewStatement”**.
File fileToCheck = new File(Environment.getExternalStorageDirectory(), “Statements_” + this.uname + “.html”);
The above code is simply checking if the file is available to fetch or not. It is also checking the availability of the file at a specific location.
The interesting thing about the code is the location where the file is stored, which is external storage. *”getExternalStorageDirectory()”* means *”sdcard/*”, which is publicly accessible. So anyone with access to the file can view or edit the contents.
But the vulnerable part of this code is that it stores the statements in the form of an HTML file which can later be fetched by the below code.
mWebView.loadUrl(“file://” + Environment.getExternalStorageDirectory() + “/Statements_” + this.uname + “.html”);
So, the file URI will look something like this: `file://sdcard/Statements_<username>.html`
To exploit this issue, just include malicious JavaScript code in the statement file. Whenever the program attempts to execute **ViewStatement** functionality, the malicious JavaScript code hidden within the Statement file will also be executed.
Safeguard Your Android Apps with SecureLayer7’s Advanced Static Analysis
Ensure the safety of your Android apps with SecureLayer7’s advanced static analysis. As mobile apps grow and cyber threats increase, it’s essential to secure and fortify your Android applications.
Our cutting-edge static analysis technology uses a comprehensive and automated approach to scanning your Android app’s source code, bytecode, and other components. We identify vulnerabilities, malicious code, and risky practices, offering deeper code analysis than traditional scanning techniques to uncover hidden threats.
Contact us today to learn more about our advanced static analysis services and elevate your Android app security!