Skip to Main Content
April 04, 2023

Android Hacking for Beginners

Written by Kurt Muhl
Hardware Security Assessment Penetration Testing Research Security Testing & Analysis

1.1    Prerequisites

As discussed in the previous blog post, an Android emulator was set up for testing a mobile application. Some of the most common tools were configured to see the application’s environment details and start probing for potential flaws. If you followed my previous post, you should now have a lab set up with Damn Vulnerable Banking App (DVBA), Objection, and Android Debug Bridge (ADB). In this post, we will get DVBA back up and running, then learn how to proxy traffic and use the tools we installed to test various aspects of the application. One (1) of the key reasons DVBA was chosen for this lab is due to the backend server that can be used to practice analyzing network traffic and API calls. Before starting on the next section of this guide, take some time to get the DVBA backend server installed and running.

1.2       Intercepting Network Traffic

In order to intercept encrypted network traffic, the emulator needs to trust Burp Suite as a certificate authority (CA). Within Burp Suite, navigate to Proxy > Options Import/export CA certificate > export certificate in DER format. On the following screen, choose a folder and name it for where the certificate will be stored. Mine is saved as burp.cer.

Figure 1 - Export Burp Suite Certificate

The certificate needs to be installed in the emulator to proxy traffic. The certificate can be pushed to the download folder on the emulator using ADB for easy access.

adb push burp.cer /storage/self/primary/Download

Figure 2 - Upload Certificate to AVD

Next, navigate to Settings > Security > Encryption & Credentials > install a certificate > CA Certificate and select the certificate from the Download directory.

Figure 3 - Install a Certificate

The device needs to be restarted to fully trust the certificate. To configure the emulator to send traffic to the proxy navigate to Settings > Network & Internet > Internet > Select the gear next to AndroidWifi > Select edit in the top-right corner > Advanced Options > Manually configure the proxy

Start DVBA, enter the IP address and port for the API URL, and verify any requests that show within Burp Suite’s proxy history.

Figure 4 - Configure Network Proxy (click to play)
Figure 5 - API Status
Figure 6 - API Request Proxied

For some Android applications, certificate pinning may be used to prevent unauthorized third-parties from intercepting the traffic. At a high-level, certificate pinning tells the application which SSL certificates should be trusted for communication. Any certificates that are not explicitly trusted, including installed CA certificates, will cause SSL negotiation errors. In those instances, additional tools and scripts may be needed for an SSL pinning bypass. Objection has a built-in command for attempting to bypass common forms of pinning (android sslpinning disable), but it may not be effective in all scenarios. It is good practice to have multiple tools and techniques on hand for when those situations arise.

1.3       Bypassing fridaCheck

Picking up where the previous blog left off, let's hook DVBA and try to use the application. The command below will launch the application and inject Frida. Once DVBA is launched, Objection has built-in Frida scripts that can be used to analyze various aspects of the application. One (1) thing that is apparent when we start the app is that DVBA will immediately close.

objection –gadget “DamnVulnerableBank” explore

Figure 7 - Launch the App Using Objection
Figure 8 - App Closes After Launch

Using Objection, let’s take a look at what functionality may have been loaded and try to find a solution to the app closing. Using the Search classes functionality, it is possible to see the classes that have been loaded.

android hooking search classes com.app.damnvulnerablebank

Figure 9 - Search for Classes Loaded By DVBA

A class with an interesting name, FridaCheckJNI, was loaded by the application. Let’s list the methods used by the class to identify functionality that may be worth digging into.

android hooking watch class
_method com.app.damnvulnerablebank.FridaCheckJNI.fridaCheck --dump-return
Figure 10 - Look for Classes Loaded at Launch

Right away, it looks like a class is loaded to check if Frida is running, and there is only one (1) method within the class. Watching the identified method may provide some insight into what is happening at runtime. Run the following command that logs calls to the method as well as the return value, then try to relaunch the app.

android hooking watch class
_method com.app.damnvulnerablebank.FridaCheckJNI.fridaCheck --dump-return
Figure 11 - Watch the Class for Return Values

When DVBA is first launched, the fridaCheck method is called and returns a 1, which signals that Frida is enabled, and results in the app closing. Objection supports the ability to change Boolean return values, but since the method is returning an integer, this functionality may not work for bypassing the controls. Below is an example Frida script that can be used to hook the method call and modify the value that would be returned. Since the return value is 1 when Frida is running, switching the value to 0 should bypass the controls and allow the app to run.

Java.perform(function () {
    //output to make sure the script is running
    console.log("looking for FridaCheckJNI.fridaCheck()")
    
    //look for FridaCheckJNI
    const FridaCheckJNI = Java.use('com.app.damnvulnerablebank.FridaCheckJNI'); 
    
    //hook fridaCheck() method
    FridaCheckJNI.fridaCheck.implementation = function() { 
        console.log("hooking fridaCheck().")
        
        //call the function to see original output
        var value = this.fridaCheck.call(this) 
        
        //print the original value, then return 0 to bypass the check
        console.log("fridaCheck() returned "+value)
        console.log("switching fridaCheck() to 0")
        return 0;
    }
});

The script can be run within Objection using import bypass_fridaCheck.js. Within the emulator, click on the DVBA icon to run the app and it should load the welcome screen. Back in the terminal, console.log statements show each step of the process that allowed the app to run.

Figure 12 - Run Custom Frida Script

It can be beneficial to run certain commands when first starting the application with Objection. Below are a few examples of commands that may be useful.

Figure 13 - Bypassed fridaCheck

Disable SSL pinning:

objection –gadget “DamnVulnerableBank” explore –startup-command ‘android sslpinning disable’

Disable root detection:

objection –gadget “DamnVulnerableBank” explore –startup-command ‘android root disable’

At this point, all of the application’s API calls should be routing through Burp Suite’s proxy, and Frida can now be used to analyze various parts of the application’s functionality in real time. Take some time to navigate the application and try to use the different pieces of functionality that have been implemented. Make sure to register a new user account and login to the application, as it will be used later in this post. Walking through all of the functionalities will give us a sense of what has been implemented and areas an attacker may want to focus. Additionally, walking through the functionality will populate the different data stores on the device, which should be reviewed for potential vulnerabilities or sensitive information that may be exposed.

1.4       Local File Analysis

One (1) of the most common issues related to mobile applications is insecure data storage. When dealing with a mobile banking application, there is often PII associated with an account. If a device is stolen or gets infected with malware, access to this information could be detrimental to the device owner.

Using Objection, run the envcommand to identify the application's various data directories.

Figure 14 - List Environment Paths on AVD

Note that /data/user/0/com.app.damnvulnerablebank/ appears to be a common directory for cached information and app files. Additionally, some information is being cached in /storage/emulated/0/Android/data/com.app.damnvulnerablebank/. Let’s take a moment to zip those directories and pull them from the device for offline review.

tar -zcf /sdcard/Download/data.tar.gz /data/user/0/com.app.damnvulnerablebank
tar -zcf /sdcard/Download/storage.tar.gz /storage/emulated/0/Android/data/com.app.damnvulnerablebank/
Figure 15 - Zip Files on the Virtual Device

Using adb pull, these archives can be downloaded to the local system for review. Within the directories that were extracted, there is not a lot of plaintext user data or PII that can be identified. However, there is an XML document containing a JWT within /data/user/0/com.app.damnvulnerablebank/shared_prefs. These tokens contain three (3) different sections: Header, Payload, and Signature. The Header and Payload are encoded with base64 and typically contain information about the algorithm used to generate the signature, when the token was issued, when the token expires, and other details about the access a user should have. Using the JSON Web Tokens extension for Burp Suite, it is possible to decode and see the information contained within the token.

Figure 16 - Decode JWT

The payload contains the username and an is_admin field. This means the API is relying on user supplied data that could be manipulated to gain access to other usernames or admin functionality. Since the JWT is signed using HS256, we need to look for a signature exclusion vulnerability, or identify if the signature was generated using a weak secret. Using Hashcat, a wordlist can be used to brute-force the secret value:

hashcat -a 0 -m 16500 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRydXN0ZWRzZWMiLCJpc19hZG1pbiI6ZmFsc2UsImlhdCI6MTY3NDE2MTMwN30.lS5Rwrr5YxU5HzJ-9vm5hgUsl8gCoETpvtUjZwocKW8 secrets.txt
  • -a 0 specifies the attack mode that will be used.
    • In this case, a straight dictionary attack
  • -m 16500 is the hash algorithm that will be recovered (JWT).
  • Secret.txt is the list of possible passwords to try.
Figure 17 - Crack JWT Secret

The signature was, in fact, generated with a weak secret. Knowing the secret, it is possible to generate a new signature for the JWT and manipulate the values within the payload. Within Burp Suite’s proxy history, find a request that uses the JWT, right-click it, and send the request to Repeater.

Figure 18 - JWT Sent in Request

Switch the view in Repeater from pretty to JSON Web Tokens. Try modifying is_adminto true, and the username to admin. On the right-hand side of the screen, choose the option to Recalculate Signature and place the secret as shown in the input box below.

Figure 19 - Modify JWT and Resign

The server accepted the JWT and provides a 200 OK response, indicating it accepted the modified JWT. Now use the modified JWT to overwrite the value within jwt.xml to see if it can be used to gain access to additional activities within the app.

Figure 20 - Contextual Information in jwt.xml

The file is used to note if the user has logged into the app and caches the account details. Close the app and push the modified jwt.xml to the Android device. The next time the app is launched, the admin account will be logged in.

Figure 21 - Overwrite jwt.xml With Custom JWT
Figure 22 - Access to the Admin Profile

If you have made it this far, congratulations on finding your first vulnerability in DVBA! Continuing with the local file analysis, let’s go back to the directories that were extracted from the device and try to identify additional information to exploit. SQLite databases are commonly used in mobile apps because they fit the processing constraints of a mobile device really well. Often, these databases are not encrypted due to the majority of users never seeing the contents of the files. Two (2) searches that can be worthwhile to run are for common file extensions, and common strings that appear in the file contents.

find . \( -name \*.db -o -name \*.sqlite \-o -name \*.sqlite3 \) -print

find . -type f -exec file '{}' \; | grep 'SQLite 3.x database'

Figure 23 - Search for SQLite in AVD

The search for file extensions returned no results, but the search for a string within the file identified two (2) database files that should be reviewed. Using DB Browser for SQLite, each can be opened and reviewed for content that is unencrypted and could be used for malicious purposes.

Figure 24 - Using DB Browser to Open SQLite Files

Review of the Web Data files shows there are several database tables that may be of interest, including: credit_cards, unmasked_credit_cards, payments_customer_data, and many others. A quick review of each table shows there is no data currently in the database. This is something to keep an eye on during testing because there may be a specific functionality that will populate the information, but the app just needs to be used a bit more.

1.5       Exploiting Activities

Review of the activities within the app’s user interface only shows five (5) different activities that can be performed. Using Objection to list activities shows a lot more functionality that can be used. This is due to every activity needing to be listed within AndroidManifest.xml when the app is built. Objection gets the list of activities from this manifest file.

Figure 25 - Activities Shown in the Mobile UI
Figure 26 - Activities Listed Within Android Manifest

It is worth noting some activities, such as debugging functionality, are purposefully hidden by developers using secret gestures or may be disabled at build time. An example of this would be swiping from the bottom-left of the screen to the top-right to access a page with application logs. While these activities are not intended to be used by a typical user, they are often left in the code. Additionally, launching activities without authentication may lead to unexpected behavior within the application. Each of the listed activities can be launched using Objection. The command to do this is android intent launch_activity <activity name>. The device should now display the screen associated with the activity.

Figure 27 - Launch Android Activity
Figure 28 - Activity as Shown in the UI

The Send Money functionality requires a user to be added as a beneficiary to the account before any money can be transferred. Lets try to transfer money to the account we created earlier. The way this attack will work is:

  • Add user account to the admin’s beneficiaries
  • Approve the beneficiary
  • Use the Send Money activity to transfer money from the admin account

Since we have access to the admin account, thanks to the jwt.xml vulnerability, use the Add Beneficiary activity to add the user-created account.

Figure 29 - Launch Activity
Figure 30 - Add Beneficiary to the Account

Once the account has been added, it needs to be approved by the admin. Launch the Pending Beneficiary activity from Objection and select the pending request. The on-screen prompt will ask for the ID to be entered again, which is displayed at the top of the screen (In this case, it is 9).

Figure 31 - Approve the Beneficiary

Once approved, relaunching the View Beneficiary activity will show our user account is an approved beneficiary.

Figure 32 - Verify New Beneficiary

Use Objection to launch the Send Money activity. Specify the account number and dollar amount to be transferred.

Figure 33 - Send Money to the Beneficiary

If successful, viewing the balance of the trustedsec user should now show an extra $100.

Figure 34 - New Balance After Transfer

By combining the authentication bypass with the functionality to launch activities within Objection, it was possible to put together a multi-step attack that has a financial impact.

1.6       Reverse Engineering

While walking through the application’s functionality thus far, every API request has been logged in Burp Suite’s proxy history. When reviewing these requests, it is apparent that data in the requests and responses has been encrypted. More specifically, the data has been encrypted and base64 encoded. If the data can be decrypted, modified, and re-encrypted it may allow for additional attacks against the API.

Figure 35 - Data is Encrypted in Both the Request and Response

In order to accomplish this, it may be beneficial to understand the file structure of an Android application. An APK file can be unpacked by changing the file extension to .zip, which can allow for the contents to be extracted.

Figure 36 - File Structure of the APK

Within this folder structure is a classes.dex file. This is an executable file (Dalvik Executable bytecode) that contains compiled Java code. The DEX file can be converted into Java class files using a tool like dex2jar. The Java class files can be further decompiled into the source code for easier interpretation. If no code obfuscation was used when building the application, this process can allow for a deeper understanding of the application and vulnerabilities that have been introduced. There are many different tools that can do the decompiling for us, but the tool that will be used for this write-up is JADX. Once installed, the JADX-GUI can be run to provide a graphical interface for loading an APK and reviewing the decompiled source code. Within the user interface, navigate to File > Open and select the dvba.apk.

Figure 37 - Jadx-GUI Opening the APK

When expanding the Source code, com, and app.damnvulnerablebank folders, you will notice that many filenames match the activities seen when interacting with the mobile device. These are the class files for each of those activities. Since the Send Money activity had encrypted data in both the request and the response, let's open that code to take a closer look.

Figure 38 - Java Class Structure

In the proxy history, the attribute enc_data was observed in both the request and response. It is worth noting at this point, that name obfuscation has been implemented to change class and method names to a meaningless string. However, control flow obfuscation has not been implemented, which would have made the code difficult to read and follow. Within the source code, enc_data is being assigned a value based on class e and method b. Method b is being sent a string that contains the values to_account and amount. At the very top of the class file, we can see that class e is being loaded from package c.b.a. Let’s open this class to see what the code is doing.

Figure 39 - Encryption Functionality

Review of the code shows that function a is used for decoding and unencrypting data. Function b is used to base64 encode the data after it has been encrypted. Function c is used to encrypt and decrypt the data. Rather than trying to re-code these functions and copy/paste data into our own script, writing another Frida script to utilize the existing functions may be worthwhile.

//Used when server response is decrypted
setTimeout(function(){
    Java.perform(function() {
        var crypt = Java.use("c.b.a.e"); //look for class c.b.a.e
        crypt.a.implementation = function(enc_data){ //hook method a
            console.log("Encrypted Data:"+enc_data) //print encrypted
            var value = this.a(enc_data) //run the function
            console.log("Response Data: "+value) //print the plain-text
            return value;
        }
    })
},10); //wait 10ms then run

//Used when request data is encrypted
setTimeout(function(){
    Java.perform(function() {
        var crypt = Java.use("c.b.a.e"); //look for class c.b.a.e
        crypt.b.implementation = function(enc_data) {  //hook method b
            console.log("Request Data: "+enc_data) //print plain-text
            let value = this.b(enc_data); //run the function
            console.log("Encrypted data: "+value) //print encrypted data
            return value;
        } 
    })
},10); //wait 10ms then run 

Once the new script is imported, and a transfer is made, Objection will output the request data and the response data. This will make things a little more tester-friendly when analyzing how the user input affects server responses.

Figure 40 - View Request and Response Unencrypted

Having access to the unencrypted data can help when testing the API for additional vulnerabilities, such as SQLi. With access to all of the functionality, system files, and data being transmitted, take some time to look for other vulnerabilities within the app. Some examples of things to look for have been provided throughout this blog. While this may not be a comprehensive list of everything to look for within a mobile application, these steps can be used as the building blocks to create a methodology for analyzing other applications.