Static analysis is the exploitation of strategies that parse the program source code or bytecode, regularly navigating program routes to check the program properties. Static analysis approaches have been proposed for various assignments, including surveying the security of Android applications, test case generation automation, distinguishing application clones, or uncovering non-useful issues identified with execution or energy.
This blog is a beginner-friendly guide on How to do Static Analysis for Android Applications. But, before diving directly into testing, let us clear the dust over a few things first.
Android Architecture consists of different components such as Linux kernel, Libraries, Android Runtime(ART), Application Framework, and Applications.
For static analysis, the required thing to know from the architecture facet is Application Framework and Applications.
Application Framework controls how the different components work together for application such as Activities, Content Providers, Resource Manager, Notification and View System, etc.
Application is an Android Package(apk) file which will consist of dex file, Android Manifest, resources, and libraries, etc. The classes dex file contains the main code which is generated from Java code responsible for the application to work properly. This dex file is then run inside Android Runtime by Dalvik VM.
The Android Manifest is basically an XML file that consists of certain permission, the connection between different application components through activities, intents, receiver, and broadcast, etc for the application to work properly.
The library consists of java based library code responsible to interact with Libc, WebKit, SSL, etc. The resources contain media files that are required to display a graphical view of the application.
The Definition of static analysis is the same for android like analyzing source code to ensure proper code implementation, but there are certain things to keep in mind.
The static analysis is done on the code, “regenerate” it from smali/byte code. This is an important thing to know as code is merely the representation which means the actual code may differ and this leads to many false-positive while using automated scanners.
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 used in this blog is [InsecureBankV2](https://github.com/dineshshetty/Android-InsecureBankv2/releases/tag/2.3.1 ) and tools required for static code analysis is [Jadx-gui](https://github.com/skylot/jadx/releases ) this tool will decode the contents of the apk file and convert the dex file into human-readable code; previously, to do the same apktool and dex2jar tools were used.
For pentesting android application, it is preferred to use physical device rather than using Genymotion as it has certain limitation for free version also it messes up the virtual network if using other virtual machine software on the system.
Let us start with static code analysis of InsecureBankV2 application, open apk file in Jadx-Gui.
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 basic vulnerabilities in Android Manifest files are Debuggable and Backup options enable for the application.
If the **android:debuggable=”true”** 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 android manifest file).
The normal user in the ADB shell can go to the directory but it does not have the capability to list or access the contents present in the directory.
To access the contents the user needs to switch to application user (u0_a125), in android every application has its own 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 exported property set to true.
The activity starts through adb shell using the following command:
`am start -n <application_packagename>/<application_activityname>`
```bash
athene_f:/data/data/com.android.insecurebankv2 $ am start -n com.android.insecurebankv2/com.android.insecurebankv2.PostLogin
```
The Login functionality is 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>`
```bash
PS C:\Users\B0RN2R00T> adb backup -f insecurebank-backup.ab com.android.insecurebankv2
```
After the backup of application to extract the data from ‘.ab’ file use following command:
`dd if=<bacnup-filename> bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" | tar -xvf -`
The above command explanation is as mentioned below:
– dd: This command requires an input file and creates an output file as options provided. So here it will create a file that will not include the first 24bytes data of the backup file, the rest data will be a zlib compressed file.
– python: The python command will then convert the zlib file into the tar archive.
– tar: This will decompress the tar archive back into original file contents.
```bash
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 -
```
The backup file has been extracted into respective folders and files, dig into each file to harvest sensitive information of application which is not accessible otherwise.
Now let’s check some main code of the application, the very first functionality of the application is login. 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 the variable is declared in Java? how the functions are declared and called in Java? how the conditional statements work in Java? etc.
It suggested to know about fundamentals of Java code before diving into static analysis for understanding and finding the maximum number of bugs in the code.
```java
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 into the application, but there is no proper authentication in place and the developer forgot to remove it from the production application which leads to endpoint disclosure to everyone using this application.
The reason it is not validating properly for authentication is because of the following code:
```java
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 above code, `DoLogin.this.username.equals(“devadmin”)` only check for if username is **”devadmin”** and allowing login through *”/devlogin”* endpoint with any random password value.
In Layman terms, anyone can gain unauthorized access to an application with the username **”devadmin”**.
After digging more into the code, it was observed that the credential of users was stored on the device in **”mySharedPreferences.xml”** file using AES encryption specified in **”CryptoClass()”**.
```java
private void saveCreds(String username, String password) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
SharedPreferences.Editor editor = DoLogin.this.getSharedPreferences("mySharedPreferences", 0).edit();
DoLogin.this.rememberme_username = username;
DoLogin.this.rememberme_password = password;
String base64Username = new String(Base64.encodeToString(DoLogin.this.rememberme_username.getBytes(), 4));
CryptoClass crypt = new CryptoClass();
DoLogin.this.superSecurePassword = crypt.aesEncryptedString(DoLogin.this.rememberme_password);
editor.putString("EncryptedUsername", base64Username);
editor.putString("superSecurePassword", DoLogin.this.superSecurePassword);
editor.commit();
}
```
The code responsible for encrypting the credentials is present in “com.android.insecurebankv2.CryptoClass”.
As seen in the code the iv value is 16 bytes of 0’s and 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:
```python
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’s look into other activity called **”ViewStatement”** the code responsible for this functionality is present at **”com.android.insecurebankv2.ViewStatement”**.
The interesting thing about this code is the file URI from which it is fetching the contents of the statement.
```java
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 and it is 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 at external storage, the *”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 is storing the statements in form of html file which was later fetched by below code.
```java
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 vulnerability, it simply required to add malicious JavaScript code inside the Statement file, whenever the application tries to execute **ViewStatement** functionality it will also execute the malicious JavaScript code embedded inside the Statement file.
That’s all, for now, folks, hope you enjoyed the blog and learned few things about static analysis in android from this blog. Please feel free to comment on the suggestions or critics 🙂