一
前置知识
二
核心知识点
三
SSLPinning环境下如何抓包
上文可以了解到从 HTTP 到 HTTPS 数据在传输过程中添加了一层 加密(SSL/TLS),让我们数据流量处于加密状态,不再是明文可见。一旦 app 校验了证书的指纹信息。我们的证书不再受信任了。自然而然就无法建立连接,所以必须想办法让 app 信任,才能继续抓包。当然这个分为两种情况:
上篇文件提到了一个证书包含了很多信息,那么客户端校验的原理就是: 在APP中预先设置好证书的信息,在证书校验阶段时与服务器返回的证书信息进行比较。
公钥校验
证书校验
Host校验
private void doRequest(){new Thread(){@Overridepublic void run() {final String CA_PUBLIC_KEY = "sha256/kO7OP94daK9P8+X52s00RvJLU0SiCXA9KAg9PelfwIw=";final String CA_DOMAIN = "www.52pojie.cn";//校验公钥CertificatePinner buildPinner = new CertificatePinner.Builder().add(CA_DOMAIN, CA_PUBLIC_KEY).build();OkHttpClient client = new OkHttpClient.Builder().certificatePinner(buildPinner).build();Request req = new Request.Builder().url("https://www.52pojie.cn/forum.php").build();Call call = client.newCall(req);try {Response res = call.execute();Log.e("请求成功", "状态码:" + res.code());} catch (IOException e) {e.printStackTrace();Log.e("请求失败", "异常" + e);}}}.start();}
CertificatePinner buildPinner = new CertificatePinner.Builder().add(CA_DOMAIN, CA_PUBLIC_KEY).build();//将buildPinner 传给OkHttpclientOkHttpClient client = new OkHttpClient.Builder().certificatePinner(buildPinner).build();
public void check(String hostname, List<Certificate> peerCertificates)throws SSLPeerUnverifiedException {List<Pin> pins = findMatchingPins(hostname);if (pins.isEmpty()) return;if (certificateChainCleaner != null) {peerCertificates = certificateChainCleaner.clean(peerCertificates, hostname);}for (int c = 0, certsSize = peerCertificates.size(); c < certsSize; c++) {X509Certificate x509Certificate = (X509Certificate) peerCertificates.get(c);// Lazily compute the hashes for each certificate.ByteString sha1 = null;ByteString sha256 = null;for (int p = 0, pinsSize = pins.size(); p < pinsSize; p++) {Pin pin = pins.get(p);if (pin.hashAlgorithm.equals("sha256/")) {if (sha256 == null) sha256 = sha256(x509Certificate);if (pin.hash.equals(sha256)) return; // Success!} else if (pin.hashAlgorithm.equals("sha1/")) {if (sha1 == null) sha1 = sha1(x509Certificate);if (pin.hash.equals(sha1)) return; // Success!} else {throw new AssertionError("unsupported hashAlgorithm: " + pin.hashAlgorithm);}}}
// 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;
private void doRequest2(){X509TrustManager trustManager = new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {//服务器返回的证书X509Certificate cf = chain[0];//转换为RSA的公钥RSAPublicKey rsaPublicKey = (RSAPublicKey) cf.getPublicKey();//Base64 encodeString ServerPubkey = Base64.encodeToString(rsaPublicKey.getEncoded(), 0);Log.e("服务器端返回的证书",ServerPubkey);//读取客户端资源目录中的证书InputStream client_input = getResources().openRawResource(R.raw.pojie);CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");X509Certificate realCertificate = (X509Certificate) certificateFactory.generateCertificate(client_input);String realPubkey = Base64.encodeToString(realCertificate.getPublicKey().getEncoded(), 0);Log.e("客户端资源目录中的证书",realPubkey);cf.checkValidity();final boolean expected = realPubkey.equalsIgnoreCase(ServerPubkey);Log.e("eq = ",String.valueOf(expected));if (!expected){throw new CertificateException("证书不一致");}}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}};SSLSocketFactory factory = null;try {SSLContext sslContext = SSLContext.getInstance("SSL");sslContext.init(null,new TrustManager[]{trustManager},new SecureRandom());factory = sslContext.getSocketFactory();} catch (Exception e) {e.printStackTrace();}SSLSocketFactory finalFactory = factory;new Thread(){@Overridepublic void run() {try {OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(finalFactory, trustManager).build();Request req = new Request.Builder().url("https://www.52pojie.cn/forum.php").build();Call call = client.newCall(req);Response res = call.execute();Log.e("请求发送成功","状态码:" + res.code());} catch (IOException e) {Log.e("请求发送失败","网络异常" + e);}}}.start();}
trustManager 类实现的checkServerTrusted接口。X509TrustManager trustManager = new X509TrustManager() {...@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {...}....}
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) pinner');SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);};} catch (err) {console.log('[-] TrustManager (Android < 7) pinner not found');//console.log(err);}
private void doRequest3(){HostnameVerifier verifier = new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {if ("www.52pojie.cn".equalsIgnoreCase(hostname)){return true;}return false;}};new Thread() {@Overridepublic void run() {try {OkHttpClient client = new OkHttpClient.Builder().hostnameVerifier(verifier).build();Request req = new Request.Builder().url("https://www.52pojie.cn/forum.php").build();Call call = client.newCall(req);Response res = call.execute();Log.e("请求发送成功", "状态码:" + res.code());} catch (IOException ex) {Log.e("Main", "网络请求异常" + ex);}}}.start();}
在客户端放入证书(p12/bks),客户端向服务端发送请求时,携带证书信息,在服务端会校验客户端携带过来的证书合法性
private void doRequest4(){X509TrustManager trustManager = new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}};HostnameVerifier verify = new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}};new Thread(){@Overridepublic void run() {try {InputStream client_input = getResources().openRawResource(R.raw.client);Log.e("x",client_input.getClass().toString());SSLContext sslContext = SSLContext.getInstance("TLS");KeyStore keyStore = KeyStore.getInstance("PKCS12");keyStore.load(client_input, "demoli666".toCharArray());KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagerFactory.init(keyStore, "demoli666".toCharArray());sslContext.init(keyManagerFactory.getKeyManagers(), new TrustManager[]{trustManager}, new SecureRandom());SSLSocketFactory factory = sslContext.getSocketFactory();OkHttpClient client = new OkHttpClient.Builder().sslSocketFactory(factory, trustManager).hostnameVerifier(verify).build();Request req = new Request.Builder().url("https://xxx.xxx.xxx.xxx:443/index").build();Call call = client.newCall(req);Response res = call.execute();Log.e("请求发送成功","状态码:" + res.code());} catch (Exception e) {Log.e("请求发送失败","网络异常" + e);}}}.start();}
Java.perform(function () {function uuid(len, radix) {var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');var uuid = [], i;radix = radix || chars.length;if (len) {// Compact formfor (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix];} else {// rfc4122, version 4 formvar r;// rfc4122 requires these charactersuuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';uuid[14] = '4';// Fill in random data. At i==19 set the high bits of clock sequence as// per rfc4122, sec. 4.1.5for (i = 0; i < 36; i++) {if (!uuid[i]) {r = 0 | Math.random() * 16;uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];}}}return uuid.join('');}function storeP12(pri, p7, p12Path, p12Password) {var X509Certificate = Java.use("java.security.cert.X509Certificate")var p7X509 = Java.cast(p7, X509Certificate);var chain = Java.array("java.security.cert.X509Certificate", [p7X509])var ks = Java.use("java.security.KeyStore").getInstance("PKCS12", "BC");ks.load(null, null);ks.setKeyEntry("client", pri, Java.use('java.lang.String').$new(p12Password).toCharArray(), chain);try {var out = Java.use("java.io.FileOutputStream").$new(p12Path);ks.store(out, Java.use('java.lang.String').$new(p12Password).toCharArray())} catch (exp) {console.log(exp)}}//在服务器校验客户端的情形下,帮助dump客户端证书,并保存为p12的格式,证书密码为r0ysueJava.use("java.security.KeyStore$PrivateKeyEntry").getPrivateKey.implementation = function () {var result = this.getPrivateKey()var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');return result;}Java.use("java.security.KeyStore$PrivateKeyEntry").getCertificateChain.implementation = function () {var result = this.getCertificateChain()var packageName = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext().getPackageName();storeP12(this.getPrivateKey(), this.getCertificate(), '/sdcard/Download/' + packageName + uuid(10, 16) + '.p12', 'r0ysue');return result;}});
四
混淆代码
java.security.KeyStore不会被混淆,但是第三方的包都会被混淆为a.b.c.v类似的形式。Java.use('okhttp3.CertificatePinner');Java.use('com.square.okhttp.internal.tls.OkHostnamaVerifier');
Java.use("java.security.KeyStore");
java.security.KeyStore没有作用。X509TrustManager trustManager = new X509TrustManager() {@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}}
HostnameVerifier verify = new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}};
CertificatePinner buildPinner = new CertificatePinner.Builder().add(CA_DOMAIN, CA_PUBLIC_KEY).build();OkHttpClient client = new OkHttpClient.Builder().certificatePinner(buildPinner).build();
okhttp3.internal.connection.RealConnection类中的connectTls方法:Java.perform(function () {
var NativeSsl = Java.use('com.android.org.conscrypt.NativeSsl');
NativeSsl.doHandshake.overload('java.io.FileDescriptor', 'int').implementation = function (a, b) {
console.log("参数:", a, b);
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
return this.doHandshake(a, b);
};
});// frida -UF -l 1.hook_check.js
Java.perform(function () {var Platform = Java.use('com.android.org.conscrypt.Platform');Platform.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.AbstractConscryptSocket').implementation = function (x509tm, chain, authType, socket) {console.log('\n[+] checkServer ',x509tm,JSON.stringify(x509tm) );// 这里会去调用客户端证书校验的方法,不执行,就是不去校验(直接通过)。//return this.checkServerTrusted(x509tm, chain, authType, socket);};});
很少遇到 不写了Java.perform(function () {function getFieldValue(obj, fieldName) {var cls = obj.getClass();var field = cls.getDeclaredField(fieldName);field.setAccessible(true);var name = field.getName();var value = field.get(obj);return value;}function getMethodValue(obj, methodName) {var res;var cls = obj.getClass();var methods = cls.getDeclaredMethods();methods.forEach(function (method) {var method_name = method.getName();console.log(method_name, method);if (method_name === methodName) {method.setAccessible(true);res = method;return;}})return res;}var RealConnection = Java.use('uk.c');RealConnection.f.implementation = function (a, b, c, d) {try {console.log("===============");var route = getFieldValue(this, "c");console.log('route=', route);var address = getFieldValue(route, 'a');console.log('address=', address);var hostnameVerifier = getFieldValue(address, 'hostnameVerifier');console.log('hostnameVerifier=', hostnameVerifier);console.log('\n[+] hostnameVerifier', hostnameVerifier);} catch (e) {console.log(e);}return this.f(a, b, c, d);};});
connectTls中就能找到他的类和方法被混淆后的名称,可以直接Hook
五
禁用代理的场景
六
特殊框架 flutter 环境下如何抓包
function hook_ssl_verify_result(address)
{
Interceptor.attach(address, {
onEnter: function(args) {
console.log("Disabling SSL validation")
},
onLeave: function(retval)
{
console.log("Retval: " + retval)
retval.replace(0x1);
}
});
}
function disablePinning(){
// Change the offset on the line below with the binwalk result
// If you are on 32 bit, add 1 to the offset to indicate it is a THUMB function: .add(0x1)
// Otherwise, you will get 'Error: unable to intercept function at ......; please file a bug'
// 0x393DA4 换成你找到的函数地址
var address = Module.findBaseAddress('libflutter.so').add(0x393DA4)
hook_ssl_verify_result(address);
}
setTimeout(disablePinning, 1000)
七
其他抓包技巧
看雪ID:Dem0li
https://bbs.kanxue.com/user-home-976141.htm