而测试职员对APP进行渗透的首步操作常日便是上burp或者Charles这类抓包工具进行抓包,查看要求记录里的域名及链接地址是否可以进一步利用,但是如果碰着一些APP涌现证书报错或者抓不到包的情形该怎么办,读过本篇文章之后,相信你会拥有一些新的办理方案和思考。
2.数字证书我们都知道http协议传输的是明文信息,是可以直接捕获的,从而造成了数据透露。为了防止中间人的拦截,涌现了HTTPS加密机制。在HTTPS中,利用了证书+数字署名办理了这个问题。
此篇的重点在于如何应对APP的抓包对抗。

总结的HTTPS加密机制如下:
数字署名是发送方的明文经历了两次加密得到的两个东西组成,一个是hash ,一个是经由私钥加密。数字证书便是明文+数字署名。但是数字证书中的内容远不止这俩,还包括了威信机构的信息,做事器的域名,最主要的是有署名的打算方法,不然用公钥进行解密之后的hash,如何与加密明文进行比拟呢,还有证书中还包括公钥,公钥用于发放给要求证书的客户端。HTTPS便是利用SSL/TLS协议进行加密传输,让客户端拿到做事器的公钥,然后客户端随机天生一个对称加密的秘钥,利用公钥加密,传输给做事端,后续的所有信息都通过该对称秘钥进行加密解密,完玉成部HTTPS的流程。3.https抓包【逐一帮助安全学习,所有资源关注我,私信回答“资料”获取逐一】①网络安全学习路线②20份渗透测试电子书③安全攻防357页条记④50份安全攻防口试指南⑤安全红队渗透工具包⑥网络安全必备书本⑦100个漏洞实战案例⑧安全大厂内部教程
导入用户证书在第一次利用burp时,都会有这么一步,将burp的证书导出,添加进浏览器 【受信赖的根证书颁发机构】中去,这样就会信赖burp发来的要求包,也就可以要求数据进行修正。我们对APP抓包,也同样要将burp证书安装到系统证书中去,一样平常从【SD卡安装】的证书会存放在用户信赖的凭据下
但是,在Android 7.0以前,运用默认会信赖系统证书和用户证书,Android 7.0开始,默认只信赖系统证书。
以是如果你的手机是处于Android7.0以上版本的话,并且在没有绑定SSL证书的情形下,也会抓不到包,从安卓开拓的角度可以很清楚的看到这一点。
下图是我将burp证书安装到Android7.1.2的用户证书下,利用okhttp对https://ttt.com进行要求的结果。由于ttt.com的SSL证书是自署名证书,而自署名证书是不被系统默认信赖的,以是须要先将ttt.com的自署名证书添加到系统证书中才可以访问。
自署名证书的天生如下图所示:
系统证书路径:/system/etc/security/cacerts/用户证书路径:/data/misc/user/0/cacerts-added/
移动到系统根证书路径的方法:
1、导出burp.der
2、利用openssl变动证书格式,先将burp证书的der格式转成pem,再获取证书的hash
openssl x509 -inform DER -in burp.der -out burp.pemopenssl x509 -inform PEM -subject_hash_old -in burp.pem
3.移动到系统根证书目录路径下
Android根证书目录都因此pem证书的hash值+.0格式,以是要将刚才天生的pem改名为xxxx.0
mv burp.pem a5ba575.0
由于系统读写权限问题,不一定能直接上传到system目录
adb push 9a5ba575.0 /sdcardadb shell mount -o remount,rw /systemcp /sdcard/9a5ba575.0 /system/etc/security/cacerts/chmod 644 /system/etc/security/cacerts/9a5ba575.0
移动完成之后,再打开【设置】-【安全】-【信赖的凭据】验证一下
这时可以在Android7.0以上版本正常访问https://ttt.com了,其他抓包工具同理即可。
证书有效期过长还有一种情形是,导入到系统证书仍抓不到包,并且浏览器会报NET::ERR_CERT_VALIDITY_TOO_LONG缺点。
缘故原由是chrome从2018年开始只信赖有效期少于825天(27个月)的证书,而burp证书有效期过长。
办理方案是自己做一个低于27个月的root证书导入burp,再通过burp重新导出证书并放入到系统证书路径下。
openssl genrsa -out key.pem 3072 -nodesopenssl req -new -x509 -key key.pem -sha256 -config openssl.cnf -out cert.pem -days 730 -subj "/C=JP/ST=/L=/O=m4bln/CN=MY CA"openssl pkcs12 -export -inkey key.pem -in cert.pem -out cert_and_key.pfx把cert_and_key.pfx导入burp
目前还没碰着过这种情形,但是如果碰着了这种问题要知道怎么办理。
以上两种方法都是仅依赖了系统校验证书的办法进行抓包,APP在全体要求HTTPS的要求过程时还并未进行证书校验,和在普通的浏览器中访问并无差异,只是要将想要被信赖的证书放入系统证书路径内。
4.SSLPinning对付像ttt.com这种自署名的免费证书,不须要CA威信认证的证书,大多数APP开拓商都会利用。那么如果在安卓开拓的过程中,将证书的验证逻辑放在APP内部,与系统和浏览器毫无干系,这时再想将burp证书导入系统受信赖路径下也于事无补了。
APP自己校验证书,分为两种,一种是将验证逻辑也在代码中,一种是写在安卓7.0之后才有的network-security-config中。
验证是办法也有两种,一种是验证证书公钥的hash值,一种是直接验证证书的公钥文件。
这种通过APP自身的验证办法就叫做证书绑定(也叫Certificate Pinning或SSL Pinning)。
那么如何去判断一个APP是否利用了证书绑定呢?首先拿到apk文件,用apktool工具进行反编译,查看敏感文件
apktool d -s <file.apk> -o <outdir>
开拓职员常常会将网络配置的干系文件保存到指定位置,如下图就指定在了xml目录下。
以是在反编译后的res/xml目录下会有一个network_security_config.xml文件,打开看到标签,解释利用了证书绑定机制。
在配置文件中考验的两种方法
<network-security-config xmlns:tools="http://schemas.android.com/tools"> <!--许可http访问--> <base-config cleartextTrafficPermitted="true" tools:ignore="InsecureBaseConfiguration" /> <!--证书校验--> <domain-config> <domain includeSubdomains="true">www.ttt.com</domain> <trust-anchors> <certificates src="@raw/ttt"/> </trust-anchors> </domain-config> <!--公钥校验--> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">ttt.com</domain> <!--利用xml校验证书公钥的hash值--> <pin-set expiration="2099-01-01" tools:ignore="MissingBackupPin"> <pin digest="SHA-256">7VMdvZE3PGbxb0Pgf1PlCp+MI8KZ2ZC5psM8TIylNDA=</pin> </pin-set> <!--利用xml校验证书的公钥文件--> <trust-anchors> <certificates src="@raw/ttt"/> </trust-anchors> </domain-config></network-security-config>
这两种校验机制涌现一种即可,从代码中可以看出,ttt.com便是安卓自己要校验绑定的域名。
如果只是在这个文件进行校验,有两种办理方案:一是直接将文件中校验的部分或注释掉,再重新打包和署名即可,但是这过程又有些麻烦,并不是上上策,如果碰着了不能重打包的apk就尴尬了。。。二是最常用的也是最好用的frida来hook关键函数进行绕过,后面会讲解。当然有些人会直接在真机或者仿照器上安装xposed模块,但是我个人以为每次利用都要软重启,可能还会造成卡机,以是觉得还是利用frida最方便。
在代码中考验的两种方法1.利用代码校验证书的公钥hash
String hostname = "www.ttt.com";CertificatePinner certificatePinner = new CertificatePinner.Builder() .add(hostname, "sha256/7VMdvZE3PGbxb0Pgf1PlCp+MI8KZ2ZC5psM8TIylNDA=") .build();OkHttpClient client = new OkHttpClient.Builder() .certificatePinner(certificatePinner) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }).build();
2.利用代码校验证书的公钥证书文件
// 获取证书输入流InputStream openRawResource = getApplicationContext().getResources().openRawResource(R.raw.ttt); Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(openRawResource);// 创建 Keystore 包含我们的证书KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(null, null);keyStore.setCertificateEntry("ca", ca);// 创建一个 TrustManager 仅把 Keystore 中的证书 作为信赖的锚点TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // 建议不要利用自己实现的X509TrustManager,而是利用默认的X509TrustManagertrustManagerFactory.init(keyStore);// 用 TrustManager 初始化一个 SSLContextsslContext = SSLContext.getInstance("TLS"); //定义:public static SSLContext sslContext = null;sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagerFactory.getTrustManagers()[0] ) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }).build();
通过frida进行hook,这种绕过的脚本大概多,比较熟习的有JustTrustMe和DroidSSLUnpinning,他们的底层事理都是一样的,通过hook关键的验证函数,进行逻辑绕过。
frida的安装过程就不详细讲解了,网上很多教程。这里我利用的是frida 12.8.0 + frida-tools=5.3.0
这里我利用的hook.js的脚本如下:
/ Android ssl certificate pinning bypass script for various methodsby Maurizio Siddu modify by Ch3nYeRun with:frida -U -f [APP_ID] -l frida_multiple_unpinning.js --no-pause/setTimeout(function() {Java.perform(function () {console.log('');console.log('======');console.log('[#] Android Bypass for various Certificate Pinning methods [#]');console.log('======');var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');var SSLContext = Java.use('javax.net.ssl.SSLContext');// TrustManager (Android < 7) //////////////////////////////////var TrustManager = Java.registerClass({// Implement a custom TrustManagername: 'dev.asd.test.TrustManager',implements: [X509TrustManager],methods: {checkClientTrusted: function (chain, authType) {},checkServerTrusted: function (chain, authType) {},getAcceptedIssuers: function () {return []; }}});// Prepare the TrustManager array to pass to SSLContext.init()var TrustManagers = [TrustManager.$new()];// Get a handle on the init() on the SSLContext classvar SSLContext_init = SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');try {// Override the init method, specifying the custom TrustManagerSSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {console.log('[+] Bypassing Trustmanager (Android < 7) request');SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);};} catch (err) {console.log('[-] TrustManager (Android < 7) pinner not found');//console.log(err);}// OkHTTPv3 (quadruple bypass) ///////////////////////////////////try {// Bypass OkHTTPv3 {1}var okhttp3_Activity_1 = Java.use('okhttp3.CertificatePinner');okhttp3_Activity_1.check.overload('java.lang.String', 'java.util.List').implementation = function (a, b) {console.log('[+] Bypassing OkHTTPv3 {1}: ' + a);return true;};} catch (err) {console.log('[-] OkHTTPv3 {1} pinner not found');//console.log(err);}try {// Bypass OkHTTPv3 {2}// This method of CertificatePinner.check could be found in some old Android appvar okhttp3_Activity_2 = Java.use('okhttp3.CertificatePinner');okhttp3_Activity_2.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (a, b) {console.log('[+] Bypassing OkHTTPv3 {2}: ' + a);return true;};} catch (err) {console.log('[-] OkHTTPv3 {2} pinner not found');//console.log(err);}try {// Bypass OkHTTPv3 {3}var okhttp3_Activity_3 = Java.use('okhttp3.CertificatePinner');okhttp3_Activity_3.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function (a, b) {console.log('[+] Bypassing OkHTTPv3 {3}: ' + a);return true;};} catch(err) {console.log('[-] OkHTTPv3 {3} pinner not found');//console.log(err);}try {// Bypass OkHTTPv3 {4}var okhttp3_Activity_4 = Java.use('okhttp3.CertificatePinner');okhttp3_Activity_4[''].implementation = function (a, b) {console.log('[+] Bypassing OkHTTPv3 {4}: ' + a);};} catch(err) {console.log('[-] OkHTTPv3 {4} pinner not found');//console.log(err);}// Trustkit (triple bypass) ////////////////////////////////try {// Bypass Trustkit {1}var trustkit_Activity_1 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');trustkit_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) {console.log('[+] Bypassing Trustkit {1}: ' + a);return true;};} catch (err) {console.log('[-] Trustkit {1} pinner not found');//console.log(err);}try {// Bypass Trustkit {2}var trustkit_Activity_2 = Java.use('com.datatheorem.android.trustkit.pinning.OkHostnameVerifier');trustkit_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) {console.log('[+] Bypassing Trustkit {2}: ' + a);return true;};} catch (err) {console.log('[-] Trustkit {2} pinner not found');//console.log(err);}try {// Bypass Trustkit {3}var trustkit_PinningTrustManager = Java.use('com.datatheorem.android.trustkit.pinning.PinningTrustManager');trustkit_PinningTrustManager.checkServerTrusted.implementation = function () {console.log('[+] Bypassing Trustkit {3}');};} catch (err) {console.log('[-] Trustkit {3} pinner not found');//console.log(err);}// TrustManagerImpl (Android > 7) //////////////////////////////////////try {var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');TrustManagerImpl.verifyChain.implementation = function (untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {console.log('[+] Bypassing TrustManagerImpl (Android > 7): ' + host);return untrustedChain;};} catch (err) {console.log('[-] TrustManagerImpl (Android > 7) pinner not found');//console.log(err);}// Appcelerator Titanium /////////////////////////////try {var appcelerator_PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');appcelerator_PinningTrustManager.checkServerTrusted.implementation = function () {console.log('[+] Bypassing Appcelerator PinningTrustManager');};} catch (err) {console.log('[-] Appcelerator PinningTrustManager pinner not found');//console.log(err);}// OpenSSLSocketImpl Conscrypt ///////////////////////////////////try {var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');OpenSSLSocketImpl.verifyCertificateChain.implementation = function (certRefs, JavaObject, authMethod) {console.log('[+] Bypassing OpenSSLSocketImpl Conscrypt');};} catch (err) {console.log('[-] OpenSSLSocketImpl Conscrypt pinner not found');//console.log(err);}// OpenSSLEngineSocketImpl Conscrypt /////////////////////////////////////////try {var OpenSSLEngineSocketImpl_Activity = Java.use('com.android.org.conscrypt.OpenSSLEngineSocketImpl');OpenSSLSocketImpl_Activity.verifyCertificateChain.overload('[Ljava.lang.Long;', 'java.lang.String').implementation = function (a, b) {console.log('[+] Bypassing OpenSSLEngineSocketImpl Conscrypt: ' + b);};} catch (err) {console.log('[-] OpenSSLEngineSocketImpl Conscrypt pinner not found');//console.log(err);}// OpenSSLSocketImpl Apache Harmony ////////////////////////////////////////try {var OpenSSLSocketImpl_Harmony = Java.use('org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl');OpenSSLSocketImpl_Harmony.verifyCertificateChain.implementation = function (asn1DerEncodedCertificateChain, authMethod) {console.log('[+] Bypassing OpenSSLSocketImpl Apache Harmony');};} catch (err) {console.log('[-] OpenSSLSocketImpl Apache Harmony pinner not found');//console.log(err);}// PhoneGap sslCertificateChecker (https://github.com/EddyVerbruggen/SSLCertificateChecker-PhoneGap-Plugin) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////try {var phonegap_Activity = Java.use('nl.xservices.plugins.sslCertificateChecker');phonegap_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (a, b, c) {console.log('[+] Bypassing PhoneGap sslCertificateChecker: ' + a);return true;};} catch (err) {console.log('[-] PhoneGap sslCertificateChecker pinner not found');//console.log(err);}// IBM MobileFirst pinTrustedCertificatePublicKey (double bypass) //////////////////////////////////////////////////////////////////////try {// Bypass IBM MobileFirst {1}var WLClient_Activity_1 = Java.use('com.worklight.wlclient.api.WLClient');WLClient_Activity_1.getInstance().pinTrustedCertificatePublicKey.overload('java.lang.String').implementation = function (cert) {console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {1}: ' + cert);return;};} catch (err) {console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {1} pinner not found');//console.log(err);}try {// Bypass IBM MobileFirst {2}var WLClient_Activity_2 = Java.use('com.worklight.wlclient.api.WLClient');WLClient_Activity_2.getInstance().pinTrustedCertificatePublicKey.overload('[Ljava.lang.String;').implementation = function (cert) {console.log('[+] Bypassing IBM MobileFirst pinTrustedCertificatePublicKey {2}: ' + cert);return;};} catch (err) {console.log('[-] IBM MobileFirst pinTrustedCertificatePublicKey {2} pinner not found');//console.log(err);}// IBM WorkLight (ancestor of MobileFirst) HostNameVerifierWithCertificatePinning (quadruple bypass) /////////////////////////////////////////////////////////////////////////////////////////////////////////try {// Bypass IBM WorkLight {1}var worklight_Activity_1 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');worklight_Activity_1.verify.overload('java.lang.String', 'javax.net.ssl.SSLSocket').implementation = function (a, b) {console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {1}: ' + a);return;};} catch (err) {console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {1} pinner not found');//console.log(err);}try {// Bypass IBM WorkLight {2}var worklight_Activity_2 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');worklight_Activity_2.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) {console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {2}: ' + a);return;};} catch (err) {console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {2} pinner not found');//console.log(err);}try {// Bypass IBM WorkLight {3}var worklight_Activity_3 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');worklight_Activity_3.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;').implementation = function (a, b) {console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {3}: ' + a);return;};} catch (err) {console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {3} pinner not found');//console.log(err);}try {// Bypass IBM WorkLight {4}var worklight_Activity_4 = Java.use('com.worklight.wlclient.certificatepinning.HostNameVerifierWithCertificatePinning');worklight_Activity_4.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) {console.log('[+] Bypassing IBM WorkLight HostNameVerifierWithCertificatePinning {4}: ' + a);return true;};} catch (err) {console.log('[-] IBM WorkLight HostNameVerifierWithCertificatePinning {4} pinner not found');//console.log(err);}// Conscrypt CertPinManager ////////////////////////////////try {var conscrypt_CertPinManager_Activity = Java.use('com.android.org.conscrypt.CertPinManager');conscrypt_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (a, b) {console.log('[+] Bypassing Conscrypt CertPinManager: ' + a);return true;};} catch (err) {console.log('[-] Conscrypt CertPinManager pinner not found');//console.log(err);}// CWAC-Netsecurity (unofficial back-port pinner for Android<4.2) CertPinManager /////////////////////////////////////////////////////////////////////////////////////try {var cwac_CertPinManager_Activity = Java.use('com.commonsware.cwac.netsecurity.conscrypt.CertPinManager');cwac_CertPinManager_Activity.isChainValid.overload('java.lang.String', 'java.util.List').implementation = function (a, b) {console.log('[+] Bypassing CWAC-Netsecurity CertPinManager: ' + a);return true;};} catch (err) {console.log('[-] CWAC-Netsecurity CertPinManager pinner not found');//console.log(err);}// Worklight Androidgap WLCertificatePinningPlugin ///////////////////////////////////////////////////////try {var androidgap_WLCertificatePinningPlugin_Activity = Java.use('com.worklight.androidgap.plugin.WLCertificatePinningPlugin');androidgap_WLCertificatePinningPlugin_Activity.execute.overload('java.lang.String', 'org.json.JSONArray', 'org.apache.cordova.CallbackContext').implementation = function (a, b, c) {console.log('[+] Bypassing Worklight Androidgap WLCertificatePinningPlugin: ' + a);return true;};} catch (err) {console.log('[-] Worklight Androidgap WLCertificatePinningPlugin pinner not found');//console.log(err);}// Netty FingerprintTrustManagerFactory ////////////////////////////////////////////try {var netty_FingerprintTrustManagerFactory = Java.use('io.netty.handler.ssl.util.FingerprintTrustManagerFactory');//NOTE: sometimes this below implementation could be useful//var netty_FingerprintTrustManagerFactory = Java.use('org.jboss.netty.handler.ssl.util.FingerprintTrustManagerFactory');netty_FingerprintTrustManagerFactory.checkTrusted.implementation = function (type, chain) {console.log('[+] Bypassing Netty FingerprintTrustManagerFactory');};} catch (err) {console.log('[-] Netty FingerprintTrustManagerFactory pinner not found');//console.log(err);}// Squareup CertificatePinner [OkHTTP<v3] (double bypass) //////////////////////////////////////////////////////////////try {// Bypass Squareup CertificatePinner {1}var Squareup_CertificatePinner_Activity_1 = Java.use('com.squareup.okhttp.CertificatePinner');Squareup_CertificatePinner_Activity_1.check.overload('java.lang.String', 'java.security.cert.Certificate').implementation = function (a, b) {console.log('[+] Bypassing Squareup CertificatePinner {1}: ' + a);return;};} catch (err) {console.log('[-] Squareup CertificatePinner {1} pinner not found');//console.log(err);}try {// Bypass Squareup CertificatePinner {2}var Squareup_CertificatePinner_Activity_2 = Java.use('com.squareup.okhttp.CertificatePinner');Squareup_CertificatePinner_Activity_2.check.overload('java.lang.String', 'java.util.List').implementation = function (a, b) {console.log('[+] Bypassing Squareup CertificatePinner {2}: ' + a);return;};} catch (err) {console.log('[-] Squareup CertificatePinner {2} pinner not found');//console.log(err);}// Squareup OkHostnameVerifier [OkHTTP v3] (double bypass) ///////////////////////////////////////////////////////////////try {// Bypass Squareup OkHostnameVerifier {1}var Squareup_OkHostnameVerifier_Activity_1 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');Squareup_OkHostnameVerifier_Activity_1.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function (a, b) {console.log('[+] Bypassing Squareup OkHostnameVerifier {1}: ' + a);return true;};} catch (err) {console.log('[-] Squareup OkHostnameVerifier pinner not found');//console.log(err);}try {// Bypass Squareup OkHostnameVerifier {2}var Squareup_OkHostnameVerifier_Activity_2 = Java.use('com.squareup.okhttp.internal.tls.OkHostnameVerifier');Squareup_OkHostnameVerifier_Activity_2.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function (a, b) {console.log('[+] Bypassing Squareup OkHostnameVerifier {2}: ' + a);return true;};} catch (err) {console.log('[-] Squareup OkHostnameVerifier pinner not found');//console.log(err);}// Android WebViewClient (double bypass) /////////////////////////////////////////////try {// Bypass WebViewClient {1} (deprecated from Android 6)var AndroidWebViewClient_Activity_1 = Java.use('android.webkit.WebViewClient');AndroidWebViewClient_Activity_1.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function (obj1, obj2, obj3) {console.log('[+] Bypassing Android WebViewClient {1}');};} catch (err) {console.log('[-] Android WebViewClient {1} pinner not found');//console.log(err)}try {// Bypass WebViewClient {2}var AndroidWebViewClient_Activity_2 = Java.use('android.webkit.WebViewClient');AndroidWebViewClient_Activity_2.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function (obj1, obj2, obj3) {console.log('[+] Bypassing Android WebViewClient {2}');};} catch (err) {console.log('[-] Android WebViewClient {2} pinner not found');//console.log(err)}// Apache Cordova WebViewClient ////////////////////////////////////try {var CordovaWebViewClient_Activity = Java.use('org.apache.cordova.CordovaWebViewClient');CordovaWebViewClient_Activity.onReceivedSslError.overload('android.webkit.WebView', 'android.webkit.SslErrorHandler', 'android.net.http.SslError').implementation = function (obj1, obj2, obj3) {console.log('[+] Bypassing Apache Cordova WebViewClient');obj3.proceed();};} catch (err) {console.log('[-] Apache Cordova WebViewClient pinner not found');//console.log(err);}// Boye AbstractVerifier /////////////////////////////try {var boye_AbstractVerifier = Java.use('ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier');boye_AbstractVerifier.verify.implementation = function (host, ssl) {console.log('[+] Bypassing Boye AbstractVerifier: ' + host);};} catch (err) {console.log('[-] Boye AbstractVerifier pinner not found');//console.log(err);}});}, 0);
启动frida进行hook指定APP的包名
frida -U -f com.example.safehttps -l hook.js --no-pause
可以看到开启burp抓包成功。
5.开启双向校验双向校验顾名思义也便是做事器也要对客户端进行证书校验,在刚才客户端校验做事真个根本上添加一贯被校验的逻辑在里面。
首先ttt.com所在的nginx做事器要开启双向认证
开启客户真个校验后,在浏览器进行访问,会创造返回400,没有被要求的SSL证书发送,是由于浏览器正常要求不会携带证书信息去要求ttt.com
那么如何携带客户真个证书,就要利用burp来操作,将ttt.com的证书添加到TLS客户证书
这时再访问
在APP中进行绑定客户真个证书文件,一样平常是p12格式文件,会放在assets目录下或者raw目录下,client.p12会有一个密钥内置在代码中,须要找到才能添加进burp中。
这个在反编译后的目录下也能找到,常日在assets或者res/raw目录下查找,但是再导入burp这一步是须要证书密码的,比如上图能明显看到密码是123456,但是找不到证书密码怎么办,可以看一下目录下是否存在lib文件夹,如果存在的话极大可能是将密码写进so层了,这就须要你会IDA反汇编获取证书密钥了,这部分在此不详细阐述,感兴趣的朋友可以先去研究一下。
APP双向校验验证
做事器校验客户真个证书ClientSSLSocketFactory,做事端将客户真个证书进行绑定。
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init((KeyStore) null);TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));}trustManager = (X509TrustManager) trustManagers[0];OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(Objects.requireNonNull(ClientSSLSocketFactory.getSocketFactory(getApplicationContext())), Objects.requireNonNull(trustManager)) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { //强行返回true 即验证成功 return true; }}).build();
public class ClientSSLSocketFactory { private static final String KEY_STORE_PASSWORD = "123456"; // 证书密码 private static InputStream client_input; public static SSLSocketFactory getSocketFactory(Context context) { try { //客户端证书 client_input = context.getResources().getAssets().open("client.p12"); SSLContext sslContext = SSLContext.getInstance("TLS"); KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(client_input, KEY_STORE_PASSWORD.toCharArray()); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray()); sslContext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom()); return sslContext.getSocketFactory(); } catch (Exception e) { e.printStackTrace(); } finally { try { client_input.close(); } catch (IOException e) { e.printStackTrace(); } } return null; }}
双向校验时,要将SSLPinning与做事器校验客户端证书的模块同时开启。
对其就行绕过,须要先启动Frida进行hook,然后勾选上客户端证书,才可以要求成功。如下图所示:
没勾选就会要求失落败,涌现400 Bad Request,和浏览器无代理时要求的结果一样,如下图:
此时burp代理日志显示如下,SSL要求失落败
WebView证书校验
webview也是一种要求办法,相称于页面的跳转或嵌入,要求的结果会显示在主屏幕上。
对付webview证书校验,有些可以找到的脚本不一定有绕过,以是在利用的过程中要查看是否含有webview的关键信息
这里我利用的是DroidSSLUnpinning
利用效果如下:
此时的APP绑定场景为:OKhttp要求为双向校验,webview为SSLPinning校验。以是在图中的上面一行是okhttp的要求结果,下面的是webview的要求结果,此脚本双向校验仍旧可以绕过。
如果说webview再添加了客户端校验,那么在反编译apk后,须要找到webview访问域名的证书密钥,再安装进burp中即可。
6.ssl_logger通杀至此,我们可以绕过证书绑定,抓APP发出的https包了,然而上述的证书解绑hook工具仅仅是通过hook了几种绑定证书的API,不适用于新涌现或者非主流的证书绑定技能。这时,就须要神器ssl_logger
ssl_logger是用来解密SSL流量的工具,也是一款基于frida的hook工具,通过hook libssl库中的SSL_read、SSL_write等函数来实现流量解密,由于底层的实现会调用这几个函数来封装,以是可以直接解出流量数据。
r0capture是r0ysue大佬在其根本上进行改进的一款工具。
由于ssl_logger是适用于MAC和linux操作系统,以是我选择在kali上进行hook实现双向校验的app
打开抓到的1.pcap包
从图中可以看出成功的获取到了信息,是不是觉得这个工具特殊神,但是看pcap的包总归不如看burp的一览无余,这个工具就这一点不太友好。以是用哪个看你心情
7.eBPF hook 免CA证书ecapture:eBPF HOOK uprobe实现的各种用户态进程的数据捕获,无需改动原程序。这个工具也是通过hook了libssl库中SSL_write、SSL_read这两个关键的SSL加密函数的返回值,拿到明文信息,通过ebpf map通报给用户进程。在APP中,如果碰着场景是burp抓包时涌现证书报错,我以为可以考试测验一下用这个工具直接curl访问进行抓包。eBPF hook也是最近才创造的hook方法,值得我们去深入探索。
我这里用的是作者v0.1.3版本发布的工具,利用效果如下:
8.总结
在依赖系统或默认浏览器校验证书的情形下,导入burp证书为用户证书是可以抓https包的
当app支持的最小API为24(Android 7.0)或以上时,默认情形下app只信赖系统级别的证书,须要把burp变为系统证书
自署名证书作为系统证书时,有效期最长不超过825天,用户证书则没有限定
开启证书校验的APP在利用burp抓包时会报certtificate_unknown等缺点
利用frida hook绕过双向证书校验时,必须要将客户真个p12文件导入burp中
一些脚本仍绕不过可以利用ssl_logger或者逆向代码进行剖析验证逻辑,再有针对性的绕过
p12文件的密钥如果在so层,须要会用IDA进行静态剖析lib下的so文件获取关键密钥
看完本篇文章之后,相信你再面对抓不到包的APP或者是https要求也不会手足无措了。