Android Application Security Penetration Testing: A Journey Into the APK
In this article, we'll discuss methods for investigating the security of an Android application through the Application Package (APK) and describe precautions that can be taken to avoid vulnerabilities.
With the ongoing trend of security vulnerabilities being discovered in major Android apps, are you doing enough to secure yours?
Questions to ask around Android application security
How do I access the APK for an application?
This can be done on an unmodified Android device with shell access by querying the package manager for the APK path, or with apps such as Amaze file manager that have an app backup capability.
Once I have an APK, what can I do with it?
Without the use of additional tools, you can unzip the APK like any other zip file. This allows you to see a binary of the AndroidManifest.xml as well some recognizable folders such as the assets and resources, among others. These directories often contain files of interest such as properties and configurations.
To get the most information, the APK needs to be decompiled using a tool such as apktool. If successful, the APK decompiles into a folder structure of plain text files, complete with an AndroidManifest.xml and a collection of sources in a format known as "smali" (the Icelandic world for assembler). Apktool uses a process called baksmaling for generating smali source from raw Android Dalvik Executable format (DEX) files.
With a smali codebase in hand, you can figure out how the app works as well as search for interesting strings and other data. You can also decompile DEX into Java using a tool like jadx to gain further insight. It is then possible to change an application's behavior by modifying its smali and rebuilding it. Once you have made the desired changes, you can use apktool and jarsigner to recompile and sign the APK for testing on an Android device.
Additionally, with an edit to the manifest and the smalidea plugin for Android Studio, an app can be configured to run in debug mode. This allows for inspection and modification using breakpoints placed in the smali code or the source code of the app's dependencies (eg. Android SDK sources).
What barriers are available to prevent application tampering?
We investigated a number of apps in the Google Play Store to see what measures developers were taking to secure their applications. We saw a variety of attempts by developers to protect their apps. The majority of APKs we looked at are using ProGuard for obfuscation. This will prevent easy decompilation to human readable smali or Java by renaming symbols in the codebase to random strings (using a dictionary of single letter names by default).
We found that this ultimately does little to protect sensitive data or modification of the app, but it does add a little extra work for us to understand the code and separate out the interesting parts from boilerplate, dependencies, etc.
We did observe a more advanced obfuscation where decompilation of the resources directory would fail, but we were able to get around this using apktool's "--no-res" flag. We also found a case where Mac and Windows systems would omit necessary code when recompiling, and we suspect this was done using a dictionary of symbol names that are invalid for those file systems. Linux based systems using ext4 did not have this issue.
In a number of apps, we found that binary libraries had been utilized for a variety of different reasons. These were by far the trickiest to inspect, and we did not go as far as attempting to disassemble or decompile them, although there appear to be a variety of options for doing this. We instead focused on behavior that could be altered through JNI bindings from the smali code and what strings of interest were visible when viewing the library files in a hex editor.
Certificate pinning is a common barrier employed by many apps to prevent traffic inspection that would otherwise be possible using a proxy such as Burp. This is a reasonable defense for the end user against man in the middle attacks, but it is usually an easy barrier to remove from a decompiled APK. We found that we could leverage the tools Frida or Objection to remove cert pinning for us in most cases.
Can I prevent a tampered client from interacting with my server?
We found one app attempting to prevent unauthorized parties from interacting with its services by using SafetyNet. Google's SafetyNet Attestation API provides a cryptographically signed token after examining the device for integrity issues, and then the server can validate this token when a client makes a call. For the app we inspected, we were able to interact with a SafetyNet guarded endpoint using a recompiled client due to a lack of server side validation.
It may still be possible to tamper with SafetyNet from a device using the Magisk root kit, or any systemless root solution with similar capabilities. At the time of this writing Magisk can avoid detection through randomizing its package name as well as via the MagiskHide module, which hides all root kit files from specified apps.
What are the key takeaways?
At the end of the day, all client security measures are security by obscurity and cannot be used as a replacement for a secure server. With so many options for attackers to inspect and modify Android APKs, creators should be assessing what level of effort should be invested in tamper-proofing their apps. At a minimum, secure your APIs and take measures to deter decompilation/recompilation as much as possible.