Skip to content

Fix Android 16 CT enforcement, TrustManagerImpl fallback, and native TLS null pointer crash#178

Closed
JRBusiness wants to merge 2 commits intohttptoolkit:mainfrom
JRBusiness:fix/android16-ct-bypass-and-tls-fixes
Closed

Fix Android 16 CT enforcement, TrustManagerImpl fallback, and native TLS null pointer crash#178
JRBusiness wants to merge 2 commits intohttptoolkit:mainfrom
JRBusiness:fix/android16-ct-bypass-and-tls-fixes

Conversation

@JRBusiness
Copy link

@JRBusiness JRBusiness commented Feb 19, 2026

Summary

Fixes httptoolkit/httptoolkit#855

Follow-up to PR #177 (Frida 17.7.3 upgrade). After upgrading Frida, three new issues appeared on Android 16 (API 36) devices:

1. Certificate Transparency enforcement (NOT_ENOUGH_SCTS)

Android 16 enforces CT verification via com.android.org.conscrypt.ct.CertificateTransparency.checkCT(). The proxy's MITM certificates lack Signed Certificate Timestamps since they are generated locally. Added targeted hooks in android-certificate-unpinning.js:

  • isCTVerificationRequired -> returns false
  • checkCT -> no-op (all overloads)

This is consistent with how other pinning mechanisms are already disabled -- all traffic is routed through the local proxy, making CT checks unnecessary.

Reference: Android CT Policy, Conscrypt source

2. TrustManagerImpl trust anchor failures

TrustManagerImpl.verifyChain and checkTrustedRecursive throw Trust anchor for certification path not found on some Android 16 code paths, despite the certificate being injected into TrustedCertificateIndex. Added pattern recognition in android-certificate-unpinning-fallback.js to catch these specific Conscrypt methods and patch them to accept chains signed by the proxy CA.

Reference: frida-interception-and-unpinning#88

3. Native TLS hook null pointer crash (access violation accessing 0x0)

SSL_get0_peer_certificates() returns null for some connections, and the app's own verification callback (realCallback) also crashes when SSL internal state is invalid. Added:

  • Null ssl pointer guard at callback entry
  • try-catch around all NativeFunction calls (realCallback, SSL_get0_peer_certificates, crypto_buffer_len, crypto_buffer_data)
  • Graceful fallback to SSL_VERIFY_INVALID on any native exception

Changes

  • overrides/frida/android/android-certificate-unpinning.js: Add CertificateTransparency CT bypass hooks
  • overrides/frida/android/android-certificate-unpinning-fallback.js: Add TrustManagerImpl.verifyChain and checkTrustedRecursive fallback recognition
  • overrides/frida/native-tls-hook.js: Add null pointer guards and try-catch around all native calls in verification callback

Test Plan

  • Tested on Google Pixel 8, Android 16, API 36, rooted with Magisk 30.6
  • No more NOT_ENOUGH_SCTS Certificate Transparency errors
  • No more Trust anchor for certification path not found errors
  • access violation accessing 0x0 crash handled gracefully
  • Verify no regressions on older Android versions (pre-16)

…6 support


Frida 17.5.1 fails on Android 16 (API 36) devices due to ART runtime changes
that removed `copied_methods_offset_` from the Class structure. This causes
two distinct failures:

1. The bundled frida-java-bridge v7.0.10 crashes with:
   "Unable to find copied methods in java/lang/Thread; please file a bug"

2. frida-server 17.5.1 itself has transport-level incompatibilities on
   newer Android builds.

Changes:
- Bump FRIDA_VERSION from 17.5.1 to 17.7.3 with updated SRI hashes
- Rebuild frida-java-bridge.js from v7.0.10 to v7.0.12 (uses JVMTI-based
  method enumeration instead of relying on removed ART internals)

Tested on Google Pixel 8, Android 16, API 36.

Fixes httptoolkit/httptoolkit#854
…er certs crash


Three issues discovered after upgrading Frida to 17.7.3 on Android 16 (API 36):

1. Android 16 enforces Certificate Transparency (CT) via Conscrypt's
   CertificateTransparency.checkCT(). Proxy MITM certs lack SCTs, causing
   NOT_ENOUGH_SCTS failures. Fixed by hooking isCTVerificationRequired
   (return false) and checkCT (no-op) in the unpinning script.

2. TrustManagerImpl.verifyChain and checkTrustedRecursive throw "Trust
   anchor for certification path not found" on some Android 16 code paths.
   Added recognition in the fallback patcher to accept chains signed by
   the proxy CA certificate.

3. SSL_get0_peer_certificates() and realCallback() crash with "access
   violation accessing 0x0" for some connections. Added null ssl pointer
   guard and try-catch around all NativeFunction calls in the verification
   callback to gracefully handle invalid SSL states.

Tested on Google Pixel 8, Android 16, API 36.

Fixes httptoolkit/httptoolkit#855
Copy link
Member

@pimterry pimterry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added comments here, but the main one really is that this PR should go to https://github.com/httptoolkit/frida-interception-and-unpinning/. That's where the scripts are defined & managed. The code here is just copied across, they shouldn't be directly modified here.

Given that I'll close this PR. There's definitely useful things I'd here I'd like to include though, so if you'd like to open a new PR against that repo with these changes and reply to my comments there, that would be very welcome.

}
} else if (
className === 'com.android.org.conscrypt.TrustManagerImpl' &&
(methodName === 'verifyChain' || methodName === 'checkTrustedRecursive')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is for handling cases where we can't see the class or method names at all due to obfuscation. When we do have them like this, we should just put the hook into the normal hooks list in android-certificate-unpinning.js instead.

Do you know why the existing TrustedCertificateIndex patch isn't handling this case? It would be very useful to look at the Conscrypt code and see what's changed there, as it might be possible to just fix that to handle this instead.

{
methodName: 'checkCT',
overload: '*',
replacement: () => NO_OP
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually already implemented in httptoolkit/frida-interception-and-unpinning#166 (although that hasn't been pulled into this repo yet).

The isCTVerificationRequired hook wasn't added there though. I'm open to that if there's a good reason, can you explain why that's needed?


if (!peerCerts || peerCerts.isNull()) {
if (!alreadyHaveLock) pendingCheckThreads.delete(threadId);
return (realResult !== false) ? realResult : SSL_VERIFY_INVALID;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like some of this could be better handled with a large try/finally around a larger block.

Are you adding these checks blindly to every possible call, or because you've found specific cases where these can fail? It would be useful to know what those cases are if so.

@pimterry pimterry closed this Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: With the Frida Updated to 17.7.3 New bug introduced.

2 participants