VPN检测
- 检测常见VPN接口名以及网络特征
- 网络接口:tun0 ppp0
- 网络特征:WIFI|VPN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| public final boolean Check_Vpn1() { try { Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); if (networkInterfaces == null) { return false; } Iterator it = Collections.list(networkInterfaces).iterator(); while (it.hasNext()) { NetworkInterface networkInterface = (NetworkInterface) it.next(); if (networkInterface.isUp() && !networkInterface.getInterfaceAddresses().isEmpty()) { Log.d("zj595", "isVpn NetworkInterface Name: " + networkInterface.getName()); if (Intrinsics.areEqual(networkInterface.getName(), "tun0") || Intrinsics.areEqual(networkInterface.getName(), "ppp0") || Intrinsics.areEqual(networkInterface.getName(), "p2p0") || Intrinsics.areEqual(networkInterface.getName(), "ccmni0")) { return true; } } } return false; } catch (Throwable th) { th.printStackTrace(); return false; } } public final boolean Check_Vpn2() { boolean z; String networkCapabilities; try { Object systemService = getApplicationContext().getSystemService("connectivity"); Intrinsics.checkNotNull(systemService, "null cannot be cast to non-null type android.net.ConnectivityManager"); ConnectivityManager connectivityManager = (ConnectivityManager) systemService; NetworkCapabilities networkCapabilities2 = connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork()); Log.i("zj595", "networkCapabilities -> " + networkCapabilities2); boolean z2 = networkCapabilities2 != null && networkCapabilities2.hasTransport(4); if (networkCapabilities2 != null && (networkCapabilities = networkCapabilities2.toString()) != null) { if (StringsKt.contains$default((CharSequence) networkCapabilities, (CharSequence) "WIFI|VPN", false, 2, (Object) null)) { z = true; return !z || z2; } } z = false; if (z) { } } catch (Exception e) { e.printStackTrace(); return false; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function hook_vpn() { Java.perform(function () { var NetworkInterface = Java.use("java.net.NetworkInterface"); NetworkInterface.getName.implementation = function () { var name = this.getName(); console.log("name: " + name); if (name === "tun0" || name === "ppp0") { return "rmnet_data0"; } else { return name; } } var NetworkCapabilities = Java.use("android.net.NetworkCapabilities"); NetworkCapabilities.hasTransport.implementation = function () { return false; } NetworkCapabilities.appendStringRepresentationOfBitMaskToStringBuilder.implementation = function (sb, bitMask, nameFetcher, separator) { if (bitMask == 18) { console.log("bitMask", bitMask); sb.append("WIFI"); } else { console.log(sb, bitMask); this.appendStringRepresentationOfBitMaskToStringBuilder(sb, bitMask, nameFetcher, separator); } } }) }
|
SSLPinning(加强版单向校验)
指纹校验
例子:okhttp3
CertificatePinner的check方法会检测给定hash和证书hash是否匹配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
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); 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; } else if (pin.hashAlgorithm.equals("sha1/")) { if (sha1 == null) sha1 = sha1(x509Certificate); if (pin.hash.equals(sha1)) return; } else { throw new AssertionError("unsupported hashAlgorithm: " + pin.hashAlgorithm); } } } throw new SSLPeerUnverifiedException("No matching certificate found."); }
|
frida绕过(check函数直接置空干掉)
1 2 3 4 5 6 7
| function anti_ssl_key() { 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 SSL key pinning: ' + a); return; }}
|
证书校验
- 将服务器发来的证书的公钥base64和本地证书公钥base64进行匹配
trustManager
类的checkServerTrusted接口
同样frida绕过,模拟实例化trustManager类置空,hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| function anti_ssl_cert() { var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); var SSLContext = Java.use('javax.net.ssl.SSLContext'); var TrustManager = Java.registerClass({ name: 'dev.asd.test.TrustManager', implements: [X509TrustManager], methods: { checkClientTrusted: function(chain, authType) {}, checkServerTrusted: function(chain, authType) {}, getAcceptedIssuers: function() {return []; } } }); var TrustManagers = [TrustManager.$new()]; var SSLContext_init = SSLContext.init.overload( '[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom' ); try { SSLContext_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); } }
|
双向认证
- 服务端发送服务器公钥–>客户端校验后发送客户端证书公钥–>服务端校验客户端证书,拿到客户端公钥–>沟通加密方案–>对称加密
案例
- 服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| from flask import Flask, jsonify import ssl app = Flask(__name__) # ssl 证书校验 @app.route('/ca') def ssl_verify(): return jsonify({"message": "HTTPS server with mutual SSL verification started."}) # 配置ssl上下文,关键函数 def get_ssl_context(): # CA根证书路径 ca_crt_path = 'certs/ca.crt' # 服务端证书和密钥路径 server_crt_path = 'certs/server.crt' server_key_path = 'certs/server.key' # 创建SSL上下文,使用TLS服务器模式 ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # 设置验证模式为需要客户端证书 ssl_context.verify_mode = ssl.CERT_REQUIRED # 启用主机名检查(根据需要设置) ssl_context.check_hostname = False # 设置加密套件 ssl_context.set_ciphers("HIGH:!SSLv3:!TLSv1:!aNULL:@STRENGTH") # 加载CA根证书,用于验证客户端证书 ssl_context.load_verify_locations(cafile=ca_crt_path) # 加载服务端证书和私钥 ssl_context.load_cert_chain(certfile=server_crt_path, keyfile=server_key_path) return ssl_context if __name__ == '__main__': ssl_context = get_ssl_context() app.run(host="192.168.124.21", port=8088,ssl_context=ssl_context)
|
- 客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| fun ssl_verify() = Thread {
var trustManager: X509TrustManager? = null try { val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) trustManagerFactory.init(null as KeyStore?) val trustManagers = trustManagerFactory.trustManagers if (trustManagers.size != 1 || trustManagers[0] !is X509TrustManager) { throw IllegalStateException("Unexpected default trust managers: ${trustManagers.contentToString()}") } trustManager = trustManagers[0] as X509TrustManager } catch (e: Exception) { e.printStackTrace() } val client = OkHttpClient.Builder() .sslSocketFactory( ClientSSLSocketFactory.getSocketFactory(applicationContext), trustManager ?: throw IllegalStateException("TrustManager is null") ) .hostnameVerifier { hostname, session -> true } .build() val request = Request.Builder() .url("https://192.168.124.21:8088/ca") .build() try { val response = client.newCall(request).execute() Log.d(TAG, "双向检测通过:${response.code()}") } catch (e: IOException) { Log.d(TAG, "双向检测不通过") e.printStackTrace() } }.start()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| function hook_KeyStore_load() { Java.perform(function () { var ByteString = Java.use("com.android.okhttp.okio.ByteString"); var myArray=new Array(1024); var i = 0 for (i = 0; i < myArray.length; i++) { myArray[i]= 0x0; } var buffer = Java.array('byte',myArray); var StringClass = Java.use("java.lang.String"); var KeyStore = Java.use("java.security.KeyStore"); KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) { console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); console.log("KeyStore.load1:", arg0); this.load(arg0); }; KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) { console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1) : null); if (arg0){ var file = Java.use("java.io.File").$new("/data/user/0/com.zj.wuaipojie/files/client"+".p12"); var out = Java.use("java.io.FileOutputStream").$new(file); var r; while( (r = arg0.read(buffer)) > 0){ out.write(buffer,0,r) } console.log("证书保存成功!") out.close() } this.load(arg0, arg1); }; }); }
|
原文链接:[原创]《安卓逆向这档事》第二十一课、抓包学得好,牢饭吃得饱(中)-Android安全-看雪-安全社区|安全招聘|kanxue.com
Socket通信Hook
1 2 3 4 5 6 7 8
| socketRead0 socketWrite0 recvfrom sendto
sendtoBytes readBytes recvfrom sendto
SSL_write SSL_read【find SSL库函数】
|
python爬虫