Android App—HTTPS證書校驗繞過
前言:
Android滲透過程中,會經常遇見https證書校驗,不能抓取數據包。就比如我手機無法Root,每次都要用到模擬器,但是有些App它會檢查是否在模擬器中運行,從而閃退無法正常使用。於是,這篇文章誕生了。
基礎學習:
APP是HTTPS的服務提供方自己開發的客戶端,開發者可以先將自己服務器的證書打包內置到自己的APP中,或者將證書簽名內置到APP中,當客戶端在請求服務器建立連接期間收到服務器證書后,先使用內置的證書信息校驗一下服務器證書是否合法,如果不合法,直接斷開
當APP是HTTPS時,單純的使用Burpsuite無法抓取數據包,原因是APP啟用了SSL Pinning(又叫做“SSL證書綁定”)
如果這里不懂的話,可以了解以下https的建立過程:

抓包原理:
幾乎所有網絡數據的抓包都是采用中間人的方式(MITM),包括大家常用的Fiddler、Charles等知名抓包工具
不論是使用burpsuite還是fiddler,當前的抓包工具基本原理都是采用的中間人的方式。原理就是這些工具作為中間人,對客戶端偽裝成服務端,對服務端偽裝成客戶端
Android App 抓包有三種情況(https):
情況1,客戶端不存在證書校驗,服務器也不存在證書校驗。
情況2,客戶端存在校驗服務端證書,服務器也不存在證書校驗,單項校驗。
情況3、客戶端存在證書校驗,服務器也存在證書校驗,雙向校驗。
1、使用jd-gui進行反編譯后,全局搜索:checkClientTrusted 或者checkServerTrusted 字符串,如下圖:
情況1:apk程序客戶端與服務端都沒有存在證書校驗

設置代理,偽造證書,即可成功抓取數據包
情況2:存在檢測服務器端證書(SSL pinning):
由於服務器並不會校驗Client(絕大部分情況),所以這個問題一般不會存在。比如Server一般不會關心Client到底是Chrome瀏覽器還是IE瀏覽器,是Android App還是iOS App,比如谷歌瀏覽器就能夠模擬移動端。當然,Server也是可以校驗Client的,這個后面分析。

在無法root的設備上抓包:

SSL pinning:
SSL Pinning是一種防止中間人攻擊的技術,主要機制是在客戶端發起請求–>收到服務器發來的證書進行校驗,如果收到的證書不被客戶端信任,就直接斷開連接不繼續求情。可以發現中間人攻擊的要點的偽造了一個假的服務端證書給了客戶端,客戶端誤以為真。解決思路就是,客戶端也預置一份服務端的證書,比較一下就知道真假了。
SSL-pinning有兩種方式:
證書鎖定(Certificate Pinning) 和公鑰鎖定( Public Key Pinning)
證書鎖定:
需要在客戶端代碼內置僅接受指定域名的證書,而不接受操作系統或瀏覽器內置的CA根證書對應的任何證書,通過這種授權方式,保障了APP與服務端通信的唯一性和安全性,因此客戶端與服務端(例如API網關)之間的通信是可以保證絕對安全。但是CA簽發證書都存在有效期問題,缺點是在
證書續期后需要將證書重新內置到APP中
公鑰鎖定:
提取證書中的公鑰並內置到客戶端中,通過與服務器對比公鑰值來驗證連接的正確性。制作證書密鑰時,公鑰在證書的續期前后都可以保持不變(即密鑰對不變),所以可以避免證書有效期問題,一般推薦這種做法。
https需要CA證書,我們之前說的中間人需要對客戶端偽裝成真正的服務端,要求就是當客戶端向我們發送網絡請求時,我們必須能夠給指定域名簽發公鑰證書,且公鑰證書能夠通過系統的安全校驗。對於我們是不是真正的客戶端,通常來說服務器是不太會關心的,他是不會去關心你是谷歌瀏覽器還是百度瀏覽器,當然了也會有例外。接下來要說的雙向驗證就是如此
繞過方案一:
burpsuite CA證書安裝繞過:
在已經root的設備上安裝CA證書,然后直接抓包即可,在本機無法取得root的情況下,通過模擬器也可抓到https數據包
繞過方案二(無需root,通用):
思路:
反編譯apk,找到校驗證書方法,將校驗部分刪除,從而變成情況1,再編譯apk,成功抓取數據包
利用條件:
客戶端程序沒有對自身完整性進行校驗應用完整性校檢
利用Androidkiller.exe反編譯apk文件,找到checkServerTrusted方法的smali代碼:

對齊進行適量刪改后:

反編譯apk文件再查看其代碼:

安裝apk至Android設備,嘗試抓包:


繞過思路3(需root):
Xposed框架+JustTrustMe 繞過:
Xposed框架是一款開源框架,其功能是可以在不修改APK的情況下影響程序運行(修改系統)的框架服務,基於它可以制作出許多功能強大的模塊,且在功能不沖突的情況下同時運作。Xposed 就好比是 Google 模塊化手機的主體,只是一個框架的存在,在添加其他功能模塊(Modules)之前,發揮不了什么作用,但是沒了它也不行。也正因為如此,Xposed 具有比較高的可定制化程度。Moto X 用戶可定制手機的外觀、壁紙、開機動畫等,Xposed 則允許用戶自選模塊對手機功能進行自定義擴充。
JustTrustMe:https://github.com/Fuzion24/JustTrustMe/releases
JustTrustMe 一個用來禁用、繞過 SSL 證書檢查的基於 Xposed 模塊。簡單來說,JustTrustMe 是將 APK 中所有用於校驗 SSL 證書的 API 都進行了 Hook,從而繞過證書檢查
在夜神模擬器上就可以直接下載安裝:

注意:使用Xposed時需注意,盡可能安裝至模擬器中,不然手機可能變磚。

將JustTrustMe.apk 拖入模擬器中,然后進入Xposed激活並重啟

繞過方案四(需root) :
Frida繞過,詳情參考如下:
Frida詳細安裝教程
https://github.com/WooyunDota/DroidDrops/blob/master/2018/Frida.Android.Practice.md
情況三:雙向認證
當服務器啟用了證書雙向認證之后,除了客戶端去驗證服務器端的證書外,服務器也同時需要驗證客戶端的證書,也就是會要求客戶端提供自己的證書,如果沒有通過驗證,則會拒絕連接,如果通過驗證,服務器獲得用戶的公鑰
詳細過程:
(1)客戶端發起HTTPS請求,將SSL協議版本的信息發送給服務端。
(2)服務端去CA機構申請來一份CA證書,在前面提過,證書里面有服務端公鑰和簽名。將CA證書發送給客戶端
(3)客戶端讀取CA證書的明文信息,采用相同的hash散列函數計算得到信息摘要(hash目的:驗證防止內容被修改),然后用操作系統帶的CA的公鑰去解密簽名(因為簽名是用CA的私鑰加密的),對比證書中的信息摘要。如果一致,則證明證書是可信的,然后取出了服務端公鑰
(4)客戶端發送自己的客戶端證書給服務端,證書里面有客戶端的公鑰:C_公鑰
(5)客戶端發送支持的對稱加密方案給服務端,供其選擇
(6)服務端選擇完加密方案后,用剛才得到的C_公鑰去加密選好的加密方案
(7)客戶端用自己的C_私鑰去解密選好的加密方案,客戶端生成一個隨機數(密鑰F),用剛才等到的服務端B_公鑰去加密這個隨機數形成密文,發送給服務端。
(8)服務端和客戶端在后續通訊過程中就使用這個密鑰F進行通信了。和之前的非對稱加密不同,這里開始就是一種對稱加密的方式
雙向認證需要Server支持,Client必須內置一套公鑰證書 + 私鑰。在SSL/TLS握手過程中,Server端會向Client端請求證書,Client端必須將內置的公鑰證書發給Server,Server驗證公鑰證書的真實性
雙向認證內置的公鑰證書+私鑰是額外的一套,不同於證書固定內置的公鑰證書
用於雙向認證的公鑰證書和私鑰代表了Client端身份,所以其是隱秘的,一般都是用.p12或者.bks文件+密鑰進行存放。由於是內置在Client中,存儲的密鑰一般也是寫死在Client代碼中,有些App為了防反編譯會將密鑰寫到so庫中,比如S匿名社交App,但是只要存在於Client端中都是有辦法提取出來的
這里我們以Soul舉例:
該app直接封裝了客戶端的證書,相比於單項認證,多了一個服務器端驗證客戶端證書的過程,而在以往的用代理工具如burp這類工具,抓取https的包時,除了瀏覽器獲取的是代理工具的證書外,默認是不發送證書給服務器端的。burp在抓取https報文的過程中也提供了雙向認證的證書發送,但是是使用了burp提供的證書文件,也就是CA證書。app的服務端不認證這個burp提供的CA證書,那么我們就需要拿到匹配的證書,以其對服務端進行匹配
首先,解壓APK,提取出.p12/.pfx或者.bks文件,二進制的文件一般存放都在raw或者assets目錄

由於雙向認證的公鑰證書和私鑰是受密鑰保護的,所以需要輸入密碼
app解密的代碼邏輯:
客戶端發送數據包以后,需要去從app中讀取這個證書文件,密碼是以硬編碼形式放在了代碼中,利用這個代碼中的密碼字段去解密證書文件,從中讀取以后,再進行解密並回傳給服務器端進行確認。由此推斷,尋找證書名稱應該就可以拿到安裝密碼
利用搜索來找client.p12的值(或者關鍵字PKCS12,這是通常讀取證書需要用到的關鍵字)
一般通過逆向可以從APK中提取出密鑰,怪我太菜java完全不會,Android滲透剛剛接觸,這里先略過,以后再來填坑……

詳情可以參考:
APP 如何進行HTTPS雙向認證抓包
APP無法抓包解決方案 安卓單向/雙向認證
找到之后可以通過burp添加客戶端證書:
host填寫app服務端的主域名:

選擇app客戶端內的client.p12證書文件,並輸入安裝密碼

證書成功導入后,勾選即可使用
雙向驗證與SSL pinning的區別:
SSL pinning實際上是客戶端鎖定服務器端的證書, 在要與服務器進行交互的時候, 服務器端會將CA證書發送給客戶端, 客戶端會調用函數對服務器端的證書進行校驗, 與本地的服務器端證書(存放在.\asset目錄或\res\raw下)進行比對。而雙向認證是添加了客戶端向服務器發送CA證書, 服務器端對客戶端的證書進行校驗的部分。在app上,https雙向認證的方案也可以防止中間人劫持,但這種雙向認證開銷較大,且安全性與SSL pinning一致,目前大多數app都采用SSL Pinning這種方案
CA證書:
抓包應用內置的CA證書要洗白,必須安裝到系統中。而Android系統將CA證書又分為兩種:用戶CA證書和系統CA證書。顧明思議,用戶CA證書是由用戶自行安裝的,系統CA證書是由系統內置的,很明顯后者更加真實有效
系統CA證書存放在/etc/security/cacerts/目錄下,名稱是CA證書subjectDN的Md5值前四位移位取或,后綴名是.0,比如00673b5b.0。考慮到安全原因,系統CA證書需要有Root權限才能進行添加和刪除。
對於非Root的Android設備,用戶只能安裝用戶CA證書。
使用限制:
Android從7.0開始系統不再信任用戶CA證書(應用targetSdkVersion >= 24時生效,如果targetSdkVersion < 24即使系統是7.0+依然會信任)。也就是說即使安裝了用戶CA證書,在Android 7.0+的機器上,targetSdkVersion >= 24的應用的HTTPS包就抓不到了
繞過方案一:配置networkSecurityConfig
1、在AndroidManifest中配置networkSecurityConfig
形如:
<?xml version="1.0" encoding="utf-8"?> <manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > ... </application> </manifest>
2、在項目res目錄下新增一個文件夾,命名xml,並且新建一個xml文件,命名為network_security_config.xml,命名名稱跟上面匹配。
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <certificates src="system" /> <certificates src="user" /> </trust-anchors> </base-config> </network-security-config>
這樣即表示,App信任用戶CA證書,讓系統對用戶CA證書的校驗給予通過
詳情請參考:https://developer.android.com/training/articles/security-config
最開始直接通過adb安裝,發現無法抓到包

成功抓到包:


繞過方案二:調低targetSdkVersion < 24
如果想抓一個App的包,可以找個歷史版本,只需要其targetSdkVersion < 24即可。然而,隨着GooglePlay開始限制targetSdkVersion,現在要求其必須>=26,2019年8月1日后必須>=28,國內應用市場也開始逐步響應這種限制。然后目前絕大多數App的targetSdkVersion都大於24
繞過方案三:平行空間抓包:
如果我們希望抓targetSdkVersion >= 24的應用的包,那又該怎么辦呢?我們可以使用平行空間或者VirtualApp來曲線救國。平行空間和VirtualApp這種多開應用可以作為宿主系統來運行其它應用,如果平行空間和VirtualApp的targetSdkVersion < 24,那么問題也就解決了。
繞過方案四:安裝到系統CA證書目錄(需root)
非Http協議抓包:
如果確認了以上幾點,仍然抓包失敗,那么極有可能使用的並非是HTTP協議。比如像微信聊天,視頻直播等,使用的就不是HTTP協議,這種情況需要使用其它的抓包工具,比如Packet Capture這種直接解析TCP/UDP協議的,但是往往非HTTP協議的數據包即使抓到了也無法解析出來,因為大概率都是二進制而非文本格式的。
補充:所有https能抓到包的前天是基於https證書校驗不嚴格,如果證書校驗嚴格的話是抓不到的
參考如下:
Android平台HTTPS抓包全方案
APP抓包——Xposed+JustTrustMe關閉SSL證書驗證
Android滲透測試HTTPS證書校驗繞過
【移動安全】記一次APP雙向認證抓包
android系統https抓包問題分析
APP 如何進行HTTPS雙向認證抓包
APP無法抓包解決方案 安卓單向/雙向認證