Data-Dive

Reverse engineering the private API of an Android app secured by certificate pinning

· mc51

Context

Reverse engineering the private API of an Android App used to be uncomplicated. One could simply intercept and decrypt the traffic between app and backend server using readily available tools. However, newer Android versions have introduced restrictions that make this more challenging. In addition, some apps are using certificate pinning to avoid having their API exposed. Hence, circumventing these safeguards has become much more challenging. In this post, I present a simple, yet clever solution for defeating the security measures: reverting to an old, less secured Android version for running the app that allows using proven methods to reverse engineer the API. I’ll describe the process using the Nextbike android app as an example. I explored this specific API to build my new web app Bike-Radar (which you should definitely check out if you’re using Nextbike).

The “classic” method

Our goal is to learn about the endpoints, requests and corresponding responses of the API that an Android app is using. The most straight forward way to do this is to observe the traffic transmitted between app and API backend server. With good reason the traffic is secured by HTTPS nowadays. This means that the communication is encrypted. However, as we own the runtime environment it is easy to implement a MITM (man in the middle) attack. We simply redirect the traffic from the android app over a proxy that we own to inspect it. To be able to analyze the traffic we need to decrypt it first. For that, we install our own tls certificate (that of the proxy) on the android device. When communicating with the proxy, the app will now use our certificate’s public key for encryption. As we own the certificate, we have the corresponding private key to decrypt and thus inspect the traffic.1

Exposing HTTPS app traffic used to be as simple as breaking this lock
Figure 1. Exposing HTTPS app traffic used to be as simple as breaking this lock [imgflip]

Numerous tools exist that make this procedure very easy. I recommend HTTP toolkit because of its simplicity, features and the fact that it’s open source.

Modern challenges

Recent versions of Android have introduced changes that are supposed to increase the security around HTTPS communication of apps. Consequently, the “classic” approach mostly stopped working. But we can still improvise, adapt and overcome those hurdles with some tricks.

Improvise. Adapt. Overcome.
Figure 2. Improvise. Adapt. Overcome. [imgflip]

No trust in user provided certificates

In older Android versions it was possible (and easy) for users to manually install a certificate to the device’s certificate store. By default it was trusted by all apps using HTTPS.2 With Android 7 (API 24) this changed. Apps won’t trust user installed certificates by default anymore. However, they will still trust the ones in the system store. By rooting the device or using an emulator one can still add system wide certificates.3 So, in order to defeat this first safety measure we can simply use an emulator, e.g. the one provided with Android Studio. This will already take care of many apps, especially those that didn’t put too much effort into hiding their API.

Certificate pinning

Let’s look at a different, more tricky, safety measure: certificate pinning. Basically, apps that implement certificate pinning are super paranoid: They only trust a certain, pre-specified certificate that is owned by the app creator.4 Those apps ignore system wide certificates. Unfortunately, this invalidates our previous solution. Several apps have started implementing this technique claiming it’s beneficial for securing user data. However, in many cases it’s not so much about securing user data and more about trying to prevent people from reverse engineering the underlying API. And that’s exactly what is on our mind.
Some advanced methods for defeating certificate pinning exist.5 Those center around manipulating the app code either in the .apk before installation or during runtime. The manipulations are supposed to turn off the restrictive certificate checks. While some automated tooling around this exists, there is no single “one size fits all” solution. In theory, an app can have its individual implementation of certificate pinning. This would then also require a tailored solution for removing it. However, in some cases there is a simpler solution which requires no manipulation at all: we can leverage the fact that Android 6 was way less restrictive in dealing with certificates.
First, we get Android 6 up and running on an emulator which is painless. Then, we need to find a version of the app that is compatible with that Android version. You can check the minimal required version of your target app in the Play store under “About this app”:

Nextbike app on the Play Store
Figure 3. Nextbike app on the Play Store

In our case we got lucky. The Nextbike app runs on Android 5 and newer. But often, for current versions of an app you won’t be as lucky. It’s hard to keep an app compatible with former versions and implement new Android features at the same time. But if the app has been around long enough, there’s a high chance we can find an old .apk. A good place to start looking for that is on apkmirror.com. For the Nextbike app we find versions going back to 2019.

Nextbike app apks on apkmirror.com
Figure 4. Nextbike app apks on apkmirror.com

Make sure the architecture for the .apk you download matches your emulator configuration. Usually, it will easiest if you find a version labeled noarch. This will work with x86 Android system images in your emulator.
Once you found a compatible app version, have it run on your Android 6 emulator (using e.g. adb install) and setup HTTP toolkit to intercept the traffic from the emulator. Then, start intercepting traffic:

Great success: we can inspect the unencrypted app traffic
Figure 5. Great success: we can inspect the unencrypted app traffic

Now, you can start exploring how app functionality corresponds to the made API calls and received responses. Happy reverse engineering!


  1. For brevity, I’m simplifying the technical details and terms. That’s why I don’t bother introducing Certificate Authorities (CA) and instead generalize everything to “certificate”. ↩︎

  2. See here: “By default, secure connections (using protocols like TLS and HTTPS) from all apps trust the pre-installed system CAs, and apps targeting Android 6.0 (API level 23) and lower also trust the user-added CA store by default.” ↩︎

  3. Learn more about that here↩︎

  4. See here: “Certificate pinning, the practice of restricting the certificates that are considered valid for your app to those you have previously authorized […]” ↩︎

  5. Read more about “Defeating Android Certificate Pinning with Frida”. ↩︎