解决APP抓包问题「网络安全」

1.前言

在日常渗透过程中我们经常会遇到瓶颈无处下手,这个时候如果攻击者从APP进行突破,往往会有很多惊喜。但是目前市场上的APP都会为防止别人恶意盗取和恶意篡改进行一些保护措施,比如模拟器检测、root检测、APK加固、代码混淆、代码反调试、反脱壳、签名校验等等对抗机制。

而测试人员对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.pem
openssl 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 /sdcard

adb shell 
mount -o remount,rw /system
cp /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 -nodes
openssl 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,而是使用默认的X509TrustManager
trustManagerFactory.init(keyStore);
// 用 TrustManager 初始化一个 SSLContext
sslContext = 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 methods
by Maurizio Siddu modify by Ch3nYe

Run 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 TrustManager
name: '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 class
var SSLContext_init = SSLContext.init.overload(
'[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
try {
// Override the init method, specifying the custom TrustManager
SSLContext_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 app
var 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请求也不会手足无措了。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/18904.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

程序员痛心流涕自述:“因为把自己代码给了别人,我亲手断送了自己的前程”

在求职的过程中&#xff0c;一般都会有投递简历、笔试、面试以及背调的环节&#xff0c;而在这几个环节中折戟沉沙的人也着实不少。 不少人觉得&#xff0c;在求职时简历需要优化&#xff0c;背调不能有瞒报、捏造的情况&#xff0c;而笔试面试则是纯纯的要靠硬实力。 虽然说…

Springboot +Flowable,服务任务ServiceTask执行的三种方式(二)

一.简介 ServiceTask 从名字上看就是服务任务&#xff0c;它的图标是像下面这样&#xff0c;截图如下&#xff1a; ServiceTask 一般由系统自动完成&#xff0c;当流程走到这一步的时候&#xff0c;不会自动停下来&#xff0c;而是会去执行我们提前在 ServiceTask 中配置好的…

Linux - 第12节 - 网络编程套接字

1.预备知识 1.1.理解源IP地址和目的IP地址 因特网上的每台计算机都有一个唯一的IP地址&#xff0c;如果一台主机上的数据要传输到另一台主机&#xff0c;那么对端主机的IP地址就应该作为该数据传输时的目的IP地址。但仅仅知道目的IP地址是不够的&#xff0c;当对端主机收到该数…

【新星计划-2023】什么是ARP?详解它的“解析过程”与“ARP表”。

一、什么是ARP ARP&#xff08;地址解析协议&#xff09;英文全称“Address Resolution Protocol”&#xff0c;是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机&#xff0c;并接收返回消息&#xff0c;以此确…

Linux 中的文件锁定命令:flock、fcntl、lockfile、flockfile

在 Linux 系统中&#xff0c;文件锁定是一种对文件进行保护的方法&#xff0c;可以防止多个进程同时访问同一个文件&#xff0c;从而导致数据损坏或者冲突。文件锁定命令是一组用于在 Linux 系统中实现文件锁定操作的命令&#xff0c;它们可以用于对文件进行加锁或解锁&#xf…

Android UI深度理解:Activity UI视图结构

Activity UI视图结构 每个Activity都会获得一个窗口&#xff0c;那就是Window&#xff0c;它用于绘制用户的UI界面 Window是一个抽象类&#xff0c;提供了绘制窗口的一组通用API&#xff0c;PhoneWindow是它的唯一实现类 DecorView是所有应用窗口的根节点。是FrameLayout的子类…

windows下python下载及安装

下载python安装包 进入python官网&#xff1a;https://www.python.org/ 鼠标移动到“Downloads”->"Windows"上&#xff0c;可以看到最新版本是3.11.3版本 点击“Windows”按钮&#xff0c;可以去下载其他版本 标记为embeddable package的表示嵌入式版本&#x…

【C语言】通讯录(文件版)

前言 前面我们完成了通讯录的静态版本和动态版本&#xff0c;虽然功能已经比较完善了&#xff0c;但是前面的通讯录缺少了存储联系人的能力&#xff0c;所以我们学习了文件的操作管理&#xff0c;这里我们就用上一篇文章的知识来完成这次的文章吧。 关于通讯录的前两篇文章我放…

Windows无法完成格式化怎么办?正确的3个解决方法!

案例&#xff1a;Windows无法完成格式化怎么办 【由于我的U盘使用时间过长&#xff0c;很多文件都是不需要的&#xff0c;我想将其格式化&#xff0c;但插入电脑后&#xff0c;Windows根本无法完成格式化&#xff0c;这是为什么呢&#xff1f;我应该怎么做呢&#xff1f;求答案…

java版工程项目管理系统源码+系统管理+系统设置+项目管理+合同管理+二次开发

工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#xff1a;实现对数据字典标签的增删改查操作 2、编码管理&#xff1a;实现对系统编码的增删改查操作 3、用户管理&#xff1a;管理和查看用户角色 4、菜单管理&#xff1a;实现对系统菜单的增删改查操…

【c语言小demo】登录demo | 账号密码验证功能

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ …

Linkage Mapper解密数字世界链接 专栏内容介绍

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Linkage Mapper 解密数字世界链接 在数字时代&#xff0c;链接是信息的核心&#xff0c;链接地…

一文读懂ChatGPT(全文由ChatGPT撰写)

最近ChatGPT爆火&#xff0c;相信大家或多或少都听说过ChatGPT。到底ChatGPT是什么&#xff1f;有什么优缺点呢&#xff1f; 今天就由ChatGPT自己来给大家答疑解惑~ 全文文案来自ChatGPT&#xff01; 01 ChatGPT是什么 ChatGPT是一种基于人工智能技术的自然语言处理系统&…

Leetcode507. 完美数

Every day a leetcode 题目来源&#xff1a;507. 完美数 解法1&#xff1a;枚举 我们可以枚举 num 的所有真因子&#xff0c;累加所有真因子之和&#xff0c;记作 sum。若 sumnum 则返回 true&#xff0c;否则返回 false。 枚举范围从 [1, sum) 的话&#xff0c;会超时&…

13 KVM虚拟机配置-配置虚拟设备(总线配置)

文章目录 13 KVM虚拟机配置-配置虚拟设备&#xff08;总线配置&#xff09;13.1 概述13.2 元素介绍13.3 配置示例 13 KVM虚拟机配置-配置虚拟设备&#xff08;总线配置&#xff09; 13.1 概述 总线是计算机各个部件之间进行信息通信的通道。外部设备需要挂载到对应的总线上&a…

Elasticsearch的索引库和文档操作、RestClient的索引库和文档操作

一、Elasticsearch Linux系统通过Docker安装Elasticsearch、部署kibana 1.Elasticsearch Elasticsearch 是位于 Elastic Stack 核心的分布式搜索和分析引擎。Logstash 和 Beats 有助于收集、聚合和丰富您的数据并将其存储在 Elasticsearch 中。Kibana 使您能够以交互方式探索…

MySQL查询分组Group By原理分析

目录 1. 使用group by的简单例子2. group by 原理分析2.1 explain 分析2.2 group by 的简单执行流程 3. where 和 having的区别3.1 group by where 的执行流程3.2 group by having 的执行3.3 同时有where、group by 、having的执行顺序3.4 where having 区别总结 4. 使用 gr…

【DRF配置管理】如何建立swagger风格api接口文档

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 DRF应用和管理 【DRF配置管理】Django安装和使用DRF框架 【DRF配置管理】如何在视图函数配置参数(一) 【DRF配置管理】如何在视图函数配置参数(二) 【…

基于狮群算法优化的核极限学习机(KELM)分类算法-附代码

基于狮群算法优化的核极限学习机(KELM)分类算法 文章目录 基于狮群算法优化的核极限学习机(KELM)分类算法1.KELM理论基础2.分类问题3.基于狮群算法优化的KELM4.测试结果5.Matlab代码 摘要&#xff1a;本文利用狮群算法对核极限学习机(KELM)进行优化&#xff0c;并用于分类 1.KE…

HAL库版FreeRTOS(中)

目录 FreeRTOS 任务切换PendSV 异常PendSV 中断服务函数FreeRTOS 确定下一个要运行的任务函数vTaskSwitchContext()函数taskSELECT_HIGHEST_PRIORITY_TASK() PendSV 异常何时触发FreeRTOS 时间片调度实验功能设计软件设计下载验证 FreeRTOS 内核控制函数FreeRTOS 内核控制函数预…