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) = process.create_(jscode) .on('message' on_message) print ("[*] Intercepting on (pid: )...") .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.PIPEstdout=subprocess.PIPE shell=True) for i in range(1len(cmd)): s.stdin.write(str(cmd[i]+"\r\n").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:
结果
再次抓包,如下:
成功绕过服务器校验。