mirror of
https://github.com/httptoolkit/frida-interception-and-unpinning.git
synced 2026-10-29 02:02:28 +01:00
Fix native TLS hook, by calling callback only for cronet
Previously we blacklisted boringssl, but did call it for others, including Conscrypt, where it seems that this can actually result in JNI crashes on Android due to exception side effects. We now whitelist cronet instead - everywhere else, the callback is skipped and we override the result directly.
This commit is contained in:
+6
-5
@@ -191,11 +191,12 @@ function patchTargetLib(targetModule, targetName) {
|
||||
}
|
||||
pendingCheckThreads.add(threadId);
|
||||
|
||||
if (targetName !== 'libboringssl.dylib') {
|
||||
// Cronet assumes its callback is always called, and crashes if not. iOS's BoringSSL
|
||||
// meanwhile seems to use some negative checks in its callback, and rejects the
|
||||
// connection independently of the return value here if it's called with a bad cert.
|
||||
// End result: we *only sometimes* proactively call the callback.
|
||||
if (targetName === 'libsscronet.so') {
|
||||
// Cronet assumes its callback is always called, and crashes if not, so it's
|
||||
// the one library where we call it up front regardless of what we decide.
|
||||
// For Conscrypt (libssl) the callback returns with a pending Java exception
|
||||
// and iOS's BoringSSL rejects bad certs via callback side-effects, so we must
|
||||
// not call the callback in those cases.
|
||||
realResult = realCallback(ssl, out_alert);
|
||||
}
|
||||
|
||||
|
||||
@@ -331,4 +331,46 @@ describe('Test Android unpinning', function () {
|
||||
|
||||
});
|
||||
|
||||
describe("given native TLS interception only", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await launchFrida([
|
||||
'./test/android/tmp/config.js', // Our custom config
|
||||
// Just enough to reach the proxy, plus the native TLS hook. No system certificate
|
||||
// injection and no Java unpinning, so the native hook is the only reason the app
|
||||
// trusts our CA at all - which is what makes this the scenario that exercises it.
|
||||
'./native-connect-hook.js',
|
||||
'./android/android-proxy-override.js',
|
||||
'./native-tls-hook.js'
|
||||
]);
|
||||
});
|
||||
|
||||
it("should intercept TLS that the platform itself would reject", async () => {
|
||||
// This ensures native TLS interception handles Conscrypt correctly, which is tricky
|
||||
// as its callback has exception-throwing side effects (see #218). Note that this
|
||||
// alone does cover config-pinned, context-pinned, Volley & TrustKit requests.
|
||||
await testAllButtons('Success', {
|
||||
exceptions: [
|
||||
// Pinned above the TLS layer, in Java, so a native trust hook can't help:
|
||||
// these need the Java-level unpinning scripts.
|
||||
'OKHTTP PINNED REQUEST',
|
||||
'APPMATTUS CT REQUEST',
|
||||
'APPMATTUS+OKHTTP CT REQUEST',
|
||||
// WebView does its networking in its own sandboxed process, which our scripts
|
||||
// are not attached to at all:
|
||||
'UNPINNED WEBVIEW REQUEST',
|
||||
// HTTP/3 is blocked outright by the config, and this one doesn't fall back to
|
||||
// anything we've made trusted:
|
||||
'UNPINNED HTTP/3 REQUEST',
|
||||
// Flutter bundles its own TLS stack & CA store, and ignores the proxy settings,
|
||||
// so it connects out directly & untouched:
|
||||
'FLUTTER REQUEST',
|
||||
// This checks the certificate itself, by hand, at the lowest level:
|
||||
'RAW CUSTOM-PINNED REQUEST'
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user