1.前言在日常渗透过程中我们经常会遇到瓶颈无处下手,这个时候如果攻击者从APP进行突破,往往会有很多惊喜。但是目前市场上的APP都会为防止别人恶意盗取和恶意篡改进行一些保护措施,比如模拟器检测、root检测、APK加固、代码混淆、代码反调试、反脱壳、签名校验等等对抗机制。 而测试人员对APP进行渗透的首步操作通常就是上burp或者Charles这类抓包工具进行抓包,查看请求记录里的域名及链接地址是否可以进一步利用,但是如果遇到一些APP出现证书报错或者抓不到包的情况该怎么办,读过本篇文章之后,相信你会拥有一些新的解决方案和思考。 2.数字证书我们都知道http协议传输的是明文信息,是可以直接捕获的,从而造成了数据泄露。为了防止中间人的拦截,出现了HTTPS加密机制。在HTTPS中,使用了证书+数字签名解决了这个问题。 此篇的重点在于如何应对APP的抓包对抗。 总结的HTTPS加密机制如下: - 数字签名是发送方的明文经历了两次加密得到的两个东西组成,一个是hash ,一个是经过私钥加密。
- 数字证书就是明文+数字签名。但是数字证书中的内容远不止这俩,还包括了权威机构的信息,服务器的域名,最重要的是有签名的计算方法,不然用公钥进行解密之后的hash,如何与加密明文进行对比呢,还有证书中还包括公钥,公钥用于发放给请求证书的客户端。
- HTTPS就是使用SSL/TLS协议进行加密传输,让客户端拿到服务器的公钥,然后客户端随机生成一个对称加密的秘钥,使用公钥加密,传输给服务端,后续的所有信息都通过该对称秘钥进行加密解密,完成整个HTTPS的流程。
3.https抓包【一一帮助安全学习,所有资源关注我,私信回复“资料”获取一一】 ①网络安全学习路线 ②20份渗透测试电子书 ③安全攻防357页笔记 ④50份安全攻防面试指南 ⑤安全红队渗透工具包 ⑥网络安全必备书籍 ⑦100个漏洞实战案例 ⑧安全大厂内部教程
导入用户证书在第一次使用burp时,都会有这么一步,将burp的证书导出,添加进浏览器 【受信任的根证书颁发机构】中去,这样就会信任burp发来的请求包,也就可以请求数据进行修改。我们对APP抓包,也同样要将burp证书安装到系统证书中去,一般从【SD卡安装】的证书会存放在用户信任的凭据下 但是,在Android 7.0以前,应用默认会信任系统证书和用户证书,Android 7.0开始,默认只信任系统证书。
所以如果你的手机是处于Android7.0以上版本的话,并且在没有绑定SSL证书的情况下,也会抓不到包,从安卓开发的角度可以很清楚的看到这一点。
下图是我将burp证书安装到Android7.1.2的用户证书下,使用okhttp对https://ttt.com进行请求的结果。由于ttt.com的SSL证书是自签名证书,而自签名证书是不被系统默认信任的,所以需要先将ttt.com的自签名证书添加到系统证书中才可以访问。 自签名证书的生成如下图所示: - 系统证书路径:/system/etc/security/cacerts/
- 用户证书路径:/data/misc/user/0/cacerts-added/
移动到系统根证书路径的方法: 1、导出burp.der 2、使用openssl更改证书格式,先将burp证书的der格式转成pem,再获取证书的hash openssl x509 -inform DER -in burp.der -out burp.pem
openssl x509 -inform PEM -subject_hash_old -in burp.pem
3.移动到系统根证书目录路径下 Android根证书目录都是以pem证书的hash值+.0格式,所以要将刚才生成的pem改名为xxxx.0 mv burp.pem a5ba575.0
由于系统读写权限问题,不一定能直接上传到system目录 adb push 9a5ba575.0 /sdcard
adb shell
mount -o remount,rw /system
cp /sdcard/9a5ba575.0 /system/etc/security/cacerts/
chmod 644 /system/etc/security/cacerts/9a5ba575.0
移动完成之后,再打开【设置】-【安全】-【信任的凭据】验证一下 这时可以在Android7.0以上版本正常访问https://ttt.com了,其他抓包工具同理即可。 证书有效期过长还有一种情况是,导入到系统证书仍抓不到包,并且浏览器会报 NET::ERR_CERT_VALIDITY_TOO_LONG错误。 原因是chrome从2018年开始只信任有效期少于825天(27个月)的证书,而burp证书有效期过长。 解决方案是自己做一个低于27个月的root证书导入burp,再通过burp重新导出证书并放入到系统证书路径下。 openssl genrsa -out key.pem 3072 -nodes
openssl req -new -x509 -key key.pem -sha256 -config openssl.cnf -out cert.pem -days 730 -subj "/C=JP/ST=/L=/O=m4bln/CN=MY CA"
openssl pkcs12 -export -inkey key.pem -in cert.pem -out cert_and_key.pfx
把cert_and_key.pfx导入burp
目前还没遇到过这种情况,但是如果遇到了这种问题要知道怎么解决。 以上两种方法都是仅依靠了系统校验证书的方式进行抓包,APP在整个请求HTTPS的请求过程时还并未进行证书校验,和在普通的浏览器中访问并无区别,只是要将想要被信任的证书放入系统证书路径内。
4.SSLPinning对于像ttt.com这种自签名的免费证书,不需要CA权威认证的证书,大多数APP开发商都会使用。那么如果在安卓开发的过程中,将证书的验证逻辑放在APP内部,与系统和浏览器毫无相关,这时再想将burp证书导入系统受信任路径下也于事无补了。 APP自己校验证书,分为两种,一种是将验证逻辑也在代码中,一种是写在安卓7.0之后才有的network-security-config中。 验证是方式也有两种,一种是验证证书公钥的hash值,一种是直接验证证书的公钥文件。 这种通过APP自身的验证方式就叫做证书绑定(也叫Certificate Pinning或SSL Pinning)。 那么如何去判断一个APP是否使用了证书绑定呢?首先拿到apk文件,用apktool工具进行反编译,查看敏感文件 apktool d -s <file.apk> -o <outdir>
开发人员经常会将网络配置的相关文件保存到指定位置,如下图就指定在了xml目录下。 所以在反编译后的res/xml目录下会有一个 network_security_config.xml文件,打开看到标签,说明使用了证书绑定机制。 在配置文件中检验的两种方法<network-security-config xmlns:tools="http://schemas.android.com/tools">
<base-config cleartextTrafficPermitted="true"
tools:ignore="InsecureBaseConfiguration" />
<domain-config>
<domain includeSubdomains="true">www.ttt.com</domain>
<trust-anchors>
<certificates src="@raw/ttt"/>
</trust-anchors>
</domain-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">ttt.com</domain>
<pin-set expiration="2099-01-01"
tools:ignore="MissingBackupPin">
<pin digest="SHA-256">7VMdvZE3PGbxb0Pgf1PlCp+MI8KZ2ZC5psM8TIylNDA=</pin>
</pin-set>
<trust-anchors>
<certificates src="@raw/ttt"/>
</trust-anchors>
</domain-config>
</network-security-config>
这两种校验机制出现一种即可,从代码中可以看出,ttt.com就是安卓自己要校验绑定的域名。 如果只是在这个文件进行校验,有两种解决方案:一是直接将文件中校验的部分或注释掉,再重新打包和签名即可,但是这过程又有些麻烦,并不是上上策,如果遇到了不能重打包的apk就尴尬了。。。二是最常用的也是最好用的frida来hook关键函数进行绕过,后面会讲解。当然有些人会直接在真机或者模拟器上安装xposed模块,但是我个人觉得每次使用都要软重启,可能还会造成卡机,所以感觉还是使用frida最方便。 在代码中检验的两种方法1.利用代码校验证书的公钥hash String hostname = "www.ttt.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(hostname, "sha256/7VMdvZE3PGbxb0Pgf1PlCp+MI8KZ2ZC5psM8TIylNDA=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}).build();
2.利用代码校验证书的公钥证书文件
InputStream openRawResource = getApplicationContext().getResources().openRawResource(R.raw.ttt);
Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(openRawResource);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
|