httptoolkit / frida-interception-and-unpinning

Frida scripts to directly MitM all HTTPS traffic from a target mobile application
https://httptoolkit.com/android/
GNU Affero General Public License v3.0
905 stars 178 forks source link

Exceptions for Blizzard Messenger aren't patched properly #39

Open yoshimo opened 11 months ago

yoshimo commented 11 months ago

com.blizzard.messenger their chat and authentication app isn't unpinned completely . The log says:

Unpinning Android app...
[+] SSLPeerUnverifiedException auto-patcher
[+] HttpsURLConnection (setDefaultHostnameVerifier)
[+] HttpsURLConnection (setSSLSocketFactory)
[+] HttpsURLConnection (setHostnameVerifier)
[+] SSLContext
[+] TrustManagerImpl
[+] OkHTTPv3 (list)
[ ] OkHTTPv3 (cert)
[+] OkHTTPv3 (cert array)
[+] OkHTTPv3 ($okhttp)
[ ] Trustkit OkHostnameVerifier(SSLSession)
[ ] Trustkit OkHostnameVerifier(cert)
[ ] Trustkit PinningTrustManager
[ ] Appcelerator PinningTrustManager
[ ] OpenSSLSocketImpl Conscrypt
[ ] OpenSSLEngineSocketImpl Conscrypt
[ ] OpenSSLSocketImpl Apache Harmony
[ ] PhoneGap sslCertificateChecker
[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string)
[ ] IBM MobileFirst pinTrustedCertificatePublicKey (string array)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSocket)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (cert)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (string string)
[ ] IBM WorkLight HostNameVerifierWithCertificatePinning (SSLSession)
[ ] Conscrypt CertPinManager
[ ] CWAC-Netsecurity CertPinManager
[ ] Worklight Androidgap WLCertificatePinningPlugin
[ ] Netty FingerprintTrustManagerFactory
[ ] Squareup CertificatePinner (cert)
[ ] Squareup CertificatePinner (list)
[ ] Squareup OkHostnameVerifier (cert)
[ ] Squareup OkHostnameVerifier (SSLSession)
[+] Android WebViewClient (SslErrorHandler)
[ ] Android WebViewClient (WebResourceError)
[ ] Apache Cordova WebViewClient
[ ] Boye AbstractVerifier
[ ] Appmattus (CertificateTransparencyInterceptor)
[ ] Appmattus (CertificateTransparencyTrustManager)
Unpinning setup completed
---
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing OkHTTPv3 ($okhttp): account.battle.net
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing Trustmanager (Android < 7) request
  --> Unexpected SSL verification failure, adding dynamic patch...
      Thrown by okhttp3.internal.connection.RealConnection->connectTls
      Attempting to patch automatically...
      [+] okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
  --> Bypassing Trustmanager (Android < 7) request
  --> Bypassing okhttp3.internal.connection.RealConnection->connectTls (automatic exception patch)
Process crashed: kotlin.KotlinNullPointerException

***
FATAL EXCEPTION: OkHttp Dispatcher
Process: com.blizzard.messenger, PID: 23849
kotlin.KotlinNullPointerException
        at okhttp3.internal.connection.RealConnection.isHealthy(RealConnection.kt:635)
        at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:117)
        at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:76)
        at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:245)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:82)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:100)
        at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:197)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:502)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)

maybe the okhttp3 auto patch has unwanted side-effects.

pimterry commented 11 months ago

Hi @yoshimo - the automated exception patcher basically exists as a fallback, which is likely to fail in many cases it's used, but exists because a) in those cases, you're already in big trouble, and b) the failures it returns give you a good idea what's going on to investigate the real issues involved more closely.

All it does is wait for any method to throw a SSLPeerUnverifiedException, and then delete the body of that method (basically replacing it with just return null). Once that exception has been thrown, your certificate is already untrusted, but in quite a few cases it turns out that just skipping methods that throw this error can solve some issues even for totally unrecognized unknown certificate pinning techniques.

Unfortunately, that's not true in all cases, and this is a last-ditch fallback. It's totally possible that it can break things, as here. Regardless though, a certificate error was being thrown before this patch was applied, so you're already in trouble anyway.

In practice, in both this case and #38, the automated exception patcher is indeed running, and it looks like it's patching two methods that don't have existing patches: in this issue it's patching okhttp3.internal.connection.RealConnection->connectTls, and in #38 it's okhttp3.internal.tls.BasicCertificateChainCleaner->clean. That means an SSLPeerUnverifiedException was thrown within those methods, so even before the patcher fires, you're in trouble.

I think the source for the methods in question is here and here.

For connectTls, I think this must mean that the hostname of your certificate wasn't verified at all. It's not that it's not trusted or hitting pinning issues - the certificate isn't valid somehow. E.g. the client could be requesting google.com and getting a certificate for bing.com. You could write a Frida hook to disable this, but it's quite unusual, and so I wouldn't normally include that in the script here. That probably means something unusual is happening with your interception setup, but you'd need to investigate further to work out exactly what.

For clean, it looks like this means either the TrustRootIndex doesn't contain the certificate you're using, and that causes issues. We don't have a hook for this, and maybe we should? I'd be open to adding one, if you think there's a good approach for that. I'm not sure exactly how this is used though myself, and the comments suggest that OkHttp normally uses AndroidCertificateChainCleaner instead of this class. We might be able to get away with just returning the first argument as the result?

yoshimo commented 11 months ago

connectTls; might be connected to additional fingerprints loaded from http://nydus.battle.net/Bnet/zxx/client/bgs-key-fingerprint.

clean , we could at least try changing the return but i have yet to understand what goes on in this process.

pimterry commented 11 months ago

connectTls; might be connected to additional fingerprints loaded from http://nydus.battle.net/Bnet/zxx/client/bgs-key-fingerprint.

I don't think that's likely - as far as I can tell it's literally testing that the certificate it receives matches the hostname it was looking for. That one doesn't seem much related to certificate pinning.

With a bit of fiddling, you might be able to debug that part with Wireshark or similar, which will show you the raw TLS handshakes. If you can find the aborted handshake there, it will show you which hostname the client asked for and which certificate it received. Or of course you could use Frida to add logging for that into this method directly.

clean , we could at least try changing the return but i have yet to understand what goes on in this process.

Yep, that might well work, but you'll need to test that out and do some debugging. Given that fingerprint file, I wouldn't be surprised if that's used to build a TrustRootIndex used here - looks like BasicCertificateChainCleaner takes that in its constructor, and a custom one could plausibly be used to implement something sort-of similar to certificate pinning here. It's a bit unusual but not impossible.

Play around with the options, you might be able to avoid this by disabling using custom chain cleaners entirely, or always replacing the TrustRootIndex with a custom option (trusting the system's installed certs, or trusting all certs, or similar). Hard to know without getting into the details though I'm afraid, some proper reverse engineering is going to be required here.