Mobile Security is interesting to me because it involves both reverse engineering and parts of web application hacking you may be more familiar with. I stick with Android applications mostly because the barrier to entry is a lot lower than it is with iOS applications. In my previous post I go through setting up a basic testing environment, and is a great starting place. Now, let’s get into the basics of how an Android application works, and the various vulnerabilities that can arise.
Android applications are packaged as an APK(Android PacKage), which is a ZIP-compressed archive file. It contains the compiled source code, resources, assets, and manifest. You could extract them from the apk, however they are compressed and compiled, so its necessary to use tools to extract the necessary information. Teknogeek has a great in-depth guide on Hackerone that I recommend for further reading. I will be using jadx to do the heavy lifting rather than apktool and dex2jar, but I strongly recommend familiarizing yourself with other methods because they can come in handy.
Rather than diving into all the app components like Intents, Broadcasts, Content Providers, etc. and the vulnerabilities that arise, I will be using the intentionally vulnerable app BeetleBug by Hafiz Abdulaziz to highlight these by going through each flag in the app. You can read more of the what and why of the application by Hafiz here, and download it from here.
Setup and installation
I mentioned above my previous post on setting up a basic testing environment. This set up will be just fine for using this application, so if you are unsure of what needs to be done, begin here.
To install the app you’ll want to use the Android Debug Bridge. adb is a vital tool included in the Android SDK Platform Tools. It enables communication between a computer and an Android device, facilitating file transfers, app installation, and debugging tasks.
adb install /path/to/beetlebug.apk
This is slightly different than my normal approach to an app. Generally I like to decompile the app and look through the AndroidManifest.xml and classes before I launch the app. This completely comes down to personal preference.
Launching the app
Now that the application is installed, launch it and lets look at what the app does. First we get an initial launch screen, click through to Start Capture, create a username and proceed to the list of challenges in this app.
Go to the first flag, Hardcoding Sensitive Data and click Start.
We get a hint and a place to enter a PIN to unlock a folder. The hint says to look at the string resources. Understanding what hardcoded secrets and where they are can be stored
Flag 1 ++ Hardcoding Sensitive Data
Hardcoded secrets in Android apps refer to sensitive information, such as passwords, API keys, or cryptographic keys, that are embedded directly into the app’s code or resource files. Including such secrets within the app’s source can be dangerous because it makes them easily extractable by attackers who decompile the app. Once extracted, these secrets can be used to gain unauthorized access to external services, databases, or APIs the app communicates with, leading to data breaches or other security issues. Common locations to find them are in the strings.xml, resources, or classes and methods.
However, not all hardcoded secrets pose a security risk; for example, some API keys are designed to be publicly shared and are safe to hardcode, as they are limited in scope and functionality. The security implication of hardcoding secrets varies depending on the type of secret and its intended use. It is important to read the API documentation to understand the access available with the key.
Decompiling apks
To decompile the app, I mostly use jadx-cli. You can directly open an apk as well in jadx-gui, but I prefer to open the decompiled code in VS Code.
jadx --deobf -d beetlebug beetlebug.apk
The –deobf flag comes in handy with large codebases. The majority of apps you will look at will implement obfuscation to make reverse engineering more difficult. Many will be classes and methods and fields will share a name, so by using the –deobf flag, jadx will change there names to make it easy to track what you are looking at. It will add a letter at the beginning indicating whether its a class, method, or field using C, M, and F respectively. It will then add a number, and then the original name of the component. So you might see a class named C001a. Anytime that class is called in the code you will see it as C001a, however it is important to keep in mind, the app will just refer to it as a at runtime, this is simply in the decompiled code.
-d outputs the code to a directory, and last we provide the path to the apk.
Strings.xml
Normally I begin looking at the AndroidManifest.xml, but since we have gotten a hint from the app we will go directly to strings.xml.
strings.xml is a resource file located within the res/values/ directory that stores string values in a key-value pair format. This file is used to manage strings, so rather than writing the string in the code each time you can just reference it in the strings.xml. This can be a very tempting place to place things such as API key, and can be done in a safe way, depending on what the API key is used for. Developers might also use it to hardcode secrets in a development version of an app and accidentally release it with the secret still there in the strings.xml. It is an important place to look on any new release.
Finding the flag
Navigate to the res/values folder and open strings.xml. You may notice there are values folders. These are used for localization of apps, and the keys in the value-key pairs are almost always the same. However you may want to take a look at all of them.
At the top of the strings.xml the first resources is named a key “V98bFQrpGkDJ” with a value “7432580”.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="V98bFQrpGkDJ">7432580</string>
Since we are looking for a pin, lets try entering that one.
Success! However now let’s take a look at he class so we can get a better sense of how an app utilizes the various .xml files in the res/values directory.
EmbeddedSecretStrings.java
To find the class we are going to want to follow the directories in the source that match the package name. If the classes are obfuscated, it will take some more effort to figure out what each class is doing to find the relevant one. The package name of the app is app.beetlebug.ctf so we are going go through the source file and navigate through src/app/beetlebug/ctf. There are a number of classes here but fortunately they are not obfuscated so we can get a quick sense of what each class does. EmbeddedSecretStrings.java seems like the most likely one, so lets open it and take a look at it.
Inside this class we see an onClick
method which is an event handler whenever something is clicked in a View
.
@Override // android.view.View.OnClickListener
public void onClick(View v) {
EmbeddedSecretStrings embeddedSecretStrings = EmbeddedSecretStrings.this;
embeddedSecretStrings.pin = (EditText) embeddedSecretStrings.findViewById(C0572R.id.editTextSecretPin);
String s1 = EmbeddedSecretStrings.this.pin.getText().toString();
if (s1.equals(EmbeddedSecretStrings.this.getString(C0572R.string.V98bFQrpGkDJ))){
...
}
In the last line it is comparing the user input EditText to a key V98bFQrpGkDJ
which is being loaded from strings.xml
and matches the strings value that was hardcoded. This matches the pin we entered earlier. This could be stored integers, arrays, bools, etc. all from the values directory. These are called via R.string, R.bools, etc.
Flag 2 ++ Hardcoding Secrets
Flag 2 is another example of hardcoding something into the app that is considered sensitive. In this example we are looking for a promo code that has been hardcoded into a class.
While looking for the EmbeddedSecretStrings.java
class involved in the first flag, there was another class that stood out:
EmbeddedSecretSourceCode.java
.
public class EmbeddedSecretSourceCode extends AppCompatActivity{
public String beetle_bug_shop_promo_code = "beetle1759";
. . .
}
And there we have the promo code saved a string. Input it in the promo code and earn the flag 🙂
How to store secrets
The Android Developer docs suggest not putting secrets like passwords or API keys directly in your app’s code or in files like strings.xml
. Instead, they recommend using the Android Keystore system. It’s a more secure way to keep sensitive info safe. The Android Keystore system lets you store cryptographic keys in a container to make it more difficult to extract from the device.
Also, they say it’s best not to hardcode secrets at all. If your app needs to use special codes or keys to talk to websites or services, you should try to get those details from your server when the app is running, rather than keeping them in the app all the time. This helps make sure that even if someone decompiles an app, they won’t easily find those important secrets.
Conclusion
We’ve covered the basics of Android apps, focusing on APK files and the common issue of hardcoded secrets. Hardcoded secrets are sensitive bits of data like passwords or API keys that developers sometimes leave in the app’s code. This can be risky because anyone who gets their hands on the app can find these secrets and misuse them. However, not all hardcoded data is bad. Some API keys are meant to be public and are safe to hardcode. The key is to know which is which by reading the service’s documentation. This is a simple tip but super important for keeping your app secure.