Android逆向猿人学2022年app比赛第五题双向验证SSLpinning(步步验证)

本文主要是介绍Android逆向猿人学2022年app比赛第五题双向验证SSLpinning(步步验证),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SSLpinnig

  • 前言
  • 一、起步
  • 二、抓包
  • 三、分析
  • 四、验证
    • 第一种方法:
    • 第二种方法:
  • 借鉴

前言

这题在抓包方面会有点小问题,但是最后结果是正确出来了,如果有了解后面这个问题的读者,请多指教,十分感谢。

一、起步

先打开APP,打开第五题,可以发现是一个双向认证的题目。
在这里插入图片描述

二、抓包

打开代理工具抓包设置一下于Charles的端口,我这里使用的代理工具是Brook。
在这里插入图片描述
代理打开后,访问第五题,会发现一直转不出来结果。
在这里插入图片描述
并且Charles会显示访问失败的信息
在这里插入图片描述

三、分析

从错误信息可以看到这个是跟SSL相关的请求,首先就排除了VPN检测这个问题。接下来就可以启动一下Client校验Server的frida脚本看看是否能够继续访问。(这句话其实是正常逆向要走的一个步骤,但是在本题已经很明确了是双向校验,所以其实可以提前确认这个是走不通的,但是为了有个正常流程还是形式走一下)。
DroidSSLUnpinning.js这份代码是从猿人学课程得到的,可以直接拿去使用,在没有特殊情况下,这份代码算是Client校验Server的优解了


var DroidSSLUnpinning = function(){function klog(data){var message={};message["jsname"]="DroidSSLUnpinning";message["data"]=data;console.log("DroidSSLUnpinning", data);
}Java.perform(function() {console.log("DroidSSLUnpinning","init","DroidSSLUnpinning.js init hook success")
/*
hook list:
1.SSLcontext
2.okhttp
3.webview
4.XUtils
5.httpclientandroidlib
6.JSSE
7.network\_security\_config (android 7.0+)
8.Apache Http client (support partly)
9.OpenSSLSocketImpl
10.TrustKit
11.Cronet
*/// Attempts to bypass SSL pinning implementations in a number of// ways. These include implementing a new TrustManager that will// accept any SSL certificate, overriding OkHTTP v3 check()// method etc.var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');var SSLContext = Java.use('javax.net.ssl.SSLContext');var quiet_output = false;// Helper method to honor the quiet flag.function quiet_send(data) {if (quiet_output) {return;}klog(data);}// Implement a new TrustManager// ref: https://gist.github.com/oleavr/3ca67a173ff7d207c6b8c3b0ca65a9d8// Java.registerClass() is only supported on ART for now(201803). 所以android 4.4以下不兼容,4.4要切换成ART使用./*
06-07 16:15:38.541 27021-27073/mi.sslpinningdemo W/System.err: java.lang.IllegalArgumentException: Required method checkServerTrusted(X509Certificate[], String, String, String) missing
06-07 16:15:38.542 27021-27073/mi.sslpinningdemo W/System.err:     at android.net.http.X509TrustManagerExtensions.<init>(X509TrustManagerExtensions.java:73)at mi.ssl.MiPinningTrustManger.<init>(MiPinningTrustManger.java:61)
06-07 16:15:38.543 27021-27073/mi.sslpinningdemo W/System.err:     at mi.sslpinningdemo.OkHttpUtil.getSecPinningClient(OkHttpUtil.java:112)at mi.sslpinningdemo.OkHttpUtil.get(OkHttpUtil.java:62)at mi.sslpinningdemo.MainActivity$1$1.run(MainActivity.java:36)
*/var X509Certificate = Java.use("java.security.cert.X509Certificate");var TrustManager;try {TrustManager = Java.registerClass({name: 'org.wooyun.TrustManager',implements: [X509TrustManager],methods: {checkClientTrusted: function(chain, authType) {},checkServerTrusted: function(chain, authType) {},getAcceptedIssuers: function() {// var certs = [X509Certificate.$new()];// return certs;return [];}}});} catch (e) {quiet_send("registerClass from X509TrustManager >>>>>>>> " + e.message);}// Prepare the TrustManagers array to pass to SSLContext.init()var TrustManagers = [TrustManager.$new()];try {// Prepare a Empty SSLFactoryvar TLS_SSLContext = SSLContext.getInstance("TLS");TLS_SSLContext.init(null, TrustManagers, null);var EmptySSLFactory = TLS_SSLContext.getSocketFactory();} catch (e) {quiet_send(e.message);}quiet_send('Custom, Empty TrustManager ready');// 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');// Override the init method, specifying our new TrustManagerSSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {quiet_send('Overriding SSLContext.init() with the custom TrustManager');SSLContext_init.call(this, null, TrustManagers, null);};/*** okhttp3.x unpinning ***/// Wrap the logic in a try/catch as not all applications will have// okhttp as part of the app.try {var CertificatePinner = Java.use('okhttp3.CertificatePinner');quiet_send('OkHTTP 3.x Found');CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {quiet_send('OkHTTP 3.x check() called. Not throwing an exception.');}} catch (err) {// If we dont have a ClassNotFoundException exception, raise the// problem encountered.if (err.message.indexOf('ClassNotFoundException') === 0) {throw new Error(err);}}// Appcelerator Titanium PinningTrustManager// Wrap the logic in a try/catch as not all applications will have// appcelerator as part of the app.try {var PinningTrustManager = Java.use('appcelerator.https.PinningTrustManager');quiet_send('Appcelerator Titanium Found')PinningTrustManager.checkServerTrusted.implementation = function() {quiet_send('Appcelerator checkServerTrusted() called. Not throwing an exception.');}} catch (err) {// If we dont have a ClassNotFoundException exception, raise the// problem encountered.if (err.message.indexOf('ClassNotFoundException') === 0) {throw new Error(err);}}/*** okhttp unpinning ***/try {var OkHttpClient = Java.use("com.squareup.okhttp.OkHttpClient");OkHttpClient.setCertificatePinner.implementation = function(certificatePinner) {// do nothingquiet_send("OkHttpClient.setCertificatePinner Called!");return this;};// Invalidate the certificate pinnet checks (if "setCertificatePinner" was called before the previous invalidation)var CertificatePinner = Java.use("com.squareup.okhttp.CertificatePinner");CertificatePinner.check.overload('java.lang.String', '[Ljava.security.cert.Certificate;').implementation = function(p0, p1) {// do nothingquiet_send("okhttp Called! [Certificate]");return;};CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(p0, p1) {// do nothingquiet_send("okhttp Called! [List]");return;};} catch (e) {quiet_send("com.squareup.okhttp not found");}/*** WebView Hooks ***//* frameworks/base/core/java/android/webkit/WebViewClient.java *//* public void onReceivedSslError(Webview, SslErrorHandler, SslError) */var WebViewClient = Java.use("android.webkit.WebViewClient");WebViewClient.onReceivedSslError.implementation = function(webView, sslErrorHandler, sslError) {quiet_send("WebViewClient onReceivedSslError invoke");//执行proceed方法sslErrorHandler.proceed();return;};WebViewClient.onReceivedError.overload('android.webkit.WebView', 'int', 'java.lang.String', 'java.lang.String').implementation = function(a, b, c, d) {quiet_send("WebViewClient onReceivedError invoked");return;};WebViewClient.onReceivedError.overload('android.webkit.WebView', 'android.webkit.WebResourceRequest', 'android.webkit.WebResourceError').implementation = function() {quiet_send("WebViewClient onReceivedError invoked");return;};/*** JSSE Hooks ***//* libcore/luni/src/main/java/javax/net/ssl/TrustManagerFactory.java *//* public final TrustManager[] getTrustManager() *//* TrustManagerFactory.getTrustManagers maybe cause X509TrustManagerExtensions error  */// var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");// TrustManagerFactory.getTrustManagers.implementation = function(){//     quiet_send("TrustManagerFactory getTrustManagers invoked");//     return TrustManagers;// }var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java *//* public void setDefaultHostnameVerifier(HostnameVerifier) */HttpsURLConnection.setDefaultHostnameVerifier.implementation = function(hostnameVerifier) {quiet_send("HttpsURLConnection.setDefaultHostnameVerifier invoked");return null;};/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java *//* public void setSSLSocketFactory(SSLSocketFactory) */HttpsURLConnection.setSSLSocketFactory.implementation = function(SSLSocketFactory) {quiet_send("HttpsURLConnection.setSSLSocketFactory invoked");return null;};/* libcore/luni/src/main/java/javax/net/ssl/HttpsURLConnection.java *//* public void setHostnameVerifier(HostnameVerifier) */HttpsURLConnection.setHostnameVerifier.implementation = function(hostnameVerifier) {quiet_send("HttpsURLConnection.setHostnameVerifier invoked");return null;};/*** Xutils3.x hooks ***///Implement a new HostnameVerifiervar TrustHostnameVerifier;try {TrustHostnameVerifier = Java.registerClass({name: 'org.wooyun.TrustHostnameVerifier',implements: [HostnameVerifier],method: {verify: function(hostname, session) {return true;}}});} catch (e) {//java.lang.ClassNotFoundException: Didn't find class "org.wooyun.TrustHostnameVerifier"quiet_send("registerClass from hostnameVerifier >>>>>>>> " + e.message);}try {var RequestParams = Java.use('org.xutils.http.RequestParams');RequestParams.setSslSocketFactory.implementation = function(sslSocketFactory) {sslSocketFactory = EmptySSLFactory;return null;}RequestParams.setHostnameVerifier.implementation = function(hostnameVerifier) {hostnameVerifier = TrustHostnameVerifier.$new();return null;}} catch (e) {quiet_send("Xutils hooks not Found");}/*** httpclientandroidlib Hooks ***/try {var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");AbstractVerifier.verify.overload('java.lang.String', '[Ljava.lang.String', '[Ljava.lang.String', 'boolean').implementation = function() {quiet_send("httpclientandroidlib Hooks");return null;}} catch (e) {quiet_send("httpclientandroidlib Hooks not found");}/***
android 7.0+ network_security_config TrustManagerImpl hook
apache httpclient partly
***/var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");// try {//     var Arrays = Java.use("java.util.Arrays");//     //apache http client pinning maybe baypass//     //https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#471//     TrustManagerImpl.checkTrusted.implementation = function (chain, authType, session, parameters, authType) {//         quiet_send("TrustManagerImpl checkTrusted called");//         //Generics currently result in java.lang.Object//         return Arrays.asList(chain);//     }//// } catch (e) {//     quiet_send("TrustManagerImpl checkTrusted nout found");// }try {// Android 7+ TrustManagerImplTrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {quiet_send("TrustManagerImpl verifyChain called");// Skip all the logic and just return the chain again :P//https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2017/november/bypassing-androids-network-security-configuration/// https://github.com/google/conscrypt/blob/c88f9f55a523f128f0e4dace76a34724bfa1e88c/platform/src/main/java/org/conscrypt/TrustManagerImpl.java#L650return untrustedChain;}} catch (e) {quiet_send("TrustManagerImpl verifyChain nout found below 7.0");}// OpenSSLSocketImpltry {var OpenSSLSocketImpl = Java.use('com.android.org.conscrypt.OpenSSLSocketImpl');OpenSSLSocketImpl.verifyCertificateChain.implementation = function(certRefs, authMethod) {quiet_send('OpenSSLSocketImpl.verifyCertificateChain');}quiet_send('OpenSSLSocketImpl pinning')} catch (err) {quiet_send('OpenSSLSocketImpl pinner not found');}// Trustkittry {var Activity = Java.use("com.datatheorem.android.trustkit.pinning.OkHostnameVerifier");Activity.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function(str) {quiet_send('Trustkit.verify1: ' + str);return true;};Activity.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function(str) {quiet_send('Trustkit.verify2: ' + str);return true;};quiet_send('Trustkit pinning')} catch (err) {quiet_send('Trustkit pinner not found')}try {//cronet pinner hook//weibo don't invokevar netBuilder = Java.use("org.chromium.net.CronetEngine$Builder");//https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#enablePublicKeyPinningBypassForLocalTrustAnchors(boolean)netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.implementation = function(arg) {//weibo not invoke// console.log("Enables or disables public key pinning bypass for local trust anchors = " + arg);quiet_send("Enables or disables public key pinning bypass for local trust anchors = " + arg);//true to enable the bypass, false to disable.var ret = netBuilder.enablePublicKeyPinningBypassForLocalTrustAnchors.call(this, true);return ret;};netBuilder.addPublicKeyPins.implementation = function(hostName, pinsSha256, includeSubdomains, expirationDate) {// console.log("cronet addPublicKeyPins hostName = " + hostName);quiet_send("cronet addPublicKeyPins hostName = " + hostName);//var ret = netBuilder.addPublicKeyPins.call(this,hostName, pinsSha256,includeSubdomains, expirationDate);//this 是调用 addPublicKeyPins 前的对象吗? Yes,CronetEngine.Builderreturn this;};} catch (err) {// console.log('[-] Cronet pinner not found')quiet_send('[-] Cronet pinner not found')}
});
}setImmediate(DroidSSLUnpinning);

老样子启动下frida,做个端口转发

./frida-server-16.0.2-android-arm64  -l 0.0.0.0:8881
adb forward tcp:8881 tcp:8881

然后直接cmd使用该DroidSSLUnpinning.js文件

frida -H 127.0.0.1:8881 -f com.yuanrenxue.match2022 -l DroidSSLUnpinning.js

在这里插入图片描述

然后再去打开第五题,看看是否能够正常显示(结果还是一样的,肯定是抓不到的了)

在这里插入图片描述
那么接下来,我们就得走Server校验Client的路子了。
Server校验Client是怎么个流程呢,总结一下就是,拿到证书文件,然后代理工具(中间人)携带证书文件去访问Server,让Server误以为是Client传来的请求。

四、验证

这里分两种方法拿到证书和证书密码:

第一种方法:

使用猿人学课程珍藏的tracer-keystore.js(用来吐证书密码的)

/*
Android keystore hooks + utilitiesAll instances of keystore are captured by hooking any getInstance() calls.
You can find them in keystoreList variable.Utilities:KeystoreListAllAliases()
ListAliasesStatic()
* List all aliases in keystores of known hardcoded types(in keystoreTypes)KeystoreListAllAliasesOnAllInstances()
ListAliasesRuntime()
* List all aliases in keystores of all instances obtained during app runtime.
* Instances that will be dumped are collected via hijacking
* Keystore.getInstance() -> hookKeystoreGetInstance()ListAliasesAndroid()
* List all aliases in AndroidKeyStore.ListAliasesType(String type)
* List all aliases in a keystore of given 'type'.
* Example: ListAliasesType('AndroidKeyStore');ListAliasesObj(Object obj)
* List all aliases for a given keystore object.
* Example: ListAliasesObj(keystoreObj);GetKeyStore(String name)
* Retrieve keystore instance from keystoreList.
* Example: GetKeyStore("KeyStore...@af102a");AliasInfo(String alias)
* List keystore key properties in a JSON object.
* Example: AliasInfo('secret');*/Java.perform(function () {hookKeystoreGetInstance();hookKeystoreGetInstance_Provider();hookKeystoreGetInstance_Provider2();hookKeystoreConstructor();hookKeystoreLoad(false);hookKeystoreLoadStream(false);hookKeystoreGetKey();hookKeystoreSetKeyEntry();//hookKeystoreGetCertificate();hookKeystoreGetCertificateChain();hookKeystoreGetEntry();hookKeystoreSetEntry();hookKeystoreSetKeyEntry();hookKeystoreSetKeyEntry2();hookKeystoreStore();hookKeystoreStoreStream()});
console.log("KeyStore hooks loaded!");var keystoreList = [];
var StringCls = null;
Java.perform(function () {StringCls = Java.use('java.lang.String');});function hookKeystoreConstructor() {var keyStoreConstructor = Java.use('java.security.KeyStore').$init.overload("java.security.KeyStoreSpi", "java.security.Provider", "java.lang.String");keyStoreConstructor.implementation = function (keyStoreSpi, provider, type) {//console.log("[Call] Keystore(java.security.KeyStoreSpi, java.security.Provider, java.lang.String )")console.log("[Keystore()]: KeyStoreSpi: " + keyStoreSpi + ", Provider: " + provider + ", type: " + type);return this.$init(keyStoreSpi, provider, type);}
}function hookKeystoreGetInstance() {var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String");keyStoreGetInstance.implementation = function (type) {//console.log("[Call] Keystore.getInstance(java.lang.String )")console.log("[Keystore.getInstance()]: type: " + type);var tmp = this.getInstance(type);keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()return tmp;}
}function hookKeystoreGetInstance_Provider() {var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String", "java.lang.String");keyStoreGetInstance.implementation = function (type, provider) {//console.log("[Call] Keystore.getInstance(java.lang.String, java.lang.String )")console.log("[Keystore.getInstance2()]: type: " + type + ", provider: " + provider);var tmp = this.getInstance(type, proivder);keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()return tmp;}
}function hookKeystoreGetInstance_Provider2() {var keyStoreGetInstance = Java.use('java.security.KeyStore')['getInstance'].overload("java.lang.String", "java.security.Provider");keyStoreGetInstance.implementation = function (type, provider) {//console.log("[Call] Keystore.getInstance(java.lang.String, java.security.Provider )")console.log("[Keystore.getInstance2()]: type: " + type + ", provider: " + provider);var tmp = this.getInstance(type, proivder);keystoreList.push(tmp); // Collect keystore objects to allow dump them later using ListAliasesRuntime()return tmp;}
}/*
* Hook Keystore.load( ... ), set dump to true if you want to perform dump of available Aliases automatically.
*/
function hookKeystoreLoad(dump) {var keyStoreLoad = Java.use('java.security.KeyStore')['load'].overload('java.security.KeyStore$LoadStoreParameter');/* following function hooks to a Keystore.load(java.security.KeyStore.LoadStoreParameter) */keyStoreLoad.implementation = function (param) {//console.log("[Call] Keystore.load(java.security.KeyStore.LoadStoreParameter)")console.log("[Keystore.load(LoadStoreParameter)]: keystoreType: " + this.getType() + ", param: " + param);this.load(param);if (dump) console.log(" Keystore loaded aliases: " + ListAliasesObj(this));}
}/*
* Hook Keystore.load( ... ), set dump to true if you want to perform dump of available Aliases automatically.
*/
function hookKeystoreLoadStream(dump) {var keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C');/* following function hooks to a Keystore.load(InputStream stream, char[] password) */keyStoreLoadStream.implementation = function (stream, charArray) {//console.log("[Call] Keystore.load(InputStream stream, char[] password)")//var hexString = readStreamToHex (stream);console.log("[Keystore.load(InputStream, char[])]: keystoreType: " + this.getType() + ", password: '" + charArrayToString(charArray) + "', inputSteam: " + stream);this.load(stream, charArray);if (dump) console.log(" Keystore loaded aliases: " + ListAliasesObj(this));}
}function hookKeystoreStore() {var keyStoreStoreStream = Java.use('java.security.KeyStore')['store'].overload('java.security.KeyStore$LoadStoreParameter');/* following function hooks to a Keystore.store(java.security.KeyStore$LoadStoreParameter) */keyStoreStoreStream.implementation = function (param) {console.log("[Keystore.store()]: keystoreType: " + this.getType() + ", param: '" + param);this.store(stream, charArray);}
}function hookKeystoreStoreStream() {var keyStoreStoreStream = Java.use('java.security.KeyStore')['store'].overload('java.io.OutputStream', '[C');/* following function hooks to a Keystore.store(OutputStream stream, char[] password) */keyStoreStoreStream.implementation = function (stream, charArray) {console.log("[Keystore.store(OutputStream, char[])]: keystoreType: " + this.getType() + ", password: '" + charArrayToString(charArray) + "', outputSteam: " + stream);this.store(stream, charArray);}
}function hookKeystoreGetKey() {var keyStoreGetKey = Java.use('java.security.KeyStore')['getKey'].overload("java.lang.String", "[C");keyStoreGetKey.implementation = function (alias, charArray) {//console.log("[Call] Keystore.getKey(java.lang.String, [C )")console.log("[Keystore.getKey()]: alias: " + alias + ", password: '" + charArrayToString(charArray) + "'");return this.getKey(alias, charArray);}
}function hookKeystoreSetEntry() {var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setEntry'].overload("java.lang.String", "java.security.KeyStore$Entry", "java.security.KeyStore$ProtectionParameter");keyStoreSetKeyEntry.implementation = function (alias, entry, protection) {//console.log("[Call] Keystore.setEntry(java.lang.String, java.security.KeyStore$Entry, java.security.KeyStore$ProtectionParameter )")console.log("[Keystore.setEntry()]: alias: " + alias + ", entry: " + dumpKeyStoreEntry(entry) + "', protection: " + dumpProtectionParameter(protection));return this.setEntry(alias, entry, protection);}
}function hookKeystoreSetKeyEntry() {var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setKeyEntry'].overload("java.lang.String", "java.security.Key", "[C", "[Ljava.security.cert.Certificate;");keyStoreSetKeyEntry.implementation = function (alias, key, charArray, certs) {//console.log("[Call] Keystore.setKeyEntry(java.lang.String, java.security.Key, [C, [Ljava.security.cert.Certificate; )console.log("[Keystore.setKeyEntry()]: alias: " + alias + ", key: " + key + ", password: '" + charArrayToString(charArray) + "', certs: " + certs);return this.setKeyEntry(alias, key, charArray, certs);}
}function hookKeystoreSetKeyEntry2() {var keyStoreSetKeyEntry = Java.use('java.security.KeyStore')['setKeyEntry'].overload("java.lang.String", "[B", "[Ljava.security.cert.Certificate;");keyStoreSetKeyEntry.implementation = function (alias, key, certs) {//console.log("[Call] Keystore.setKeyEntry(java.lang.String, [B, [Ljava.security.cert.Certificate; )")console.log("[Keystore.setKeyEntry2()]: alias: " + alias + ", key: " + key + "', certs: " + certs);return this.setKeyEntry(alias, key, certs);}
}/*
* Usually used to load certs for cert pinning.
*/
function hookKeystoreGetCertificate() {var keyStoreGetCertificate = Java.use('java.security.KeyStore')['getCertificate'].overload("java.lang.String");keyStoreGetCertificate.implementation = function (alias) {//console.log("[Call] Keystore.getCertificate(java.lang.String )")console.log("[Keystore.getCertificate()]: alias: " + alias);return this.getCertificate(alias);}
}/*
* Usually used to load certs for cert pinning.
*/
function hookKeystoreGetCertificateChain() {var keyStoreGetCertificate = Java.use('java.security.KeyStore')['getCertificateChain'].overload("java.lang.String");keyStoreGetCertificate.implementation = function (alias) {//console.log("[Call] Keystore.getCertificateChain(java.lang.String )")console.log("[Keystore.getCertificateChain()]: alias: " + alias);return this.getCertificateChain(alias);}
}function hookKeystoreGetEntry() {var keyStoreGetEntry = Java.use('java.security.KeyStore')['getEntry'].overload("java.lang.String", "java.security.KeyStore$ProtectionParameter");keyStoreGetEntry.implementation = function (alias, protection) {//console.log("[Call] Keystore.getEntry(java.lang.String, java.security.KeyStore$ProtectionParameter )")console.log("[Keystore.getEntry()]: alias: " + alias + ", protection: '" + dumpProtectionParameter(protection) + "'");var entry = this.getEntry(alias, protection);console.log("[getEntry()]: Entry: " + dumpKeyStoreEntry(entry));return entry;}
}function dumpProtectionParameter(protection) {if (protection != null) {// android.security.keystore.KeyProtection, java.security.KeyStore.CallbackHandlerProtection, java.security.KeyStore.PasswordProtection, android.security.KeyStoreParametervar protectionCls = protection.$className;if (protectionCls.localeCompare("android.security.keystore.KeyProtection") == 0) {return "" + protectionCls + " [implement dumping if needed]";}else if (protectionCls.localeCompare("java.security.KeyStore.CallbackHandlerProtection") == 0) {return "" + protectionCls + " [implement dumping if needed]";}else if (protectionCls.localeCompare("java.security.KeyStore.PasswordProtection") == 0) {getPasswordMethod = Java.use('java.security.KeyStore.PasswordProtection')['getPassword'];password = getPasswordMethod.call(protection);return "password: " + charArrayToString(password);}else if (protectionCls.localeCompare("android.security.KeyStoreParameter") == 0) {isEncryptionRequiredMethod = Java.use('android.security.KeyStoreParameter')['isEncryptionRequired'];result = isEncryptionRequiredMethod.call(protection);return "isEncryptionRequired: " + result;}elsereturn "Unknown protection parameter type: " + protectionCls;}elsereturn "null";}function dumpKeyStoreEntry(entry) {// java.security.KeyStore$PrivateKeyEntry, java.security.KeyStore$SecretKeyEntry, java.security.KeyStore$TrustedCertificateEntry, android.security.WrappedKeyEntryif (entry != null) {var entryCls = entry.$className;var castedEntry = Java.cast(entry, Java.use(entryCls));if (entryCls.localeCompare("java.security.KeyStore$PrivateKeyEntry") == 0) {var getPrivateKeyEntryMethod = Java.use('java.security.KeyStore$PrivateKeyEntry')['getPrivateKey'];var key = getPrivateKeyEntryMethod.call(castedEntry);return "" + entryCls + " [implement key dumping if needed] " + key.$className;}else if (entryCls.localeCompare("java.security.KeyStore$SecretKeyEntry") == 0) {var getSecretKeyMethod = Java.use('java.security.KeyStore$SecretKeyEntry')['getSecretKey'];var key = getSecretKeyMethod.call(castedEntry);var keyGetFormatMethod = Java.use(key.$className)['getFormat'];var keyGetEncodedMethod = Java.use(key.$className)['getEncoded'];//console.log(""+key.$className);if (key.$className.localeCompare("android.security.keystore.AndroidKeyStoreSecretKey") == 0)return "keyClass: android.security.keystore.AndroidKeyStoreSecretKey can't dump";return "keyFormat: " + keyGetFormatMethod.call(key) + ", encodedKey: '" + keyGetEncodedMethod.call(key) + "', key: " + key;}else if (entryCls.localeCompare("java.security.KeyStore$TrustedCertificateEntry") == 0) {return "" + entryCls + " [implement key dumping if needed]";}else if (entryCls.localeCompare("android.security.WrappedKeyEntry") == 0) {return "" + entryCls + " [implement key dumping if needed]";}elsereturn "Unknown key entry type: " + entryCls;}elsereturn "null";
}/*
* Dump all aliasses in keystores of all types(predefined in keystoreTypes)
*/
function ListAliasesStatic() {// BCPKCS12/PKCS12-DEF - exceptionsvar keystoreTypes = ["AndroidKeyStore", "AndroidCAStore", /*"BCPKCS12",*/ "BKS", "BouncyCastle", "PKCS12", /*"PKCS12-DEF"*/];keystoreTypes.forEach(function (entry) {console.log("[ListAliasesStatic] keystoreType: " + entry + " \nAliases: " + ListAliasesType(entry));});return "[done]";
}/*
* Dump all aliasses in keystores of all instances obtained during app runtime.
* Instances that will be dumped are collected via hijacking Keystre.getInstance() -> hookKeystoreGetInstance()
*/
function ListAliasesRuntime() {Java.perform(function () {console.log("[ListAliasesRuntime] Instances: " + keystoreList);keystoreList.forEach(function (entry) {console.log("[ListAliasesRuntime] keystoreObj: " + entry + " type: " + entry.getType() + " \n" + ListAliasesObj(entry));});});return "[done]";
}/*
* Dump all aliasses in AndroidKey keystore.
*/
function ListAliasesAndroid() {return ListAliasesType("AndroidKeyStore");
}/*
* Dump all aliasses in keystore of given 'type'.
* Example: ListAliasesType('AndroidKeyStore');
*/
function ListAliasesType(type) {var result = [];Java.perform(function () {var keyStoreCls = Java.use('java.security.KeyStore');var keyStoreObj = keyStoreCls.getInstance(type);keyStoreObj.load(null);var aliases = keyStoreObj.aliases();//console.log("aliases: " + aliases.getClass());while (aliases.hasMoreElements()) {result.push("'" + aliases.nextElement() + "'");}});return result;
}/*
* Dump all aliasses for a given keystore object.
* Example: ListAliasesObj(keystoreObj);
*/
function ListAliasesObj(obj) {var result = [];Java.perform(function () {var aliases = obj.aliases();while (aliases.hasMoreElements()) {result.push(aliases.nextElement() + "");}});return result;
}/*
* Retrieve keystore instance from keystoreList
* Example: GetKeyStore("KeyStore...@af102a");
*/
function GetKeyStore(keystoreName) {var result = null;Java.perform(function () {for (var i = 0; i < keystoreList.length; i++) {if (keystoreName.localeCompare("" + keystoreList[i]) == 0)result = keystoreList[i];}});return result;
}/*
* Dump keystore key properties in JSON object
* Example: AliasInfo('secret');
*/
function AliasInfo(keyAlias) {var result = {};Java.perform(function () {var keyStoreCls = Java.use('java.security.KeyStore');var keyFactoryCls = Java.use('java.security.KeyFactory');var keyInfoCls = Java.use('android.security.keystore.KeyInfo');var keySecretKeyFactoryCls = Java.use('javax.crypto.SecretKeyFactory');var keyFactoryObj = null;var keyStoreObj = keyStoreCls.getInstance('AndroidKeyStore');keyStoreObj.load(null);var key = keyStoreObj.getKey(keyAlias, null);if (key == null) {console.log('key does not exist');return null;}try {keyFactoryObj = keyFactoryCls.getInstance(key.getAlgorithm(), 'AndroidKeyStore');} catch (err) {keyFactoryObj = keySecretKeyFactoryCls.getInstance(key.getAlgorithm(), 'AndroidKeyStore');}var keyInfo = keyFactoryObj.getKeySpec(key, keyInfoCls.class);result.keyAlgorithm = key.getAlgorithm();result.keySize = keyInfoCls['getKeySize'].call(keyInfo);result.blockModes = keyInfoCls['getBlockModes'].call(keyInfo);result.digests = keyInfoCls['getDigests'].call(keyInfo);result.encryptionPaddings = keyInfoCls['getEncryptionPaddings'].call(keyInfo);result.keyValidityForConsumptionEnd = keyInfoCls['getKeyValidityForConsumptionEnd'].call(keyInfo);if (result.keyValidityForConsumptionEnd != null) result.keyValidityForConsumptionEnd = result.keyValidityForConsumptionEnd.toString();result.keyValidityForOriginationEnd = keyInfoCls['getKeyValidityForOriginationEnd'].call(keyInfo);if (result.keyValidityForOriginationEnd != null) result.keyValidityForOriginationEnd = result.keyValidityForOriginationEnd.toString();result.keyValidityStart = keyInfoCls['getKeyValidityStart'].call(keyInfo);if (result.keyValidityStart != null) result.keyValidityStart = result.keyValidityStart.toString();result.keystoreAlias = keyInfoCls['getKeystoreAlias'].call(keyInfo);result.origin = keyInfoCls['getOrigin'].call(keyInfo);result.purposes = keyInfoCls['getPurposes'].call(keyInfo);result.signaturePaddings = keyInfoCls['getSignaturePaddings'].call(keyInfo);result.userAuthenticationValidityDurationSeconds = keyInfoCls['getUserAuthenticationValidityDurationSeconds'].call(keyInfo);result.isInsideSecureHardware = keyInfoCls['isInsideSecureHardware'].call(keyInfo);result.isInvalidatedByBiometricEnrollment = keyInfoCls['isInvalidatedByBiometricEnrollment'].call(keyInfo);try { result.isTrustedUserPresenceRequired = keyInfoCls['isTrustedUserPresenceRequired'].call(keyInfo); } catch (err) { }result.isUserAuthenticationRequired = keyInfoCls['isUserAuthenticationRequired'].call(keyInfo);result.isUserAuthenticationRequirementEnforcedBySecureHardware = keyInfoCls['isUserAuthenticationRequirementEnforcedBySecureHardware'].call(keyInfo);result.isUserAuthenticationValidWhileOnBody = keyInfoCls['isUserAuthenticationValidWhileOnBody'].call(keyInfo);try { result.isUserConfirmationRequired = keyInfoCls['isUserConfirmationRequired'].call(keyInfo); } catch (err) { }//console.log(" result: " + JSON.stringify(result));//console.log("aliases: " + aliases.getClass());});return result;
}/* following function reads an InputStream and returns an ASCII char representation of it */
function readStreamToHex(stream) {var data = [];var byteRead = stream.read();while (byteRead != -1) {data.push(('0' + (byteRead & 0xFF).toString(16)).slice(-2));/* <---------------- binary to hex ---------------> */byteRead = stream.read();}stream.close();return data.join('');
}function charArrayToString(charArray) {if (charArray == null)return '(null)';elsereturn StringCls.$new(charArray);
}

启动效果
在这里插入图片描述
然后手机点击第五题,可以发现证书密码被吐了出来
在这里插入图片描述
拿到证书密码后,就可以直接快乐的去找证书文件了,直接先拖到jadx去反编译看看,证书一般是在资源文件的assets里面的,根据上面的截图,可以知道证书密码就是这个BKS文件的密码。在这里我们就需要把这个证书文件给取出来,然后转换成.p12格式的证书,提供给Charles抓包。
在这里插入图片描述
那怎么拿出来呢,可以直接使用解压这个功能,然后找到对应的文件,拿出来就可以了。
这个证书拿出来后,要怎么才能变成.p12格式呢,这里我使用的是keystore-explorer,进行bks到p12的转换(但是这个工具最新版是打不开这个证书文件的)所以我们要回退到旧版本,去安装使用。
找到v5.5.1版本,按照自己的系统和需求去下载。
安装好了之后,把BKS证书文件拖进去,密码就是我们上面frida吐出来的密码—MZ4cozY8Qu32UzGe在这里插入图片描述
然后点击Tools->Change KeyStore Type->PKC#12,转换后还会要求输入一次密码
O
然后右键选择Export导出证书。
在这里插入图片描述
这个就是我导出的1.p12证书
在这里插入图片描述
既然有了证书文件和证书密码,那就可以拿去Charles配置一下证书,让它伪装成携带证书的Client。

在这里插入图片描述
这里如果没有创建过的会要求输入一个密码,这里直接回车就行,不用去设置。
在这里插入图片描述
把刚刚访问失败的ip和端口填上,然后点击Import P12文件
在这里插入图片描述
找到刚刚导出的1.p12证书文件
在这里插入图片描述
输入一下刚刚得到的证书密码—MZ4cozY8Qu32UzGe
在这里插入图片描述
然后一路点OK
在这里插入图片描述
然后我们回到SSL Proxying标签,新增加一个*:18443(刚刚抓失败的端口)
在这里插入图片描述
最后点一下OK,按照道理说这么干已经成功了,但是很可惜,我做到这一步还是失败了。直接告诉我证书是损坏的。这也就是我开头说的无法解决的一个小问题,希望读者如果有解决方法请告知,感激不尽。
在这里插入图片描述
但是这并不代表,我们的证书文件不能用了。我们直接用吾爱破解网darbra大佬写的文章的python代码来进行携带证书请求我们刚刚访问失败的网站。
先安装requests_pkcs12

pip install requests_pkcs12
import requests_pkcs12def get_page(page):url = 'https://180.76.60.244:18443/api/app5'hd = {'Content-Type':'application/x-www-form-urlencoded','user-agent': 'okhttp/3.14.9'}data = {'page': page}resp = requests_pkcs12.post(url,headers=hd, data=data, pkcs12_filename='1.p12',pkcs12_password='MZ4cozY8Qu32UzGe', verify=False)print(resp.json())get_page(1)

可以看到数据出来了!
在这里插入图片描述
当然这个请求不是无缘无故写出来的,是可以通过r0capture抓包工具抓到分析出来的

python r0capture.py -H 127.0.0.1:8881 猿人学2022

在这里插入图片描述
POST请求

url是…:https://180.76.60.244:18443/api/app5

Content-Type是application/x-www-form-urlencoded

User-Agent是okhttp/3.14.9

data是page=1

到此我们的第一种方法已经结束。

第二种方法:

第二种证书和密钥的获取方法,就比较简单了,使用我们刚刚说的r0capture抓包工具就可以做到自动将证书给下载保存到我们的手机的/sdcard/Download/目录下,密码是r0ysue
启动一下r0captue。

python r0capture.py -H 127.0.0.1:8881 猿人学2022

然后启动成功后,去点击第五题,就会自动保存证书了。

在这里插入图片描述
剩下的步骤我们只需要用下面代码就可以把证书导出来

adb pull /sdcard/Download/证书文件.p12 ./ 

如果显示没有该文件就到/sdcard/Download/看看是什么情况。会发现很多大小一样的证书文件,但是名字都和上面截图不一样,不用慌,我们只需要选择其中一个,名字改一下(方便pull),我这里是改了一个为client

在这里插入图片描述
再执行adb pull语句就成功了。
然后重复第一方法的步骤,将证书导入,不过这次的证书密码就得是r0ysue了(这里就不演示怎么导入的了,除了导出证书和密码不一样,其他的都一模一样,就连python使用都一样)

import requests_pkcs12def get_page(page):url = 'https://180.76.60.244:18443/api/app5'hd = {'Content-Type':'application/x-www-form-urlencoded','user-agent': 'okhttp/3.14.9'}data = {'page': page}resp = requests_pkcs12.post(url,headers=hd, data=data, pkcs12_filename='client.p12',pkcs12_password='r0ysue', verify=False)print(resp.json())get_page(1)

可以看到结果都是一样的
在这里插入图片描述

借鉴

猿人学比赛第五题-双向认证分享
猿人学

这篇关于Android逆向猿人学2022年app比赛第五题双向验证SSLpinning(步步验证)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/220004

相关文章

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后