如何使用Frida绕过Android网络安全配置
写在前面的话
在这篇文章中,我们将演示如何利用Frida脚本来绕过Android的网络安全配置,这是一种绕过网络安全配置的新技术。除此之外,我们还将演示如何在其他场景来测试该脚本,并分析脚本的运行机制。
在之前的一次Android应用程序安全审计过程中,首先我们要做的就是准备渗透测试的环境,并配置应用程序来绕过网络安全配置。由于我个人比较喜欢Frida,因此它也就成为了我的首选工具。
当时我下载了两到三个脚本,但是当我在Android 7.1.0中运行脚本时,没有一个可以成功的。这也就是为什么我想研究网络安全配置的运行机制,并且如何用Frida绕过它们。
我所做的第一件事就是生成不同的测试用例,我尝试选择了几款比较常见的:
1、OKHttp
2、HttpsURLConnection
3、WebView
接下来,我用不同的网络安全配置生成了三个应用程序:
1、一个使用了默认NSC配置的应用程序-BypassNSC
2、一个带有NSC文件(仅使用了系统证书)的应用程序-BypassNSC2
3、一个带有NSC文件的应用程序(强制证书绑定)-BypassNSC3
代码会解析并验证Android SDK中的网络安全配置,我的测试版本为24、25和26。广大用户可以点击【这里】获取我所生成的应用程序以及所使用的脚本。
脚本名如下:
network-security-config-bypass-1.js
network-security-config-bypass-2.js
network-security-config-bypass-3.js
network-security-config-bypass-cr.js
下图为每一个脚本的分析测试结果:
network-security-config-bypass-1.js
原始引用:【链接】
该脚本会修改NetworkSecurityConfig.Builder类中的getEffectiveCertificatesEntryRefs方法,该方法可以返回有效证书列表。在标准的Android配置中,它所返回的有效证书列表就是安装在目标系统中的有效证书。毫无以为,这个脚本将直接返回用户安装的证书,因此理论上来说,它可以绕过前两个应用程序的网络安全配置,但让我惊讶的是,它竟然也适用于第三种情况,也就是证书绑定配置。我们可以使用下列方法来验证绑定的证书:
android.security.net.config.NetworkSecurityTrustManager.checkPins
下面的栈跟踪记录显示了代码到checkPins函数的执行路径:
at android.security.net.config.NetworkSecurityTrustManager.checkPins(Native Method) at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:95) at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88) at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:178) at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:596) at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:357) …
如果补丁没有执行,那么当执行路径抵达该函数时,则会抛出异常:
Caused by: java.security.cert.CertificateException: Pin verification failed at android.security.net.config.NetworkSecurityTrustManager.checkPins(NetworkSecurityTrustManager.java:148) at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:95) at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88) at com.android.org.conscrypt.Platform.checkServerTrusted(Platform.java:178) at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:596) at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method) at com.android.org.
我们看一下这个方法的实现代码(API 25):
private void checkPins(ListX509Certificate> chain) throws CertificateException { PinSet pinSet = mNetworkSecurityConfig.getPins(); if (pinSet.pins.isEmpty() || System.currentTimeMillis() > pinSet.expirationTime || !isPinningEnforced(chain)) { return; } SetString> pinAlgorithms = pinSet.getPinAlgorithms(); MapString, MessageDigest> digestMap = new ArrayMapString, MessageDigest>( pinAlgorithms.size()); for (int i = chain.size() – 1; i >= 0 ; i–) { X509Certificate cert = chain.get(i); byte[] encodedSPKI = cert.getPublicKey().getEncoded(); for (String algorithm : pinAlgorithms) { MessageDigest md = digestMap.get(algorithm); if (md == null) { try { md = MessageDigest.getInstance(algorithm); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } digestMap.put(algorithm, md); } if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) { return; } } } // TODO: Throw a subclass of CertificateException which indicates a pinning failure. throw new CertificateException("Pin verification failed"); }