An API key is probably the most common method used by developers to identify what is making the request to an API server, but most developers are not aware how trivial it is for a hacker or even a script kiddie to steal and reuse an API key in order to gain unauthorized access to their APIs.
In the previous article we saw why your mobile app needs an API key, and now we will see how to grab that API key from your mobile app by reverse engineering the binary in an effective and quick way with an open source tool. Once we see how easy it can be done, we will realize that it is even achievable by non-developers.
While reverse engineering a mobile binary may seem a very technical task, only achieved by hackers, specialists or skilled developers, it turns out to be easier than one may think. This is possible because the open source world is full of excellent tooling to help security researchers to perform their jobs, but guess what, once they are open source tools, they are accessible to everyone willing to use them, including developers, security engineers, hackers and script kiddies. Scripts kiddies are of course skilled enough to understand how to use these tools, without the need to have any background in IT, just like they are smart enough to learn and master the use of every day software, such as the Microsoft suite of programs. So if script kiddies can do it, we developers must be able to do so too, and learning to think like an attacker must become a regular exercise so we should therefore hack ourselves by trying to reverse engineer our mobile app just as they do.
The range of open source tools available for reverse engineering is huge, and we really can't scratch the surface of this topic in this article, but instead we will focus in using the Mobile Security Framework(MobSF) to demonstrate how to reverse engineer the APK of our mobile app. MobSF is a collection of open source tools that present their results in an attractive dashboard, but the same tools used under the hood within MobSF and elsewhere can be used individually to achieve the same results.
During this article we will use the Android Hide Secrets research repository that is a dummy mobile app with API keys hidden using several different techniques.
The first approach a developer may take to provide an API key to the mobile app is to store it in the source code of the mobile app, and we will exemplify this approach with the API key stored in the SOURCE_CODE_API_KEY variable. This approach is easy to reverse engineer and has the disadvantage of being present in the code being tracked by git.
Another approach is to store the key in the Android manifest file, but this one is also easy to reverse engineer and will be tracked by git. A better approach is to get the API key from the gradle file and then we retrieve it from the environment, this way solving the API key being tracked in git, but not the problem of being easy to reverse engineer it.
It's time to look for a more advanced technique to hide the API key in a way that will be very hard to reverse engineer from the APK, and for this we will make use of native C++ code to store the API key, by leveraging the JNI interface which uses NDK under the hood.
Let’s clone the Android Hide Secrets research project from Github, compile the APK for Android and upload it into the MobSF environment. I will not go into details how to compile the APK for Android, but I can make your life easier and let you grab it already compiled from the Github release page.
I will now show you a quick demo on how you can reverse engineer an APK with MobSF in order to extract the API Key. We will use the MobSF docker image, but you are free to install it in your computer if you wish, just follow their instructions to do it so.
To run the docker image just execute on your terminal:
docker run -it --name mobsf -p 8000:8000 opensecurity/mobile-security-framework-mobsf
Depending on your system you may need to prefix the command with sudo.
When you execute the docker command, if the MobSF docker image doesn't exist yet in your system it will be downloaded and then immediately executed:
Now in your browser got to http://localhost:8000 and upload the APK into it:
Now that you have uploaded the APK, MobSF will start analyzing it. This can take several minutes, so please be patient while you stare at this screen:
When the analysis completes we will be presented with a nice dashboard summarizing the result:
Now we have lots of information to look through, but since we are only interested in the API key we may want to start searching for it in the Android Manifest xml file. We can view it by clicking in the dark blue button in the section “View Code” at the bottom right of the above screenshot, and we will land on this page:
As we can see from the Android manifest file the values for MANIFEST_API_KEY and GRADLE_API_KEY_PLACEHOLDER are readily available, but not the ones for the GRADLE_API_KEY and GRADLE_ENV_API_KEY, though we can see in the Android manifest that they are retrieved as strings, thus we can also easily find them in the strings section of the MobSF report:
The Android manifest xml file is one of the places where developers like to drop their API keys, but you might be interested to take a look into the source code itself by clicking on Security Analysis > Code Analysis on the left menu:
Now we land in a section of the page with links to files with the source code decompiled, and there we should look for this section:
If we click in the link for the file, we will see the source code for it:
As you can see the above code is obfuscated, and if you compare it with the code in Github you will notice that the static variable SOURCE_CODE_API_KEY is not present in the source code of the mobile app APK, but you can see the value for it in the screenshot on the line outlined with an orange background. While is not immediately obvious that this value is for the SOURCE_CODE_API_KEY variable, it will not take to much time to work it out after reading the code.
By now the only API key we have not been able to find is the JNI_API_KEY from the C native code, and that is not so easy to do because the C code is compiled into a .so file that is in HEX format and doesn’t contain any reference to the JNI_API_KEY, thus making it hard to link the strings with what they belong to.
Using the strings command we can see that the API key value is present, but we cannot associate it with the JNI_API_KEY, thus anyone inspecting the .so file will have to do some guesswork in any strings they find in the file in order to find out which one is the JNI_API_KEY.
Let's unzip the APK we downloaded previously from the Github releases page:
After we unzip the downloaded code we can use the strings command:
strings -aw lib/x86/libnative-lib.so | grep -in -C 1 'JNI_API_KEY' -
Nothing in the output for the above command, thus let's try with a less restricted search:
strings -aw lib/x86/libnative-lib.so | grep -in -C 1 'API' -
Also no output. What about the JNI_API_KEY value itself? Let's try to find it with:
strings -aw lib/x86/libnative-lib.so | grep -in -C 1 'yDbx5R+a6zJ3H76iU9YB9U0GY6DjZ4FiWFb8vCMCdLg=' -
This time we have some output:
934-cannot allocate __cxa_eh_globals
We are not able to find any occurrences for the JNI_API_KEY, but we are able to find the value for it, although that is because we are the developers of the mobile app and we know the value we assigned to it. Anyone else would have a hard time to make sense from analyzing this file with a HEX editor or with the strings output. Strings with high entropy are likely to be secrets, so one idea is to look specifically for them.
Let's try out the srtings command without trying to match any string:
strings -aw lib/x86/libnative-lib.so
Strings output example:
- - -> omitted output < - - -
cannot allocate __cxa_eh_globals
std::__libcpp_tls_set failure in __cxa_get_globals()
execute once failure in __cxa_get_globals_fast()
cannot create thread specific key for __cxa_get_globals()
cannot zero out thread value for __cxa_get_globals()
- - -> omitted output < - - -
Android (5058415 based on r339409) clang version 8.0.2 (https://android.googlesource.com/toolchain/clang 40173bab62ec746213857d083c0e8b0abb568790) (https://android.googlesource.com/toolchain/llvm 7a6618d69e7e8111e1d49dc9e7813767c5ca756a) (based on LLVM 8.0.2svn)
GCC: (GNU) 4.9.x 20150123 (prerelease)
- - -> omitted output < - - -
Did you spot the raw value for the JNI_API_KEY in the above output from the strings method?
As you can see it is not associated with anything useful to give it meaning, but don’t underestimate people who try to reverse engineer your mobile app, because If they cannot reverse engineer the APK statically, they will do it at run-time with introspection frameworks. In this particular case a MITM attack would suffix to extract the API key in the request sent from the mobile app to the API server, thus putting script kiddies back into the game.
Using MobSF to reverse engineer an APK for a mobile app allows us to quickly extract an API key and also gives us a huge amount of information we can use to perform further analysis that may reveal more attack vectors into the mobile app and API server. It is not uncommon to also find secrets for accessing third part services among this info or in the decompiled source code that is available to download in smali and java formats.
Now you may be questioning yourself as to how you would protect the API key, and for that I recommend starting by reading this series of articles about Mobile API Security Techniques.
The lesson here is that shipping your API key or any other secret in the mobile app code is like locking the door of your home but leaving the key under the mat!
Stay tuned for my next article on how to perform a MITM attack against a mobile app, where I will demonstrate how it can be done in a device we control, or when we have been able to trick the user into installing some software in order to get free WiFi in a public place.