Exploiting the Native Libraries of an Android app

Stavro XhardhaStavro Xhardha
3 min read

There are many reasons why developers load native code in their Android apps. Some are ex-C++ engineers who feel more comfortable writing low-level code instead of Kotlin or Java. Others rely on legacy libraries written in C++, which require a lot of effort to rewrite. Native code is here to stay. However, native libraries can sometimes pose security risks because they are easier to load compared to reverse-engineering the actual Android app written in Kotlin or Java.

The use case

In this example, I have reverse-engineered an app that uses a native library to retrieve an API key by providing a hardcoded passkey as a parameter. The original example comes from Hextree’s Reverse Engineering course.

We could try to load and analyze the native library using tools like Ghidra, but in this case, there is an easier, more effective, and elegant method.

The Android documentation explains how to load native libraries in your own app. After reverse-engineering the target app using tools like jadx-gui, we can extract these libraries ourselves. In other words, nothing stops us from creating our own application to use the code inside the native library against itself.

Proof of Concept (PoC)

The only tricky part is that, for this to work, we need to let the JNI (Java Native Interface) know how to match the function signature. This is done using the target app's package name:

void Java_io_hextree_weatherusa_InternetUtil_getKey
               (_JNIEnv *param_1,undefined8 param_2,_jstring *param_3)

{
  xorDecrypt(param_1,param_3);
  return;
}

This method performs an XOR decryption once the correct passkey is provided. The real security issue here, apart from the basic XOR decryption, is that the secret is hardcoded in the app. Loading the native library from a malicious PoC is just part of the process.

But what stops us from creating a module or folder that matches this signature?

The libraries can be loaded in the main module. It took me a while to realize this because I was trying to load the native library from the same module. But it makes sense, as the APK accesses resources from the actual app (main module) rather than other modules.

If everything is set up correctly, we just need to implement the InternetUtil class to retrieve the hidden API key easily:

public class InternetUtil {
    private static native String getKey(String str);

    public static String stealApiKey() {
        System.loadLibrary("native-lib");
        return getKey(Utils.getSecretPasscode());
    }
}

Then, we can either display the API key on the screen or log it in Android's Logcat from the malicious app:

Log.d("NativeCodeCall", "{${InternetUtil.stealApiKey()}}")

With this, we have retrieved the API key hidden in the native library without even manually going through the XOR decryption process.

Conclusion

This example, along with many others, shows not just how to load native libraries and tamper with them, but also highlights how ineffective local encryption can be. If basic mistakes are made, like hardcoding secret keys or relying on simple XOR implementations, sensitive information becomes easy to extract.

0
Subscribe to my newsletter

Read articles from Stavro Xhardha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Stavro Xhardha
Stavro Xhardha

Experienced Android Developer with high focus on security topics. Diving into bug bounty or security audits for Android Apps.