Android滲透測試HTTPS證書校驗繞過
日常Android滲透過程中,會經常遇見https證書校驗(http就不存在證書校驗了,直接抓包便可),不能抓取數據包。APP是HTTPS的服務提供方自己開發的客戶端,開發者可以先將自己服務器的證書打包內置到自己的APP中,或者將證書簽名內置到APP中,當客戶端在請求服務器建立連接期間收到服務器證書后,先使用內置的證書信息校驗一下服務器證書是否合法,如果不合法,直接斷開。
環境
nuexs 5 windows10 burpsuite jeb3
情況分類
情況1,客戶端不存在證書校驗,服務器也不存在證書校驗。
情況2,客戶端存在校驗服務端證書,服務器也不存在證書校驗,單項校驗。
情況3、客戶端存在證書校驗,服務器也存在證書校驗,雙向校驗。
情況1
1、可以拿到apk文件,利用jeb3反編譯apk文件,反編譯成功,Ctrl+f -->搜索checkClientTrusted或者checkServerTrusted字符串,如下

結果如下:

反編譯成java代碼,如下:

分析得知,apk程序客戶端與服務端都沒有存在證書校驗。設置代理,偽造證書,成功抓取數據包。
情況2
1、可以拿到apk文件,利用jeb3反編譯apk文件,反編譯成功,Ctrl+f -->搜索checkClientTrusted字符串,如下

反編譯成java代碼,如下

偽造證書代理,抓取數據包出現如下:

存在證書校驗,不能成功進行抓取數據包。客戶端校驗服務端證書,這個過程由于客戶端操作,存在不可控因素,可通過客戶端進行繞過https校驗。
繞過思路1
反編譯apk,找到校驗證書方法,將校驗部分刪除,從而變成情況1,成功抓取數據包。如下
利用Androidkiller.exe反編譯apk文件,找到checkServerTrusted方法的smali代碼:

將所有校驗部分刪除,如下:

反編譯apk文件,jeb打開重打包的apk。如下

安裝重打包的apk,運行,設置代理,抓取數據包

成功抓到數據包。
繞過思路2
JustTrustMe.apk 是一個用來禁用、繞過 SSL 證書檢查的基于 Xposed 模塊。JustTrustMe 是將 APK 中所有用于校驗 SSL 證書的 API 都進行了 Hook,從而繞過證書檢查。xposed的54的可以運行,高版本沒有測試,可以自行測試。
使用如下:

重啟設備,抓取數據包,成功抓取,如下:

繞過思路3
Frida進行繞過,Frida安裝教程自行百度,可以下載這里腳本。其中application.py腳本,我修改了一下:
# -*- coding: utf-8 -*-
import frida, sys, re, sys, os
import codecs, time
APP_NAME = ""
def sbyte2ubyte(byte):
return (byte % 256)
def print_result(message):
print ("[!] Received: [%s]" %(message))
def on_message(message, data):
if 'payload' in message:
data = message['payload']
if type(data) is str:
print_result(data)
elif type(data) is list:
a = data[0]
if type(a) is int:
hexstr = "".join([("%02X" % (sbyte2ubyte(a))) for a in data])
print_result(hexstr)
print_result(hexstr.decode('hex'))
else:
print_result(data)
print_result(hexstr.decode('hex'))
else:
print_result(data)
else:
if message['type'] == 'error':
print (message['stack'])
else:
print_result(message)
def main():
try:
with codecs.open("hooks.js", 'r', encoding='utf8') as f:
jscode = f.read()
process = frida.get_usb_device().attach(APP_NAME)
script = process.create_script(jscode)
script.on('message', on_message)
print ("[*] Intercepting on (pid: )...")
script.load()
sys.stdin.read()
except KeyboardInterrupt:
print ("[!] Killing app...")
if __name__ == "__main__":
if (len(sys.argv) > 1):
APP_NAME = str(sys.argv[1])
main()
else:
print("must input two arg")
print("For exanple: python application.py packName")
這里以com.flick.flickcheck包名為例,首先pc端運行啟動frida服務器腳本startFridaService.py,
# -*- coding: utf-8 -*-
# python3.7
import sys
import subprocess
cmd = ["adb shell","su","cd /data/local/tmp","./frida-server-12-7-11-android-arm64"]
def Forward1():
s = subprocess.Popen("adb forward tcp:27042 tcp:27042")
return s.returncode
def Forward2():
s = subprocess.Popen("adb forward tcp:27043 tcp:27043")
return s.returncode
def Run():
s = subprocess.Popen("adb shell", stdin=subprocess.PIPE,stdout=subprocess.PIPE, shell=True)
for i in range(1,len(cmd)):
s.stdin.write(str(cmd[i]+"\r").encode("utf-8"))
s.stdin.flush()
return s.returncode
if __name__ == "__main__":
Forward1()
print("adb forward tcp:27042 tcp:27042")
Forward2()
print("adb forward tcp:27043 tcp:27043")
print("Android server--->./frida-server-12-7-11-android-arm64")
print("success-->frida-ps -R")
Run()
pc端運行命令如下:
python application.py com.flick.flickcheck

設置代理,抓取數據包:

成功抓取數據包。
情況3
雙向認證:apk客戶端對服務器證書進行認證,服務器對客戶端證書進行認證,防止證書被篡改。
客戶端
首先我們嘗試使用 JustTrustMe.apk進行繞過,如果發現繞過不了,可能客戶端還存在其他校驗,這里發現一個還使用了,如下

下載JustTrustMe的源代碼,進行編譯,增加對這兩個函數的hook,繞過攔截請求。重新編譯繞過的JustTrustMe1.apk
下載安裝如下:

勾選,重啟設備 (真機需要重啟設備xposed才能生效,模擬器軟重啟便可生效),再次請求登錄數據包,客戶端返回:

成功繞過客戶端驗證,成功抓取數據包,查看服務端返回數據包

這里客戶端可嘗試frida hook進行繞過那個兩個網絡攔截方法,可自己嘗試。
服務端
服務器認證,是因為服務端存有客戶端的公鑰,客戶端自己用私鑰進行簽名,服務端用公鑰進行驗證。
因此,客戶端私鑰一般都是存放在apk本身內,在apk里找到私鑰,便可利用私鑰對證書進行簽名。
1、拿到apk,如果存在殼,先進行脫殼,不存在,則直接查看apk的目錄,在assets目錄下發現了用于雙向認證的證書庫文件

要使用證書庫,我們還需要找到證書庫的密碼,jeb反編譯apk,搜索字符串證書名字字符串 :

反編譯此方法:

發現證書調用KeyStore.getInstance方法進行簽名,KeyStore.load的方法進行加載私鑰,這里如果apk有殼(加固),可以通過hook這兩個函數拿到簽名方法與私鑰。
偽造服務端信任證書,可使用Burpsuite進行操作,User options--->SSL--->Client SSL Certificates,點擊Add:

結果

再次抓包,如下:

成功繞過服務器校驗。