Android App Security:
In this post, I will explore the topic of bypassing Root Certificate Authority (CA) checks in Android applications developed using the Flutter framework. Although it is important to note that bypassing these security measures is generally not recommended, I aim to provide insights into the technical aspects for educational purposes only.
Understanding Root CA Checks:
Root CA checks are a fundamental security measure in Android applications that validate the authenticity and integrity of SSL/TLS connections. These checks ensure that the server’s certificate is issued by a trusted CA and has not been tampered with. Bypassing these checks allows one to perform Man-in-the-Middle attacks, such as using BurpSuite.
Limitations in Flutter:
Flutter, as a cross-platform framework, leverages platform-specific APIs for networking operations. However, as of the latest update, Flutter does not provide direct access to low-level networking APIs, making it difficult to bypass Root CA checks solely within the Flutter framework.
Recently, I delved into Android applications developed using the Flutter framework, which was a new territory for me. While engaging in a discussion unrelated to app testing, I stumbled upon an app to analyze. Excited about the opportunity, I launched the app on my mobile device, connected it to a testing WiFi network and fired up burpsuite, and soon encountered a familiar challenge faced by app testers.
Wonderful! They’re doing something right by preventing Burps root CA from being used and I could probably bypass this with Frida. I pulled the .apk apart and found…
After a bit of reading about the Flutter Engine it seems the majority of the work is performed by “libflutter.so”. This was made more apparent after starting “frida-trace” and setting up intercept scripts for all the usual SSL methods that you’d need to bypass, although they libraries were loaded I didn’t see a single call to any of the methods and the SSL connection still failed to start.
So I dug further, after some further research I found out that Flutter bundles the BoringSSL libraries into “libflutter.so” and performs its own verification steps rather than trust the OS’s systems. It also forces the use of a known set of Root certificates which goes someway to explain why I couldn’t MITM the connection.
In summary, Flutter integrates BoringSSL into “libflutter.so” and implements its own SSL verification steps, bypassing the OS’s SSL mechanisms. The enforced use of trusted Root certificates and the challenge of analyzing the stripped library make it essential to monitor logcat output for insights into the SSL connection process. These findings are valuable for security assessments and custom SSL handling in Flutter-based Android apps.
Breaking out Ghidra and loading the shared object wasn’t much better as the library had been stripped of symbols, making it tough to find the verification functions. I fired up the app again and watched the output of logcat to see if it gave me any clues:
See the line containing “handshake.cc”? After some trawling through strings and references in Ghidra I found some methods that contained it:
The 0x160 seems to correspond to the line number in the source that generate the error, I grabbed the BoringSSL code and started looking for x509 verification functions. One cropped up in “ssl_x509.cc”:
Searching Ghidra for this file name showed up the full path string as expected, tracing cross-references to that string dumped me in the middle of a call to “FUN_00316500” above with a line number value that roughly matched the source file. Bingo!
To get this check to pass all I’d have to do would be:
- Calculate the actual address of the function in the phones memory
- Build a Frida Interceptor script to trap it
- Alter the return value to “true”
Calculating the offset of the function could be done by finding the Virtual address of a function we know the name of, working out the target functions offset from it and then adding that to the the actual address of the first function.
The Flutter shared object exports one function, “JNI_OnLoad” which is called by the Android runtime during startup, Frida could find the address of this easily so that made for a good base-function to start with. The offset of the X509 function from this could be calculated and added to the base address easily, setting up an interception for this showed repeated calls to the method whenever I forced the app to make a request:
See how the “ret” value is “0x0”? Lets patch that out:
This was definitely a much harder one to crack than most apps, I’m expecting people will hide behind that as a form of “security” and forget to secure the API’s the app interfaces with 🙂
I grabbed a few more Flutter apps and check the MD5 hashes of their “libflutter.so” files, it appears they differ in most cases which means that each app will need the offset of “ssl_crypto_x509_session_verify_cert_chain” calculating, I’m not sure if this can be automated or not given the lack of debug symbols but honestly its not that much of a problem for testing only a single app.
#FlutterApp #Androidsecurity #Applicationsecurity #infosec