Fetching API/Encryption Key from Android app secured by NDK (Native Development Kit)

vishal kumar vishwakarma
7 min readJun 12, 2021

Key, the most crucial and sensitive component of almost all of the Android apps present on the planet. If you need to hit an API from the UI(Mobile app or Web app), you need an API key. If you want to encrypt/decrypt the data, you need the private key and so on.

Even there are certain apps wherein all the request and response traffic are encrypted. which means before getting into how and what the app is communicating to the server, the attacker must need to find out the key to decrypt the traffic. Similarly, the API request structure is none of the use for the attacker even if they are able to draft the request by analyzing application code post reverse engineering, but don’t have the API key. Due to all these, finding the key becomes the primary goal of the attackers before they can actually attack the application. It is very difficult to do anything without the key and no wonder they put a maximum of their efforts into finding the keys.

There are multiple approaches to hide the hardcoded key/secrets in the app code wherein the popular ones are obfuscating code and using NDK. In this article, we are going to see how to fetch the Keys/Secrets from the Android application that secures the keys using Native Development Kit.

If the key is secured using NDK, you won’t be able to find it in the application code even after reverse engineering. It is because the key is hidden somewhere in a compiled file(.s or .so file) which doesn’t get decompiled like Java code. So let’s get started and follow the below steps to fetch the key from that compiled file. For the demonstration, I’m using Kali Linux but if you are using another OS, you can follow the same steps with another tool that does the same task.

Step 1. Confirm if NDK is used in android to hide keys.

Before beginning to hunt for the key, we must need to get an idea of what methodology has been used by the App to hide the key. To get an understanding of that, first we’ll need to do reverse engineering and get the application code. You can use Mobsf, Jadx-gui and other similar tools for decompiling the application code.

Once you have the application code, analyze how the key is being loaded on the key variable. To get the key variable name either you can search for key related keywords like secrets, key, APIkey and many more or may need to go through each line of code if the code is obfuscated. In my application, I found the below highlighted line of code which confirmed that NDK has been used to hide the key. The line says the key could be fetched from the native function through static method getapisecretkey.

Since its confirmed that application is using NDK for hiding key, our key hunting approach will be specific to that.

Step 2. Unzip the APK.

Since the compiled file having the key is contained in the APK, first we’ll have to unzip the APK which could be done by simple command(unzip appname.apk) in Kali. In windows, you can replace the file extension .apk with .zip and then unzip the file to get the files contained in the APK.

Step 3. Search for .so file.

I have extracted the apk within a directory named mydirectory as you can see in the previous image. Now, let’s scan the directory to locate .so files. I’ll be using “find” command to check if the .so file is present in mydirectory. In windows, you can simply search for it inside the folder using the search box.

And boom! I got the .so file named libnative-main-lib.so along with its path. In the above image, it shows four different paths wherein it contains the same file(libnative-main-lib.so). You can chose any of them to disassemble and fetch keys. In this demonstration, I selected mydirectory/lib/x86_64/libnative-main-lib.so.

Now since I have the file which contains the key. All I have to do is to read the file and find the key. But as we know it’s a compiled file, it won’t be readable using text editors like vi, namo, leaf pad etc. Below is the outcome of cat command used to read the .so file.

Step 4. Open the file using hexdit.

To read the file we require a hexadecimal editor which resolves all the above shown junk into readable strings. We’ll be using hexedit which is inbuilt hexadecimal editor in Kali.

Type hexedit followed by the file name/path as shown below.

After hitting enter, you’ll be able to see the junk of the files has been turned into hexadecimal format and in the right most column you’ll be able to see the characters/strings associated with those hexadecimal values.

Now you’ll have to keep on pressing the down arrow button and scanning the right column for the strings looking similar to the key. What?! how could I know what the key looks like? Don’t worry, in the next section I’ll be explaining that as well :)

After a while, I was able to find the Key. The below highlighted key was the one my application was using to encrypt/decrypt the request and response.

How the key looks like?

Since there will be lots of strings in the .so file, you must have some idea about what you are looking for. For instance, if you know that the encryption algorithm used in the application is AES 256 encryption(which you can only get after analyzing the decompiled code), the key required for encryption/decryption of cipher text requires a 32 character long key. So you should be looking for a string that looks like 32 characters long string. If the algo is AES 128, it would be a 16 character long key. If it is AES 192, the key size would be 24. If the mode of encryption is CBC, you must be looking for an Initialization vector of size 16,24 & 32 for AES 128, AES 192 & AES 256 respectively. Similarly, if you are looking for Azure API key, you must be looking for a string that is 32 characters long and so on.

But don’t worry if there are numerous such strings in the .so file that looks like the key you are looking for. We have a very good friend to help us in such scenarios and we call them Burpsuite Intruder. Just load all the strings in the payload, configure Intruder to hit the API with all the keys one after one and start the attack. You’ll get the correct one! The same goes for the encryption/decryption key. Try decrypting the cipher text with all the keys and see which one worked. You can use AES killer Burpsuite extension for this purpose.

Once you have the key, you’ll be able to hit the APIs authentically and can do all that an authenticated user can do. You will also be able encrypt/decrypt the request/response body to facilitate further pentesting and explore the application for more vulnerabilities.

Remediation : Any methodology to hide the key at the APK level is not full proof. However, it makes reverse engineering a bit difficult. To make it more difficult you can obfuscate the code written in C++(src/main/cpp/native_lib.cpp) which was compiled to form the .so file and ensure to break the key string into chunks. The application must rely on the session to verify the authentication of the API request. The attacker may get the keys by reverse-engineering the APK but they will never get the session until they bypass the authentication. Similarly, never completely rely on encryption of request body to prevent attacks like IDOR, Injection, and so on. Make sure your application is immune to all the vulnerabilities even if you are adding encryption as encryption is never primary but a secondary layer of protection.

Thank you very much for reading the article. Keep on securing!

--

--

vishal kumar vishwakarma

{ "Role" : "Cyber Security Engineer", "Expertise" : "Web & Mobile VAPT & Cloud security", "Off work" : "Traveler, Reader, Foody", "Favorite word" : "Cheers!"}