如何使用Frida繞過Android網(wǎng)絡(luò)安全配置,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對(duì)這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長期合作伙伴,公司提供的服務(wù)項(xiàng)目有:主機(jī)域名、虛擬主機(jī)、營銷軟件、網(wǎng)站建設(shè)、連城網(wǎng)站維護(hù)、網(wǎng)站推廣。
我們將演示如何利用Frida腳本來繞過Android的網(wǎng)絡(luò)安全配置,這是一種繞過網(wǎng)絡(luò)安全配置的新技術(shù)。除此之外,我們還將演示如何在其他場景來測試該腳本,并分析腳本的運(yùn)行機(jī)制。
在之前的一次Android應(yīng)用程序安全審計(jì)過程中,首先我們要做的就是準(zhǔn)備滲透測試的環(huán)境,并配置應(yīng)用程序來繞過網(wǎng)絡(luò)安全配置。由于我個(gè)人比較喜歡Frida,因此它也就成為了我的首選工具。
當(dāng)時(shí)我下載了兩到三個(gè)腳本,但是當(dāng)我在Android 7.1.0中運(yùn)行腳本時(shí),沒有一個(gè)可以成功的。這也就是為什么我想研究網(wǎng)絡(luò)安全配置的運(yùn)行機(jī)制,并且如何用Frida繞過它們。
我所做的第一件事就是生成不同的測試用例,我嘗試選擇了幾款比較常見的:
1、OKHttp
2、HttpsURLConnection
3、WebView
接下來,我用不同的網(wǎng)絡(luò)安全配置生成了三個(gè)應(yīng)用程序:
1、一個(gè)使用了默認(rèn)NSC配置的應(yīng)用程序-BypassNSC
2、一個(gè)帶有NSC文件(僅使用了系統(tǒng)證書)的應(yīng)用程序-BypassNSC2
3、一個(gè)帶有NSC文件的應(yīng)用程序(強(qiáng)制證書綁定)-BypassNSC3
代碼會(huì)解析并驗(yàn)證Android SDK中的網(wǎng)絡(luò)安全配置,我的測試版本為24、25和26。廣大用戶可以點(diǎn)擊【這里】獲取我所生成的應(yīng)用程序以及所使用的腳本。
腳本名如下:
network-security-config-bypass-1.js
network-security-config-bypass-2.js
network-security-config-bypass-3.js
network-security-config-bypass-cr.js
下圖為每一個(gè)腳本的分析測試結(jié)果:
原始引用:【鏈接】
該腳本會(huì)修改NetworkSecurityConfig.Builder類中的getEffectiveCertificatesEntryRefs方法,該方法可以返回有效證書列表。在標(biāo)準(zhǔn)的Android配置中,它所返回的有效證書列表就是安裝在目標(biāo)系統(tǒng)中的有效證書。毫無以為,這個(gè)腳本將直接返回用戶安裝的證書,因此理論上來說,它可以繞過前兩個(gè)應(yīng)用程序的網(wǎng)絡(luò)安全配置,但讓我驚訝的是,它竟然也適用于第三種情況,也就是證書綁定配置。我們可以使用下列方法來驗(yàn)證綁定的證書:
android.security.net.config.NetworkSecurityTrustManager.checkPins
下面的棧跟蹤記錄顯示了代碼到checkPins函數(shù)的執(zhí)行路徑:
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) ...
如果補(bǔ)丁沒有執(zhí)行,那么當(dāng)執(zhí)行路徑抵達(dá)該函數(shù)時(shí),則會(huì)拋出異常:
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.
我們看一下這個(gè)方法的實(shí)現(xiàn)代碼(API 25):
private void checkPins(List<X509Certificate> chain) throws CertificateException { PinSet pinSet = mNetworkSecurityConfig.getPins(); if (pinSet.pins.isEmpty() || System.currentTimeMillis() > pinSet.expirationTime || !isPinningEnforced(chain)) { return; } Set<String> pinAlgorithms = pinSet.getPinAlgorithms(); Map<String, MessageDigest> digestMap = new ArrayMap<String, 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"); }
這個(gè)方法可以接收網(wǎng)站通信所返回的證書列表,它做的第一件事情就是條件檢測:
1、pinset為空
2、驗(yàn)證時(shí)pinset已過期
3、證書綁定并非配置強(qiáng)制要求
如果上述條件沒有一個(gè)為真,綁定驗(yàn)證將會(huì)被忽略。如果必須實(shí)現(xiàn)驗(yàn)證,應(yīng)用程序?qū)z查站點(diǎn)提供的任何證書是否與網(wǎng)絡(luò)安全配置文件中定義的某個(gè)證書匹配,此時(shí)驗(yàn)證就是成功的。如果沒有發(fā)生這種情況,該方法將拋出前一個(gè)stacktrace中顯示的異常。
一開始,我以為這個(gè)問題存在于用來分析每一個(gè)證書的for循環(huán)中,所以我在Frida腳本中添加了下列l(wèi)og:
var Pin = Java.use("android.security.net.config.Pin");Pin.$init.implementation = function (digestAlg, digest) {var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());console.log("\nBacktrace:\n" + bt);console.log(digestAlg);return this.$init(digestAlg,digest);}
它可以輸出驗(yàn)證過程中的每一個(gè)pin,當(dāng)我運(yùn)行改動(dòng)過的應(yīng)用程序之后,我發(fā)現(xiàn)這并沒有什么用。于是我又在log中添加了針對(duì)pinSet.getPinAlgorithms()的調(diào)用,并在for循環(huán)之前執(zhí)行:
var PinSet = Java.use("android.security.net.config.PinSet");PinSet.getPinAlgorithms.implementation = function () {var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());console.log("\nBacktrace:\n" + bt);return this.getPinAlgorithms();}
這一次什么都沒打印出來,于是接下來我得看看函數(shù)的條件判斷是否為真,因此我在腳本中添加了下列代碼:
NetworkSecurityTrustManager.checkPins.implementation = function (pins) {var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());console.log("\nBacktrace:\n" + bt);pinSet = this.mNetworkSecurityConfig.value.getPins();console.log("pinSet.pins.value.isEmpty: " +pinSet.pins.value.isEmpty());console.log("isPinningEnforced: " +this.isPinningEnforced(pins));console.log("pins.isEmpty: " +pins.isEmpty());console.log(System.currentTimeMillis())console.log(pinSet.expirationTime.value);console.log(System.currentTimeMillis() > pinSet.expirationTime.value);this.checkPins(pins);}
運(yùn)行應(yīng)用程序之后,我拿到了下列輸出:
pinSet.pins.value.isEmpty: falseisPinningEnforced: false <-- this condition is the problematic onepins.isEmpty: false15620312482749223372036854775807false
我們可以看到,isPinningEnforced為False,此時(shí)其他所有的表達(dá)式都將為True。該方法的實(shí)現(xiàn)代碼如下:
private boolean isPinningEnforced(List<X509Certificate> chain) throws CertificateException {if (chain.isEmpty()) {return false;}X509Certificate anchorCert = chain.get(chain.size() - 1);TrustAnchor chainAnchor =mNetworkSecurityConfig.findTrustAnchorBySubjectAndPublicKey(anchorCert);if (chainAnchor == null) {throw new CertificateException("Trusted chain does not end in a TrustAnchor");}return !chainAnchor.overridesPins;}
原來,問題出在findTrustAnchorBySubjectAndPublicKey的身上,它是NetworkSecurityConfig類中的一個(gè)方法,能夠返回一個(gè)chainAnchor:
public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {for (CertificatesEntryRef ref : mCertificatesEntryRefs) {TrustAnchor anchor = ref.findBySubjectAndPublicKey(cert);if (anchor != null) {return anchor;}}return null;}
它會(huì)在配置過程中對(duì)創(chuàng)建的CertificatesEntryRef進(jìn)行迭代,并返回第一個(gè)跟SubjectAndPublicKey匹配的對(duì)象。在這個(gè)場景中,它將返回的是其中一個(gè)代理。研究完源代碼后,我找到了CertificatesEntryRef類,并發(fā)現(xiàn)了唯一一個(gè)構(gòu)造器:
public CertificatesEntryRef(CertificateSource source, boolean overridesPins) {mSource = source;mOverridesPins = overridesPins;}
如果再回頭看一次Frida腳本,你將會(huì)發(fā)現(xiàn)CertificatesEntryRef是以下列方式創(chuàng)建的:
NetworkSecurityConfig_Builder.getEffectiveCertificatesEntryRefs.implementation = function(){origin = this.getEffectiveCertificatesEntryRefs()source = UserCertificateSource.getInstance()userCert = CertificatesEntryRef.$new(source,true) <-- sets overridesPins in trueorigin.add(userCert)return origin}
這也就是為什么這個(gè)腳本適用于所有場景。
原始引用:【鏈接】
在這個(gè)場景下,唯一適用的就是不包含網(wǎng)絡(luò)安全配置文件的應(yīng)用程序。
我分析了一下為什么補(bǔ)丁不起作用,原因在于parseNetworkSecurityConfig方法:
XmlUtils.beginDocument(parser, "network-security-config");int outerDepth = parser.getDepth();while (XmlUtils.nextElementWithin(parser, outerDepth)) {//here it creates a NetworkSecurityconfig.Builder based on the xml structure....}...NetworkSecurityConfig.Builder platformDefaultBuilder =NetworkSecurityConfig.getDefaultBuilder(mTargetSdkVersion); <-- this is the method changed with the scriptaddDebugAnchorsIfNeeded(debugConfigBuilder, platformDefaultBuilder);//baseConfigBuilder is null only if the xml network-security-config is not defined in the AndroidManifest.xmlif (baseConfigBuilder != null) {baseConfigBuilder.setParent(platformDefaultBuilder);addDebugAnchorsIfNeeded(debugConfigBuilder, baseConfigBuilder);} else {baseConfigBuilder = platformDefaultBuilder;}...mDefaultConfig = baseConfigBuilder.build();mDomainMap = configs;}
構(gòu)建方法會(huì)生成NetworkSecurityConfig實(shí)體:
public NetworkSecurityConfig build() {boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();boolean hstsEnforced = getEffectiveHstsEnforced();PinSet pinSet = getEffectivePinSet();List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);}
有效證書源在entryRefs變量中定義,其構(gòu)造方法如下:
private List<CertificatesEntryRef> getEffectiveCertificatesEntryRefs() {if (mCertificatesEntryRefs != null) {return mCertificatesEntryRefs;}if (mParentBuilder != null) {return mParentBuilder.getEffectiveCertificatesEntryRefs();}return Collections.<CertificatesEntryRef>emptyList();}
此時(shí),mCertificatesEntryRefs不為空,并會(huì)返回標(biāo)準(zhǔn)的SystemCertificateSource。因此,mParentBuilder永遠(yuǎn)不會(huì)被調(diào)用。
接下來,當(dāng)服務(wù)器證書驗(yàn)證成功后,應(yīng)用程序?qū)?huì)調(diào)用NetworkSecurityConfig.findTrustAnchorBySubjectAndPublicKey方法,該方法將會(huì)對(duì)有效證書進(jìn)行過濾:
public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {for (CertificatesEntryRef ref : mCertificatesEntryRefs) {TrustAnchor anchor = ref.findBySubjectAndPublicKey(cert);if (anchor != null) {return anchor;}}return null;}
并導(dǎo)致棧跟蹤拋出異常:
com.android.org.conscrypt.TrustManagerImpl.checkTrusted(TrustManagerImpl.java:375)at com.android.org.conscrypt.TrustManagerImpl.getTrustedChainForServer(TrustManagerImpl.java:304)at android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted(NetworkSecurityTrustManager.java:94)at android.security.net.config.RootTrustManager.checkServerTrusted(RootTrustManager.java:88)...
原始引用:【鏈接】
該腳本適用于三個(gè)場景中的其中兩個(gè),因?yàn)檠a(bǔ)丁會(huì)在驗(yàn)證證書的方法中執(zhí)行。但是它不適用于第三種場景,因?yàn)樽C書綁定是在其他方法中執(zhí)行的,具體可以參考棧跟蹤記錄中的錯(cuò)誤信息:
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:203)at com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:592)at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:351)... 25 more
在這個(gè)場景下,修補(bǔ)的方法為getConfigSource,當(dāng)代碼對(duì)network-security-config進(jìn)行解析時(shí)將會(huì)調(diào)用這個(gè)方法。我們可以看到,重寫的方法會(huì)創(chuàng)建一個(gè)DefaultConfigSource,并在Android 23中以參數(shù)形式進(jìn)行定義。
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。
網(wǎng)站欄目:如何使用Frida繞過Android網(wǎng)絡(luò)安全配置
文章起源:http://aaarwkj.com/article36/peggsg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、移動(dòng)網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作、微信公眾號(hào)、網(wǎng)站內(nèi)鏈、定制開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)