In their quest for new attack surfaces, threat actors find easy targets among the ~2.7 million Android applications, ad infinitum.
What makes Android applications irresistible targets?
- The ease with which an attacker can acquire the entire source code of an Android application.
- The source codes often contain API keys, secret tokens, sensitive credentials, and endpoints, which developers forget to remove after staging.
Developers often lack awareness about attack vectors on a mobile app. Owing to a false sense of security, provided by the sandboxed and permission-oriented operating systems. However, mobile apps have the same attack vectors as web apps, albeit with different exploitation techniques.
When an attacker meets an Android app
After getting their hands on an Android app’s source code, attackers first decompile it, analyse it for weaknesses, and then exploit it.
Decompiling the source code
The attacker first extracts files and folders from the apk file of an Android app, which is similar to unzipping a zip file. However, the files are compiled. To read the source code, the files are decompiled using:
- Apktool: To read the AndroidManifest.xml file. It also disassembles to small code and allows to repack the apk after modifications.
- Dex2jar: To convert the Dex file into a Jar file.
- Jadx/Jd-gui: To read the code in GUI format.
Analysing the source code
The attacker analyses the source code using 2 methods:
- Static Analysis
- Dynamic Analysis
In this approach, the attacker examines the source code for secret tokens, API keys, credentials, and secret paths. They also understand the source code, check for activities, content providers, broadcast receivers, vulnerable permissions, local storage, sensitive files, etc.
Here are some examples of what attackers look for during static analysis:
As we can see, the strings.xml file below exposes sensitive information such as API keys, bucket name, Firebase database URL, etc.
Often, usernames and passwords are hidden in the source code, because developers forget to remove them.
Content Providers allow applications to access data from other applications. And in order to access a Content Provider, you need its URI.
Attackers check if the Content Provider attribute is ‘exported=true,’ which implies that it can be accessed by third-party applications.
Below we are accessing the content provider declared by the vulnerable application through the tool i.e. content, which acts as a third-party application.
An activity implements a screen/window in an app. So, an app usually invokes only an activity i.e. a particular screen in another app, and not the app as a whole.
Attackers check for weaknesses in activities in the AndroindManifest.xml file. Where, if an activity is marked as ‘exported=true,’ a third-party app can initiate that activity.
For example, the screen below has a functionality to submit a password. And only on submitting the password, we can see the dashboard.
However, if the activity responsible for showing the dashboard is marked ‘exported= true,’ an attacker can use an Activity Manager (AM) tool to run it.
And by doing this the attacker can access the dashboard without a password.
Broadcast receivers listen for system-generated or custom generated events from other applications or from the system itself.
Attackers check for weaknesses in Broadcast Receivers in the AndroindManifest.xml file. And if the intent-filter tag is declared ‘exported=true,’ a third-party application can easily access it. To prevent this, we need to explicitly declare ‘exported=false.’
After checking for the parameters that the receiver can accept, attackers can write a command that will trigger the receiver on behalf of the application.
For example: The following command triggers the receiver to send a message to a phone number.
Following which, the message is delivered to the phone number:
In this approach, the attacker uses a binary toolkit such a Frida to hook to a targeted application and change its implementation. It also allows the attacker to bypass root detection and SSLs.
Bypassing root detection
The following application has root detection. Hence, to access all the capabilities of the application, the attackers need to bypass root detection.
First, the attacker needs to identify the function that is responsible for root detection. Which they can get from the source code:
If one of the functions i.e. ‘doesSuperuserApkExist(‘’) and doesSuExist(‘’)’ returns True, it will be identified as a Rooted Device.
So, the attacker needs to change the implementation of these two functions to False, in order to bypass the rooting. And this is where Frida comes to use.
By injecting the following JS payload, the attacker changes the implementation of the functions responsible for root detection.
With the help of use(), the attacker accesses the PostLogin class. Here, they mention the function i.e. ‘doesSUexist or doesSuperuserApkExist,’ that they need to hook. After which, the function will start returning False instead of True.
Frida Client then sends the server this JS payload. And the server makes it a thread and sends it to the JS Engine. So, whenever the application calls this function, Frida will call the thread instead of the actual function declared in source code.
Given the increasing sophistication of cyber-attacks, it is important that Android apps undergo proper vulnerability assessments before publishing. Also, developers should not leave sensitive information such as API keys and credentials exposed in the source code.