Android平臺HTTPS抓包解決方案及問題分析

MegatronKing發表於2019-04-27

HTTP協議發展至今已經有二十多年的歷史,整個發展的趨勢主要是兩個方向:效率和安全。效率方面,從HTTP1.0的一次請求一個連線,到HTTP1.1的連線複用,到SPDY/HTTP2的多路複用,到QUIC/HTTP3的基於UDP傳輸,在效率方面越來越高效。安全方面,從HTTP的明文,到HTTP2強制使用TLSv1.2,到QUIC/HTTP3強制使用TLSv1.3,越來越注重資料傳輸的安全性。總而言之,HTTP協議的發展對使用者是友好的,但是對開發者而言卻不那麼友善。

抓包是每個程式設計師的必修技能之一,尤其是在介面除錯和程式逆向方面具有廣闊的用途。但是,隨著越來越多的通訊協議使用加密的HTTPS,而且系統層面也開始強制規定使用HTTPS,抓包似乎是顯得越來越難了。

本篇部落格,主要詳解Android平臺下,HTTPS抓包的常見問題以及解決辦法。工欲善其事必先利其器,部落格中以HttpCanary作為抓包工具進行講解。更多HttpCanary的資料,請見:github.com/MegatronKin…

1. 抓包原理

幾乎所有網路資料的抓包都是採用中間人的方式(MITM),包括大家常用的Fiddler、Charles等知名抓包工具,HttpCanary同樣是使用中間人的方式進行抓包。

Android平臺HTTPS抓包解決方案及問題分析

從上面這個原理圖,可以看出抓包的核心問題主要是兩個:

  • MITM Server如何偽裝成真正的Server;
  • MITM Client如何偽裝成真正的Client。

第一個問題,MITM Server要成為真正的Server,必須能夠給指定域名簽發公鑰證照,且公鑰證照能夠通過系統的安全校驗。比如Client傳送了一條https://www.baidu.com/的網路請求,MITM Server要偽裝成百度的Server,必須持有www.baidu.com域名的公鑰證照併發給Client,同時還要有與公鑰相匹配的私鑰。

MITM Server的處理方式是從第一個SSL/TLS握手包Client Hello中提取出域名www.baidu.com,利用應用內建的CA證照建立www.baidu.com域名的公鑰證照和私鑰。建立的公鑰證照在SSL/TLS握手的過程中發給Client,Client收到公鑰證照後會由系統會對此證照進行校驗,判斷是否是百度公司持有的證照,但很明顯這個證照是抓包工具偽造的。為了能夠讓系統校驗公鑰證照時認為證照是真實有效的,我們需要將抓包應用內建的CA證照手動安裝到系統中,作為真正的證照發行商(CA),即洗白。這就是為什麼,HTTPS抓包一定要先安裝CA證照。

第二個問題,MITM Client偽裝成Client。由於伺服器並不會校驗Client(絕大部分情況),所以這個問題一般不會存在。比如Server一般不會關心Client到底是Chrome瀏覽器還是IE瀏覽器,是Android App還是iOS App。當然,Server也是可以校驗Client的,這個後面分析。

2. 安裝CA證照

抓包應用內建的CA證照要洗白,必須安裝到系統中。而Android系統將CA證照又分為兩種:使用者CA證照和系統CA證照。顧明思議,使用者CA證照是由使用者自行安裝的,系統CA證照是由系統內建的,很明顯後者更加真實有效。

系統CA證照存放在/etc/security/cacerts/目錄下,名稱是CA證照subjectDN的Md5值前四位移位取或,字尾名是.0,比如00673b5b.0。考慮到安全原因,系統CA證照需要有Root許可權才能進行新增和刪除。

對於非Root的Android裝置,使用者只能安裝使用者CA證照。

無論是系統CA證照還是使用者CA證照,都可以在設定->系統安全->加密與憑據->信任的憑據中檢視:

Android平臺HTTPS抓包解決方案及問題分析

3. Android 7.0的使用者CA證照限制

Android從7.0開始系統不再信任使用者CA證照(應用targetSdkVersion >= 24時生效,如果targetSdkVersion < 24即使系統是7.0+依然會信任)。也就是說即使安裝了使用者CA證照,在Android 7.0+的機器上,targetSdkVersion >= 24的應用的HTTPS包就抓不到了。

比如上面的例子,抓包工具用內建的CA證照,建立了www.baidu.com域名的公鑰證照發給Client,系統校驗此證照時發現是使用者CA證照籤發的,sorry。。。

那麼,我們如果繞過這種限制呢?已知有以下四種方式(低於7.0的系統請忽略):

3.1 AndroidManifest中配置networkSecurityConfig

如果我們想抓自己的App,只需要在AndroidManifest中配置networkSecurityConfig即可:

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:networkSecurityConfig="@xml/network_security_config"
                    ... >
        ...
    </application>
</manifest>
複製程式碼
<?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證照的校驗給予通過。更多相關資訊,詳見Network security configuration

3.2 調低targetSdkVersion < 24

如果想抓一個App的包,可以找個歷史版本,只需要其targetSdkVersion < 24即可。然而,隨著GooglePlay開始限制targetSdkVersion,現在要求其必須>=26,2019年8月1日後必須>=28,國內應用市場也開始逐步響應這種限制。絕大多數App的targetSdkVersion都將大於24了,也就意味著抓HTTPS的包越來越難操作了。

3.3 平行空間抓包

如果我們希望抓targetSdkVersion >= 24的應用的包,那又該怎麼辦呢?我們可以使用平行空間或者VirtualApp來曲線救國。平行空間和VirtualApp這種多開應用可以作為宿主系統來執行其它應用,如果平行空間和VirtualApp的targetSdkVersion < 24,那麼問題也就解決了。

在此,我推薦使用平行空間,相比部分開源的VirtualApp,平行空間執行得更加穩定。但必須注意平行空間的版本4.0.8625以下才是targetSdkVersion < 24,別安裝錯了。當然,HttpCanary的設定中是可以直接安裝平行空間的。

3.4 安裝到系統CA證照目錄

對於Root的機器,這是最完美最佳的解決方案。如果把CA證照安裝到系統CA證照目錄中,那這個假CA證照就是真正洗白了,不是真的也是真的了。由於系統CA證照格式都是特殊的.0格式,我們必須將抓包工具內建的CA證照以這種格式匯出,HttpCanary直接提供了這種匯出選項。

操作路徑:設定 -> SSL證照設定 -> 匯出HttpCanary根證照 -> System Trusted(.0)

Android平臺HTTPS抓包解決方案及問題分析

PS. 很不幸的HttpCanary v2.8.0前匯出的證照名稱可能不正確,建議升級到v2.8.0以上版本操作。

匯出.0格式的證照後,可以使用MT管理器將.0檔案複製到/etc/security/cacerts/目錄下,或者通過adb remount然後push也可(這裡稍微提一下,別在sdcard裡找這個目錄)。

4. Firefox證照安裝

火狐瀏覽器Firefox自行搞了一套CA證照管理,無論是系統CA證照還是使用者CA證照,Firefox通通都不認可。這種情況,我們需要將CA證照通過特殊方式匯入到Firefox中,否則Firefox瀏覽網頁就無法工作了。

HttpCanary v2.8.0版本提供了Firefox證照匯入選項。在設定 -> SSL證照設定 -> 新增HttpCanary根證照至Firefox 中:

Android平臺HTTPS抓包解決方案及問題分析

點選右上角複製按鈕將url複製到貼上板,然後保持此頁面不動,開啟Firefox貼上輸入複製的url。

Android平臺HTTPS抓包解決方案及問題分析

出現下載證照彈框後,一定要手動勾上:信任用來標誌網站和信任用來標誌電子郵件使用者。然後確定即可。

5. 公鑰證照固定

證照固定(Certificate Pinning)是指Client端內建Server端真正的公鑰證照。在HTTPS請求時,Server端發給客戶端的公鑰證照必須與Client端內建的公鑰證照一致,請求才會成功。

在這種情況下,由於MITM Server建立的公鑰證照和Client端內建的公鑰證照不一致,MITM Server就無法偽裝成真正的Server了。這時,抓包就表現為App網路錯誤。已知的知名應用,比如餓了麼,就採用了證照固定。

另外,有些伺服器採用的自簽證照(證照不是由真正CA發行商簽發的),這種情況App請求時必須使用證照固定。

證照固定的一般做法是,將公鑰證照(.crt或者.cer等格式)內建到App中,然後建立TrustManager時將公鑰證照加進去。很多應用還會將內建的公鑰證照偽裝起來或者加密,防止逆向提取,比如餓了麼就偽裝成了png,當然對公鑰證照偽裝或者加密沒什麼太大必要,純粹自欺欺人罷了。

證照固定對抓包是個非常麻煩的阻礙,不過我們總是有辦法繞過的,就是麻煩了點。

5.1 JustTrustMe破解證照固定

Xposed和Magisk都有相應的模組,用來破解證照固定,實現正常抓包。

例如:github.com/Fuzion24/Ju…

破解的原理大致是,Hook建立SSLContext等涉及TrustManager相關的方法,將固定的證照移除。

5.2 基於VirtualApp的Hook機制破解證照固定

Xposed和Magisk需要刷機等特殊處理,但是如果不想刷機折騰,我們還可以在VirtualApp中加入Hook程式碼,然後利用VirtualApp開啟目標應用進行抓包。當然,有開發者已經實現了相關的功能。詳見:

不過,這裡CertUnpinning外掛的程式碼有點問題,要改改。

5.3 匯入真正的公鑰證照和私鑰

如果Client固定了公鑰證照,那麼MITM Server必須持有真正的公鑰證照和匹配的私鑰。如果開發者具有真正服務端的公鑰證照和私鑰,(比如百度的公鑰證照和私鑰百度的後端開發肯定有),如果真有的話,可以將其匯入HttpCanary中,也可以完成正常抓包。

在設定 -> SSL證照設定 -> 管理SSL匯入證照 中,切換到服務端,然後匯入公鑰證照+私鑰,支援.p12和.bks格式檔案。

Android平臺HTTPS抓包解決方案及問題分析

6. 雙向認證

SSL/TLS協議提供了雙向認證的功能,即除了Client需要校驗Server的真實性,Server也需要校驗Client的真實性。這種情況,一般比較少,但是還是有部分應用是開啟了雙向認證的。比如匿名社交應用Soul部分介面就使用了雙向認證。使用了雙向認證的HTTPS請求,同樣無法直接抓包。

Android平臺HTTPS抓包解決方案及問題分析

關於雙向認證的原理。

首先,雙向認證需要Server支援,Client必須內建一套公鑰證照 + 私鑰。在SSL/TLS握手過程中,Server端會向Client端請求證照,Client端必須將內建的公鑰證照發給Server,Server驗證公鑰證照的真實性。

注意,這裡的內建的公鑰證照有區別於前面第5點的公鑰證照固定,雙向認證內建的公鑰證照+私鑰是額外的一套,不同於證照固定內建的公鑰證照。

如果一個Client既使用證照固定,又使用雙向認證,那麼Client端應該內建一套公鑰證照 + 一套公鑰證照和私鑰。第一套與Server端的公鑰證照相同,用於Client端系統校驗與Server發來的證照是否相同,即證照固定;第二套SSL/TLS握手時公鑰證照發給Server端,Server端進行簽名校驗,即雙向認證。

用於雙向認證的公鑰證照和私鑰代表了Client端身份,所以其是隱祕的,一般都是用.p12或者.bks檔案+金鑰進行存放。由於是內建在Client中,儲存的金鑰一般也是寫死在Client程式碼中,有些App為了防反編譯會將金鑰寫到so庫中,比如S匿名社交App,但是隻要存在於Client端中都是有辦法提取出來的。

6.1 雙向認證抓包

這裡以S匿名社交App為例,講解下如何抓取使用了雙向認證的App的HTTPS包。

如果伺服器使用了Nginx且開啟了雙向認證,抓包時會出現400 Bad Request的錯誤,如下:

Android平臺HTTPS抓包解決方案及問題分析

Android平臺HTTPS抓包解決方案及問題分析

有些伺服器可能不會返回404,直接請求失敗。

接下來看,如何使用HttpCanary配置雙向認證抓包。

首先,解壓APK,提取出.p12或者.bks檔案,二進位制的檔案一般存放都在raw或者assets目錄。

Android平臺HTTPS抓包解決方案及問題分析

將client.p12檔案匯入手機,然後在HttpCanary的設定 -> SSL證照設定 -> 管理SSL匯入證照中,切換到客戶端(因為需要配給MITM Client),然後匯入.p12檔案。

由於雙向認證的公鑰證照和私鑰是受金鑰保護的,所以需要輸入密碼:

Android平臺HTTPS抓包解決方案及問題分析

一般通過逆向可以從APK中提取出金鑰,具體操作這裡略過。輸入金鑰後,需要輸入對映域名,這裡使用萬用字元*對映所有相關域名:

Android平臺HTTPS抓包解決方案及問題分析

匯入完成後如下:

Android平臺HTTPS抓包解決方案及問題分析

可以點進證照詳情檢視細節,這個client.p12檔案包含公鑰證照和私鑰,是用於雙向認證的。

Android平臺HTTPS抓包解決方案及問題分析

配置完成後,重新進行抓包,看看效果。

Android平臺HTTPS抓包解決方案及問題分析

可以看到,之前400 Bad Request的兩個要求雙向認證的請求成功了!

7. SSL重協商

有些伺服器可能會開啟SSL重協商,即SSL/TLS握手成功後傳送請求時伺服器會要求重新握手。這種情況一般比較少,但是也不排除,已知的應用比如 10000社群 就使用了SSL重協商。

由於Android系統對SSL重協商是有限支援,所以部分系統版本抓包會失敗,表現為網路異常。在Android 8.1以下,SslSocket是完全支援SSL重協商的,但是SSLEngine卻是不支援SSL重協商的,而HttpCanary解析SSL/TLS使用的是SSLEngine。在Android 8.1及以上,SSLEngine和SslSocket統一了實現,故是支援SSL重協商的。

所以,如果確認伺服器使用了SSL重協商,請使用8.1及以上版本系統進行抓包。

8. 非HTTP協議抓包

如果確認了以上幾點,HttpCanary仍然抓包失敗,那麼極有可能使用的並非是HTTP協議。比如像微信聊天,視訊直播等,使用的就不是HTTP協議,這種情況需要使用其它的抓包工具,比如Packet Capture這種直接解析TCP/UDP協議的,但是往往非HTTP協議的資料包即使抓到了也無法解析出來,因為大概率都是二進位制而非文字格式的。

9. 結語

抓包是個技術活兒,需要對網路協議有大致的瞭解,對抓包感興趣的同學可以多查閱TCP、UDP、SSL/TLS、HTTP等相關資料。

HttpCanary是專業的HTTP協議抓包工具,專注HTTP協議三十年(吹過頭了),不過目前還不支援QUIC/HTTP3這種新協議,等QUIC/HTTP3正式應用起來再說吧。

相關文章