In late Jan, someone opened an Github issue in the objection repository about Android 7’s Network Security Configuration. The issue author included a blogpost from the NCC group about this very topic which included some very helpful bits of information (which you should totally read).

Naturally, I wanted to enhance objection to be able to get past this new security feature, so the testing began. I installed a Burp CA as one would normally do for assessments as well as a small test application with certificate pinning disabled and quickly realised that literally no network traffic was passing through. Inspecting the output of adb logat , one would see messages such as the following for our failed requests:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Meh. If I disable Burp, letting traffic flow through untouched, everything worked ok. Re-enabling Burp gets me back to this broken state. Not good.

As per the documentation for Android 7, I updated my sample applications AndroidManifest.xml to include a networkSecurityConfig attribute within the <application> tag, together with the following xml definition:

Running this application saw me being able to once again intercept HTTPS traffic as one is used to in older Android environments. But that is the path a developer would take. As hackers, we need another (maybe faster :P) way.

repackaging enhancements

Considering objection already has an Android patcher with a number of features, adding this was a breeze. All that was required was to parse the AndroidManifest.xml again, check for the networkSecurityConfig tag and add it if needed. Finally, copy over a sample network_security_config.xml (like the screenshot above) and repackage the app as normal. The resultant method can be seen here: https://github.com/sensepost/objection/blob/d53ff2c4dec31d3cf5ea6dd8c657e36777ad16da/objection/utils/patchers/android.py#L433-L484

A simple job, mostly because I could easily reuse parts within the patcher class that was already doing lots of the heavy lifting.

runtime enhancements

Being able to permanently modify an APK is cool and all, but an even cooler party trick would be the ability to bypass this silly restriction at runtime! Referring back to the NCC blogpost linked earlier, the author goes into some nice detail about the methods he found when tracing how the validations are being done for these new checks. I was able replicate most of it, apart from the fact that the hooks supplied were hooking overloads that I simply did not have. Apart from that, the provided Frida script worked fine as long as my application did not already instantiate a DefaultConfigSource instance, or had not called getDefaultBuilder yet. This means if I hook the application too late, I may completely miss the opportunity to bypass these new restrictions at runtime.

So how can we improve on this?

To answer this question, the first thing to do was to get a nice, full stack trace of exactly how and where that CertPathValidatorException was occurring. This should give us a clear indication on the code flow which in turn will give us ideas on potential hooking locations for Frida.

Getting a stack trace was easy. I simply removed the networkSecyrityConfig changes I had made earlier to my sample app and ran the app again with Burp’s interception enabled.

Holy stack trace batman! The screenshot is actually just the first of 3 exceptions that were thrown. JavaIsFun.exe! If you were to run this app on a phone, the output of adb logcat would also have dumped this exception.

Anyways, the important part in the exception is this:

Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

The verifyChain method in the com.android.org.conscrypt.TrustManagerImpl class is the one that causes the explosion it seems. A quick google for this class would give you the source code, where one could see that numerous code paths lead to verifyChain being called.

After some more testing, I concluded that this might be a prime candidate for some hooking! Before hooking though, it is important to study the method you plan on messing with first. In this case, the method signature for verifyChain was the first thing to inspect, which looked as follows:

private List<X509Certificate> verifyChain(List<X509Certificate> untrustedChain, List<TrustAnchor> trustAnchorChain, String host, boolean clientAuth, byte[] ocspData, byte[] tlsSctData) throws CertificateException

More hints on how it is called as well as which types of arguments it takes can be seen in other methods that call verifyChain within TrustManagerImpl which actually makes understanding this one a breeze.

Inspecting the actual implementation of verifyChain is also important. After having a quick (*cough*) read through it, it looked like it would simply add the untrusted cert to existing trusted ones and return those. As the method progresses, checks were being made for validity which could result in the exception we are trying avoid being thrown.

So, for the hook, all I needed to do was replace the implementation of verifyChain and return the original untrusted chain again. This results in a really simple Frida hook similar to the following:

Java.perform(function () { var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl'); TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) { // Skip all the logic and just return the chain again :P return untrustedChain; } });

Running this a raw Frida hook on my sample application saw the network traffic hit Burp as no exceptions were being thrown, making traffic manipulation possible once again.

conclusion