使用frida繞過安卓ssl pinning

一、背景

安卓安全測試中,其中一個維度測試就是服務端業務邏輯安全性測試,主要通過抓包實現。
其實說白了就是中間人攻擊,裝着要測試APP的安卓手機發包給我們電腦的burpsuite,我們可以使用bp截包改包的內容,再發給服務端,服務端收到請求後響應,把返回包發給電腦的bp,bp最後發給我們手機上的app。
隨着移動端安全逐漸加強,現在越來越多的app已經無法抓到包,或者提示網絡相關錯誤。其實根本原因在於客戶端發包時對於服務端的ssl證書進行了校驗。

二、客戶端證書處理邏輯分類

客戶端關於證書的處理邏輯,按照安全等級可做如下分類:
在這裏插入圖片描述
本文所述的使用frida繞過ssl pinning即通過hook注入方式篡改鎖定邏輯,可破解後三種情況。

三、ssl pinning原理

證書鎖定(SSL/TLS Pinning)即將服務器提供的SSL/TLS證書內置到APP客戶端中,當客戶端發請求時,通過比對內置的證書和服務器端證書的內容,以確定這個連接的合法性。
所以,ssl pinning需要開發人員將APP代碼內置僅接受指定CA或域名的證書,而不接受操作系統或瀏覽器內置的CA根證書對應的任何證書,通過這種授權方式,保障了APP與服務端通信的唯一性和安全性。但是CA簽發證書都存在有效期問題,所以缺點是在證書續期後需要將證書重新內置到APP中。

四、frida繞過安卓ssl pinning

那麼,假設一個APP使用了ssl pinning,不信任任何系統和用戶證書,整個通信過程就真的安全了嗎,安全測試人員就完全沒辦法使用bp抓包來測試服務端業務邏輯安全了嗎。
答案當然是否定的,我們可以使用VirtualXposed+JustTrustMe在安卓手機不需root條件下,禁用客戶端的SSL 證書檢查,但是這種方法現在對很多APP也不奏效了。
因此,本文推薦使用frida繞過安卓APP的ssl pinning機制。我們想要實現的效果就是繞過安卓APP的ssl pinning機制,把包發給我們電腦的bp(說白了就是要實現對APP的中間人攻擊),以進行服務端安全測試。

1. 繞過原理

  1. 從安卓手機中加載我們自己的 CA證書
  2. 創建包含我們信任CA證書的KeyStore
  3. 創建TrustManager,讓它信任我們KeyStore中的CA證書

當安卓APP初始化SSLContext時,我們使用frida劫持SSLContext.init方法,使用我們自己創建的TrustManager , 把它作爲實參傳入SSLContext.init方法的第二個參數( SSLContext.init(KeyManager,TrustManager,SecuRandom) 。這樣我們就使APP信任我們的CA了。
以上邏輯都是通過一個js注入腳本實現的。

2. 使用工具

Frida

Frida就是一個讓你可以注入腳本到APP中的工具,從而修改APP的行爲,並實時的進行動態測試。
github鏈接:https://github.com/frida/frida
本文基於python 3.7版本(推薦使用python3版本運行frida),frida 12.9.4版本。

root設備/模擬器

這邊推薦大家某寶買個谷歌nexus安卓手機,可以很方便刷機、root設備和進行安全測試,而且也不貴,就兩三百,由於需要注入腳本,所以手機必須是root過的。
模擬器可使用genymotion。
本文是基於nexus 6p安卓機,可root。

adb

adb工具對於移動端安全測試還是很重要的,建議大家裝個android studio安卓開發環境,裝完後自帶adb。

注入腳本

注入腳本我們使用某國外大神寫的基於js的注入腳本,地址在:https://techblog.mediaservice.net/wp-content/uploads/2017/07/frida-android-repinning_sa-1.js
在這裏插入圖片描述

3. 繞過過程

0x01 安裝Frida

Frida需要在手機上安裝服務端,電腦上安裝客戶端。

  1. 電腦安裝客戶端,使用阿里源加速
pip3 install Frida -i https://mirrors.aliyun.com/pypi/simple/
pip3 install frida-tools -i https://mirrors.aliyun.com/pypi/simple/
  1. 手機usb連接電腦,adb連接成功後輸入adb shell getprop ro.product.cpu.abi,查看手機arch 版本,然後去https://github.com/frida/frida/releases下載對應的frida 服務端版本到電腦上
    在這裏插入圖片描述
    在這裏插入圖片描述
  2. 在電腦上解壓.xz結尾的壓縮文件,得到可執行文件
  3. 通過adb命令將剛剛解壓可執行的文件push到安卓指定目錄:adb push C:\xxx\frida-server /data/local/tmp
  4. adb shell chmod 777 /data/local/tmp/frida-server給可執行文件權限

0x02 準備burpsuite證書

爲了能攔截流量,frida要訪問我們的Burpsuite CA證書,因此通過bp下載其證書,並將其重命名爲cert-der.crt,使用adb push C:\xxx\cert-der.crt /data/local/tmp命令同樣將其push到手機相同目錄

0x03 準備注入腳本

將以下代碼保存爲在電腦上,命名爲fridascript.js

/* 

   Android SSL Re-pinning frida script v0.2 030417-pier

$ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt

   $ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause

https://techblog.mediaservice.net/2017/07/universal-android-ssl-pinning-bypass-with-frida/

   

   UPDATE 20191605: Fixed undeclared var. Thanks to @oleavr and @ehsanpc9999 !

*/

setTimeout(function(){

    Java.perform(function (){

     console.log("");

     console.log("[.] Cert Pinning Bypass/Re-Pinning");

var CertificateFactory = Java.use("java.security.cert.CertificateFactory");

     var FileInputStream = Java.use("java.io.FileInputStream");

     var BufferedInputStream = Java.use("java.io.BufferedInputStream");

     var X509Certificate = Java.use("java.security.cert.X509Certificate");

     var KeyStore = Java.use("java.security.KeyStore");

     var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");

     var SSLContext = Java.use("javax.net.ssl.SSLContext");

// Load CAs from an InputStream

     console.log("[+] Loading our CA...")

     var cf = CertificateFactory.getInstance("X.509");

     

     try {

      var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");

     }

     catch(err) {

      console.log("[o] " + err);

     }

     

     var bufferedInputStream = BufferedInputStream.$new(fileInputStream);

    var ca = cf.generateCertificate(bufferedInputStream);

     bufferedInputStream.close();

var certInfo = Java.cast(ca, X509Certificate);

     console.log("[o] Our CA Info: " + certInfo.getSubjectDN());

// Create a KeyStore containing our trusted CAs

     console.log("[+] Creating a KeyStore for our CA...");

     var keyStoreType = KeyStore.getDefaultType();

     var keyStore = KeyStore.getInstance(keyStoreType);

     keyStore.load(null, null);

     keyStore.setCertificateEntry("ca", ca);

     

     // Create a TrustManager that trusts the CAs in our KeyStore

     console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");

     var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();

     var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);

     tmf.init(keyStore);

     console.log("[+] Our TrustManager is ready...");

console.log("[+] Hijacking SSLContext methods now...")

     console.log("[-] Waiting for the app to invoke SSLContext.init()...")

SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) {

      console.log("[o] App invoked javax.net.ssl.SSLContext.init...");

      SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);

      console.log("[+] SSLContext initialized with our custom TrustManager!");

     }

    });

},0);

0x04 利用腳本注入

  1. 在手機上運行Frida服務端
adb shell
su
/data/local/tmp/frida-server &

在這裏插入圖片描述
2. 通過frida-ps -U命令找到你要注入的正在運行APP的package name,如果知道的話,也不用事先找
在這裏插入圖片描述
3. 最後運行frida -U -f com.xxx.xxx-l C:\xxx\fridascript.js --no-paus 命令,將腳本注入到原生應用程序中。手機別忘了設置代理,指向電腦的bp監聽端口,bp即可截取到通信過程的包
在這裏插入圖片描述

五、後記

  1. 是否能繞過所有APP的ssl pinning機制
    安全其實是木桶原理,一些銀行類APP,安全防護機制比較健全,會進行注入的檢測,所以無法使用Frida進行注入。但是本文敘述的方法適用大部分APP。
  2. 是否能抓到所有通信包
    需要看具體APP業務邏輯,我能確認的是現在有一些APP的重要接口或者新接口會走socket通信,目前對於socket通信的包,還沒有比較好的截取辦法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章