My goal was to take a look at the HTTP requests that Instagram was making but, after setting an HTTP proxy, I couldn’t see anything. Turns out that Instagram is protected against MITM attacks using a technique called certificate validation (SSL Pinning) which compares the certificate provided by server in the TLS handshake with a trusted one embedded in APK.
This article is based on Instagram APK version 220.127.116.11.104 (x86) which you can download here. I am also using an Android 8.0 emulator with adb running as root.
The sole purpose of this article is educational and for testing of your own applications. This is not intended for piracy or any other non-legal use.
Setting up Burp to work with TLS 1.3
Facebook deployed TLS 1.3 at very large scale with their open source library Fizz. It doesn’t surprise me that they decided to use it on their Instagram application to make internet traffic more secure.
This time I decided to use Burp to capture requests that Instagram app is making. After setting up the proxy, some weird alert appears in the Alerts tab.
What is this weird “no cipher suites in common” message? Looks like this version of Burp does not support TLSv1.3 cipher suites. We can verify this by going to Project Options > SSL and list all ciphers.
A simple solution to this problem is to run Burp with the latest version of JDK. At that point, you can run burpsuite_community.jar with the newly extracted java binary taken from JDK:
./Downloads/jdk-11.0.2.jdk/Contents/Home/bin/java -jar burpsuite_community.jar
This time after opening Instagram app we get a different message from Alerts tab.
Now we get a different (fatal) alert: bad_certificate which tells us that the certificate provided by Burp is not accepted by the client. We have to dig deeper into the app internals to get around this issue.
Patching Native Layer
Android applications can interact with native (C/C++) code using Java Native Interface (JNI). You can read more about it here. Instagram loads many native libraries from /data/data/com.instagram.android/lib-zstd which is created after the first app launch.
~ adb pull /data/data/com.instagram.android/lib-zstd ~ grep lib-zstd -re fizz Binary file lib-zstd/libliger.so matches
Bingo! Let’s launch IDA Pro to take a look at this shared object file. After reading source code, I spotted the exception which was causing this bad_certificate issue.
Let’s search for strings using IDA (View > Open Subviews > Strings).
At offset 002831F4 on read-only section (.rodata) we can see the constant we were looking for.
IDA is pointing us to the subroutine sub_3C864 which is using it. After analysing the flow, we can apply a simple patch at the offset 0003CD4D patching JNZ to JZ so exception is no longer thrown!
Let’s apply the patches (Edit > Patch Program > Apply patches to input file) and push the newly patched libliger.so to the device.
adb push libliger.so /data/data/com.instagram.android/lib-zstd/libliger.so
Now Burp complains with a weird alert:
That’s weird. Analysing traffic with Wireshark didn’t help much and gave me no additional clues. Next step was to debug Android smali code using Android Studio (you can find an useful article here).
I followed this StackOverflow reply to catch any exception and this shows up shortly after:
This looks interesting. Let’s go back to IDA and search for the string constant “openssl cert verify error“. Match on offset 00295732 used by subroutine sub_176434. Similarly to what we’ve dove before, we can patch this subroutine to avoid throwing this exception.
Patch JNZ to JZ, apply to input file and open Burp.
Jackpot! We can now be the man in the middle and take a look at the “private” Instagram API.