源码
源码来自:https://github.com/Ch3nYe/httpstest
打包好的APP,启动目录下的http_server,同时修改host把www.test.com指向本地。包名为:com.example.httpstest
安装后如下所示:
然后为了方便代理,我们安装一个ProxyDroid:https://github.com/madeye/proxydroid
可以从谷歌商店代理下载:https://apkpure.com/store/apps/details?id=org.proxydroid
这个东西是利用Android iptables代理,捕获所有APP数据包。一般做WiFi代理的话,有些流量不会走代理,或者还可以使用VPN的代理模式比如Postern。
一开始的两个直接做了代理就可以抓到,就不演示了。
HTTPS系统证书校验
在Android7以上的系统,用户证书不再信任,此处配置证书到系统证书目录。
openssl x509 -inform DER -in burp.der -out cacert.pem
openssl x509 -inform PEM -subject_hash_old -in cacert.pem => hash
mv cacert.pem <hash>.0
adb push hash.0 /sdcard //由于系统读写权限问题,不一定能直接上传到system目录。
mount -o remount,rw /system //root权限下执行
cp /sdcard/hash.0 /system/etc/security/cacerts/
chmod 644 /system/etc/security/cacerts/hash.0
第一行就是那个hash
后续点击执行
SSLPINNING 代码校验
这里的校验是公钥,由于中间穿插了burp,所以burp即是客户端,又是服务端,app校验的是burp的公钥导致校验失败。此处使用的是frida,先去下载frida:https://github.com/frida/frida/releases
adb push frida-server /data/local/tmp
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
cd /data/local/tmp/
chmod 755 frida-server
./frida-server
作者提供了一个frida脚本,但是按照使用方式我这边会重启模拟器,也许是模拟器的原因?这里按照一个python脚本来调用这个js脚本。
#coding:utf8
import frida, sys,os,json,codecs
import subprocess
import time
import ctypes
if (len(sys.argv) == 3):
jsfile = str(sys.argv[1].strip())
package_name = str(sys.argv[2]).strip()
else:
print "Usage: python frida_attach.py [hook.js] [package_name] "
sys.exit(1)
def print_result(message):
print ("[!] Received: [%s]" %(message))
def stringFromArray(data):
ret = ''
for i in data:
value = ctypes.c_uint8(i).value
if value == 0:
continue
if value <=127:
ret += chr(value)
else:
ret += '\\x' + hex(value)[2:]
return ret
def hex_stringFromArray(data):
ret = '['
for i in data:
value = ctypes.c_uint8(i).value
ret += hex(value) + ","
return ret + "]"
def on_message(message, data):
print(data)
if 'payload' in message:
data = message['payload']
if type(data) is list:
print stringFromArray(data)
else:
print data
else:
if message['type'] == 'error':
print (message['stack'])
else:
print message
def main():
with codecs.open(jsfile, 'r', encoding='utf8') as f:
jscode = f.read()
process = frida.get_device_manager().enumerate_devices()[-1].attach(package_name)
script = process.create_script(jscode)
script.on('message', on_message)
script.load()
sys.stdin.read()
if __name__ == '__main__':
main()
执行如下后,就可以bypass。
python .\frida_attach.py .\new_sslpinning.js httpstest
配置文件校验跟上面的形式差不多,只是一个代码实现,一个在res/xml/network_security_config.xml
配置文件中实现。
单向校验的话,还可以使用Xposed和justtrustme一起配合来绕过。
双向校验
需要先启动目录下的http_server服务,如果访问的话,浏览器会显示异常的链接请求。
需要先把certs目录下的client.p12安装到访问浏览器,密码是clientpassword。再去访问浏览器发现可以显示,同样需要把证书加到burp,让证书可以用证书进行认证。
在user options – TLS – Client TLS certificates中添加,填入域名www.test.com,输入密码即可。
也就是如果需要绕过这种双向验证,需要客户端的证书来对请求进行身份验证。一般情况下这个证书获取从APP
解压,查看assets或者res目录内,查找是否有pfx、cer、p12格式的证书。最后我们需要导入p12的证书。
frida(1)
当然不少的APP可能存在加壳加密等办法,证书和密码的获取不是那么简单,这里提供一种利用frida来获取证书和密钥的办法。
下载frida-extract-keystore:https://gist.github.com/ceres-c/cb3b69e53713d5ad9cf6aac9b8e895d2
运行脚本后,会自动的启动APP,需要在脚本内修改APP包名,点击需要执行的功能,也就是触发请求。
脚本会自动抓取写在代码内的密码和保存证书,以jks的形式。然后需要去提取公钥。
keytool -list -rfc -keystore .\keystore1.jks -storepass clientpassword
把显示的内容保存在cer格式的证书中。导出私钥先转换为pfx。
keytool -v -importkeystore -srckeystore server.jks -srcstoretype jks -srcstorepass clientpassword -destkeystore server.pfx -deststoretype pkcs12 -deststorepass clientpassword -destkeypass 12345678
利用pfx导出key,密码还是上面查到的密码
openssl pkcs12 -in server.pfx -nocerts -nodes -out server.key
再利用key和证书生成p12证书,可以导入burp的那种,密码是我们上面设置的12345678。
openssl pkcs12 -export -clcerts -in client-cert.cer -inkey client-key.key -out client.p12
当没有配置证书的时候,抓包显示Communication error。配置进行这个p12。密码为12345678
再次访问即可成功。
frida(2)
如果能获取证书,但是需要查找密码,而又懒得去解包或者不好脱壳,可以尝试查密码的frida脚本。
使用上面的python2脚本来调用。
python .\frida_attach.py .\tracer_keystore.js httpstest
点击触发功能,会显示如下
由于可以解包获取其中的p12证书,所以直接导入证书和密码到burp即可。