mirror of
https://github.com/httptoolkit/frida-interception-and-unpinning.git
synced 2026-10-11 10:22:12 +02:00
This is covered more effectively by our injection of our system certificate into the default trust store for all Conscrypt implementations via the index.
70 lines
3.4 KiB
JavaScript
70 lines
3.4 KiB
JavaScript
/**
|
|
* Once we have captured traffic (once it's being sent to our proxy port) the next step is
|
|
* to ensure any clients using TLS (HTTPS) trust our CA certificate, to allow us to intercept
|
|
* encrypted connections successfully.
|
|
*
|
|
* This script does so by attaching to the internals of Conscrypt (the Android SDK's standard
|
|
* TLS implementation) and pre-adding our certificate to the 'already trusted' cache, so that
|
|
* future connections trust it implicitly. This ensures that all normal uses of Android APIs
|
|
* for HTTPS & TLS will allow interception.
|
|
*
|
|
* This does not handle all standalone certificate pinning techniques - where the application
|
|
* actively rejects certificates that are trusted by default on the system. That's dealt with
|
|
* in the separate certificate unpinning script.
|
|
*/
|
|
|
|
Java.perform(() => {
|
|
// First, we build a JVM representation of our certificate:
|
|
const String = Java.use("java.lang.String");
|
|
const ByteArrayInputStream = Java.use('java.io.ByteArrayInputStream');
|
|
const CertFactory = Java.use('java.security.cert.CertificateFactory');
|
|
|
|
const certFactory = CertFactory.getInstance("X.509");
|
|
const certBytes = String.$new(CERT_PEM).getBytes();
|
|
const cert = certFactory.generateCertificate(ByteArrayInputStream.$new(certBytes));
|
|
|
|
// Then we hook TrustedCertificateIndex. This is used for caching known trusted certs within Conscrypt -
|
|
// by prepopulating all instances, we ensure that all TrustManagerImpls (and potentially other
|
|
// things) automatically trust our certificate specifically (without disabling validation entirely).
|
|
// This should apply to Android v7+ - previous versions used SSLContext & X509TrustManager.
|
|
[
|
|
'com.android.org.conscrypt.TrustedCertificateIndex',
|
|
'org.conscrypt.TrustedCertificateIndex', // Might be used (com.android is synthetic) - unclear
|
|
'org.apache.harmony.xnet.provider.jsse.TrustedCertificateIndex' // Used in Apache Harmony version of Conscrypt
|
|
].forEach((TrustedCertificateIndexClassname, i) => {
|
|
let TrustedCertificateIndex;
|
|
try {
|
|
TrustedCertificateIndex = Java.use(TrustedCertificateIndexClassname);
|
|
} catch (e) {
|
|
if (i === 0) {
|
|
throw new Error(`${TrustedCertificateIndexClassname} not found - could not inject system certificate`);
|
|
} else {
|
|
// Other classnames are optional fallbacks
|
|
return;
|
|
}
|
|
}
|
|
|
|
TrustedCertificateIndex.$init.overloads.forEach((overload) => {
|
|
overload.implementation = function () {
|
|
this.$init(...arguments);
|
|
// Index our cert as already trusted, right from the start:
|
|
this.index(cert);
|
|
}
|
|
});
|
|
|
|
TrustedCertificateIndex.reset.overloads.forEach((overload) => {
|
|
overload.implementation = function () {
|
|
const result = this.reset(...arguments);
|
|
// Index our cert in here again, since the reset removes it:
|
|
this.index(cert);
|
|
return result;
|
|
};
|
|
});
|
|
});
|
|
|
|
// This effectively adds us to the system certs, and also defeats quite a bit of basic certificate
|
|
// pinning too! It auto-trusts us in any implementation that uses TrustManagerImpl (Conscrypt) as
|
|
// the underlying cert checking component.
|
|
|
|
console.log('== System certificate trust injected ==');
|
|
}); |