During my day job, I had some recent experiences with applications that leverage certificate pinning to secure client-to-server communication. This made looking at application generated traffic nasty and I want to let you profit from my experiences. In this article, I try to provide some guidance on the workflow of intercepting Android application traffic.

Setup and Proxy

To start inspecting Android application traffic I usually leverage an mitmproxy server. Mitmproxy is ncurses based, which makes it great for server installations and has a solid script interface to tailor its functionality. I prefer mitmproxy over other proxies, nevertheless, Fiddler and Charles can also be worth a look. To start intercepting, simply start mitmproxy without any arguments. The proxy will start on port 8080 and act as a HTTP forward proxy server by default. Configure the proxy’s IP and port in the Android Wi-Fi proxy settings found in the Wi-Fi menu of your device and make sure the firewall allows the phone to reach the proxy host. On most Android ROMs, the Proxy settings are located in the Wi-Fi list, long tap the network name, choose “Modify Network” and then select “Advanced Options”. After configuring the proxy, open the mitmproxy event console (key “e”) and start the app you want to intercept.

In the best (and worst) case you already see application requests. This means the app developer messed up their certificate checks and you are done already. More likely you will only see connect and disconnect events in the event console but no requests. This means that the application is using the configured proxy server but does not trust the certificate. Until Android N it was pretty easy to add user certificates to be trusted in Android apps: Go to mitm.it while connected via the proxy, download the certificate and install it. The app now trusts the certificate and requests should be shown.

In case you see no request arrive at the proxy you can try to use mitmproxy in reverse proxy mode. Override the applications backend domain within the DNS server you use, (usually your router) with the IP of the mitmproxy host. Start mitmproxy in reverse proxy mode on port 443 and see if requests arrive. If you still only see connect and disconnect events you will have to tamper with the application package.

Replacing pinned certificates

To tamper with the app you first need to decompile it. To do so, I prefer apktool. Download apktool and install it as described on the website. Now decompile the application package: apktool d file.apk. At this point, it makes sense to search for pinned certificates inside the application package folder. In case you don’t find pinned certificates just continue.

[ciko@wolf sample.com]$ grep -R MII *
Matches in binary file build/apk/classes.dex
smali/com/d/a/c.smali:    const-string v3, "-----BEGIN CERTIFICATE-----\nMII ... mOf4eJ\n-----END CERTIFICATE-----"
smali/com/amazon/identity/auth/SignatureUtil.smali:.field private static final AM:Ljava/lang/String; = "-----BEGIN CERTIFICATE-----\nMIIDo ... 4eJ\n-----END CERTIFICATE-----\n"

You can safely ignore the match in classes.dex, this file is generated at build time and will not be included in the recompiled app. Similar counts for the seconds match in META-INF/CERT.SF, since its a digest of the original application signer and not a pinned certificate. The following matches are more interesting to us. The .smali files indicate decompiled application source code. In two of those files (c.smali and SignatureUtil.smali) we found pinned certificates. Both are in standard PEM format, with line breaks indicated as “\n”. Copy the original certificate from the file and use OpenSSL or an online tool to decode the certificate. To format the certificate properly in order to decode it, just replace all “\n” with real line-breaks. If the certificate looks like it might relate to the infrastructure you try to intercept communication with (like the applications publisher or some generic cloud hoster like AWS) you want to replace it. To do this, open the mitmproxy proxy certificate in ~/.mitmproxy/mitmproxy-ca-cert.pem and format it in a way that matches the original certificate. In my case, I had to replace all line breaks in the mitmproxy certificate with “\n”. Now paste the mitmproxy certificate over the original one and save the file. Repeat the certificate replacement process for all potentially pinned certificates.

If your original certificate was in a binary file instead of a decompiled source file you might need to add some padding to match lengths. Since certificates are base64 encoded just add “="-signs until the length of the pasted certificate matches the original certificate. Also, make sure that your editor does not convert any special characters. To patch the certificate I just like to use nano and open it preservation mode with nano -Np file.so. You can use CTRL + \ to search and replace the original string by the modified certificate.

Tampering, recompiling and signing

If you are running a device with Android 7 or above now is the perfect time to add your mitmproxy certificate into the application. Follow Dean Wild’s approach to inject your custom certificate into the application files.

To test for success, we have to recompile and sign the application. Use apktool b APP_FOLDER to recompile the apk file, it will be placed in the APP_FOLDER under /dist. You now need to sign your application package in order to install it on an Android device. Signing is mandatory and Android will reject installation if the apk is not signed, but there is no requirement for it to be signed by a “real” CA. Either install apksigner and follow the procedure described there or use this app to sign your application package. Install and start the application.

If everything works, you should see the application’s requests in mitmproxy now. In case you don’t, the app might use a hardcoded IP address for communication. You can use grep like described above to find the IP and patch it inside the application folder.

Final Hints

This guide should cover most scenarios in Android traffic interception. Luckily only a few apps use protobuf or web sockets for communication which are equally terrifying to debug. In case you have to take a look at more advanced tools like Charles to decrease debugging pain. It is always worth a try to launch the app with adb logcat started on a USB-debugging computer - sometimes developers leave interesting debug messages.

Happy hacking.