Under the Sheets, Practical Android Static Analysis
First off, before we get into the technical details of attacking applications, a few housekeeping bits must be understood. This blog is aimed at technical individuals that already understand at a basic level Android development and architecture, aiming to give security testers and application developers an insight into the ways an attacker may interrogate code to achieve some form of compromise.
“Compromise” for Android applications is a generic term, colloquially used to describe the achievement of an end goal set by an attacker. This could be anything from compromising user accounts, exploiting a company’s perimeter network or in this case, simply bypassing a client-side control to access some application functionality. In this blog, we will aim to provide an understanding of how static analysis can aid a security tester when interrogating a mobile application.
So, what is static analysis? Static analysis is a term used to describe the process of decompiling and reverse engineering an application (whether this is Android, iOS, Windows, OS X and so on). It refers to the way in which the decompiled files will be interrogated in a static manner with the aim to gather information to aid further testing. The target of this process can widely vary.
The information that may be targeted as part of this process could include some of the following:
- Hardcoded API keys or credentials
- Information regarding how a particular activity functions
- Details surrounding a company’s perimeter network, for example API calls made by the application which could be further targeted.
In this blog, we will interrogate the ‘KGB messenger’ CTF which can be found here: (‘https://github.com/tlamb96/kgb_messenger’) in an attempt to bypass its launch controls.
The following tools will be used if you would like to follow along:
- AndroidStudio for device emulation.
- Adb to install the application(s).
- JadxGui, allowing us to view the code of the application.
- Apktool, to help us decompile and resign an application.
The Application
First things first, there is no threat landscape without an application. So let’s get this installed. Download the APK file from the linked GIT repository, ensure your device is started and run:
(Command)“` adb install [apkname].apk “`
Now we have this installed, let’s boot the application and see the trouble we are faced with. As you can see (Figure 1) we are greeted with a message preventing us from opening the application fully.
Now we can see the problem, let’s dive into the application source itself to identify how we may be able to bypass this. Open the APK file in JadXGui and browse to the AndroidManifest.xml file. Here we can see the following extract (Figure 2).
We can see as the intent filter states ‘android.intent.action.MAIN’ which means that ‘com.tlamb96.kgbmessenger.MainActivity’ is the entry point of the application. This means we should interrogate MainActivity to understand how the application determines whether or not we are using a russian device.
When we open the MainActivity class, we are greeted with a readable code and several Methods. In this instance we are interested in the onCreate() Method. The reason for this is that when any activity is called, the onCreate method is used to create the activity. This means that the result we are seeing above (Figure 1) is likely as a result of the onCreate method.
Please note there is lots more code in this activity, it is always recommended to research this in full.
However, for the purposes of keeping this blog concise, we will concentrate on the following extract:
String property = System.getProperty(“user.home”); String str = System.getenv(“USER”); if (property == null || property.isEmpty() || !property.equals(“Russia”)) { a(“Integrity Error”, “This app can only run on Russian devices.”); } else if (str == null || str.isEmpty() || !str.equals(getResources().getString(R.string.User))) { a(“Integrity Error”, “Must be on the user whitelist.”); } else { a.a(this); startActivity(new Intent(this, LoginActivity.class)); }
From the above we can see some interesting information:
- System.getProperty(“”) is called and as you can see, the application is asking for, you guessed it, the device user’s home. This information is then stored in a variable called ‘property’
- The above is then checked in a series of IF statements:
- The first statement checks to see if the property string is null, empty or does not equal the string “Russia”. If any of these conditions are true, an alert is displayed that states there has been an integrity error (as seen in Figure 1).
- The second statement checks to ensure the str string is not empty or null and equals a specified resource.
- After both of these statements are passed, else is then hit which starts the login activity. This gives us some intel that past this message, there is a login screen.
There are a couple of ways we could bypass this, however, only one of which is actually usable in this scenario.
- We could update the system variables to set our user.home to the String “Russia”. However, system wide variables are distributed at boot. If we set user.home with setprop, this will not be persisted across rebooting the device unless we set it with the following name: persist.user.home. This is not useful to us as we cannot update user.home and have it persist through a reboot to be distributed to the application.
- The second, and more favourable in this case is to patch the application. Edit the Smali code to delete the check and allow for the login activity to be called so we are able to attempt to break in.
Please note, this section will assume you understand how Android applications are developed and understand what smali code is. There are many articles online surrounding this topic that will explain this in greater detail than is possible here.
Begin by decompiling the application with APKTool:
(Command)“` apktool d kgbmessenger.apk -o patchedkgb “`
- d specifies that we want to decompile the application
- o specifies that we want the output to go to a folder called patchedkgb.
This will create a folder that will include the decompiled application where we are able to edit the smali code to remove the integrity message. Open the decompiled app in a text editor of your choice. I typically use something such as Visual Studio Code.
Navigate to the smali code of the MainActivity Class (Smali > com > tlamb96 > kgbmessenger > MainActivity.smali). In this file there are two things that need to be removed, even with no experience with Smali, it is fairly readable for someone who is able to read Java code (or code in general).
Find the method called onCreate(), this should be down on line 94. Both IF statements which undertake checks to ensure that the device is both Russian and the user is on the list need to be removed. At this point I would recommend spending some time familiarising yourself with smali code and how to read it at a high level.
To bypass the checks remove lines 115 through to 175, then change line 186 to return-void (as :goto_0 no longer exists).
This would result in an onCreate function which looks something similar to the following:
.method protected onCreate(Landroid/os/Bundle;)V .locals 3 invoke-super {p0, p1}, Landroid/support/v7/app/c;->onCreate(Landroid/os/Bundle;)V const v0, 0x7f09001c invoke-virtual {p0, v0}, Lcom/tlamb96/kgbmessenger/MainActivity;->setContentView(I)V const-string v0, “user.home” invoke-static {v0}, Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String; move-result-object v0 const-string v1, “USER” invoke-static {v1}, Ljava/lang/System;->getenv(Ljava/lang/String;)Ljava/lang/String; move-result-object v1 return-void .end method
After this we need to then recompile and resign the application, then install the patched APK back to the test device. Please note, when reinstalling the application, delete the version of the application originally installed on the device.
First of all start by generating the key which will be used to sign the application:
(Command)“`keytool -genkey -alias patchkgb -keystore patchingkeys -keyalg RSA -keysize 2048 -validity 10000“`
Then rebuild the application (Please note, the patched apk will be located under /dist of the decompiled app folder):
(Command)“`apktool b patchedkgb“`
Then resign the application, as Android devices always need to be signed to run on devices:
(Command)“`jarsigner -sigalg SHA1withRSA -digestalg SHA1 -keystore patchingkeys.keystore -storepass ‘
After this, ensure that the application is installed again:
(Command)“`adb install patchedkgb.apk“`
Then ta’da, we can now see the login page without needing to prove we have a russian device or prove we are on the list.
What does this mean?
It may not be entirely clear what we have just exploited here if you are not sure about the working of Android applications, however, the edits we have made to the application have allowed us to abuse client-side controls to access further functionality. Essentially deleting the previously identified checks and removing these when reinstalling the patched application.
In this case, the functionality may not be considered particularly sensitive, however, in the real world, this may be used to bypass other types of restrictions. For example:
- Bypassing the requirement for a license key
- Bypassing controls which check the user level of a particular user. For example a basic user being able to access administrative functionality.
Conclusion
Although the example explored here is fairly trivial, it helps to provide a good idea about what is required when considering client-side controls and how they can be bypassed. In a real-world engagement it is much more likely that other difficulties would be encountered, however, this helps to raise awareness for weaknesses in client-side controls and hopefully helps to prompt developers to make smart decisions when considering access requirements whilst planning application architecture.
References and further reading: