现在测试过程中有没有感觉遇到能直接抓包的APP,简直要拜菩萨,经常测试APP的心路历程是,欸,我的证书怎么又出问题了,重新导证书后,欸,怎么还是抓不到MMP…。
为什么https的网站使用伪证书可以抓到,而在app里面同样的方法就抓不到?
- 浏览器允许用户忽略证书告警;
- 浏览器允许用户导入证书到证书信任区,从而伪造证书;
也就是我们常用的burpsuite导证书操作,所以网站可以利用这种方式伪造证书中间人攻击;
而APP不行的原因在于SSLPinning(证书绑定),也就是客户端在收到服务器的证书后,对该证书进行强校验,验证该证书是不是客户端承认的证书,如果不是,则直接断开连接,而且客户端承认的证书会在应用发布的时候打包进去,这个时候是不是看起来就没什么办法了呢?
先来看一下实现的代码。
主要实现函数checkServerTrusted();分别对服务端是否有证书,和证书是否正确进行了判断,如果有问题则抛出异常,其实这个函数在每个APP都存在,为什么有些能直接抓取数据包,而有些不能呢,可以抓的是因为在开发的时候开发人员直接将此函数返回空值,不做任何处理导致;
来到这篇文章的重点,怎么绕过这个APP证书绑定呢?
第一种方式
当然是我们的hook大法啦,利用Xposed + JustTruestMe来突破SSL pinning;这种方式网上很多介绍,也就是在程序运行时修改内存中指定的参数,达到修改程序的目的,从而绕过认证;
使用前提是需要root的测试设备,这里就不多说Xposed的使用了。
https://repo.xposed.info/module/de.robv.android.xposed.installer
JustTruestMe插件下载地址:
https://github.com/Fuzion24/JustTrustMe/releases/tag/v.2
第二种方式
自己逆向修改源码;感觉对比起第一种要蠢很多;但是最近一次项目自己就这么做了。
因为刚好遇到有没有加壳的,就拿来练手了;
直接用AndroidKiller反编译,工程搜索checkServerTrusted;应用比较简单,可以看到搜索结果,第一个就是我们要找的checkServerTrusted()函数,第二个只是一个字符型,第三个是函数调用;
打开第一个函数,进去看;
.method public checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
.locals 2
.annotation system Ldalvik/annotation/Throws;
value = {
Ljava/security/cert/CertificateException;
}
.end annotation
.prologue
.line 1396
if-nez p1, :cond_0
.line 1397
new-instance v0, Ljava/lang/IllegalArgumentException;
const-string v1, "there were no certificates."
invoke-direct {v0, v1}, Ljava/lang/IllegalArgumentException;-><init>(Ljava/lang/String;)V
throw v0
.line 1399
:cond_0
array-length v0, p1
const/4 v1, 0x1
if-ne v0, v1, :cond_1
.line 1401
const/4 v0, 0x0
aget-object v0, p1, v0
invoke-virtual {v0}, Ljava/security/cert/X509Certificate;->checkValidity()V
.line 1407
:goto_0
return-void
.line 1402
:cond_1
iget-object v0, p0, Lcom/mob/tools/network/NetworkHelper$SimpleX509TrustManager;->standardTrustManager:Ljavax/net/ssl/X509TrustManager;
if-eqz v0, :cond_2
.line 1403
iget-object v0, p0, Lcom/mob/tools/network/NetworkHelper$SimpleX509TrustManager;->standardTrustManager:Ljavax/net/ssl/X509TrustManager;
invoke-interface {v0, p1, p2}, Ljavax/net/ssl/X509TrustManager;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V
goto :goto_0
.line 1405
:cond_2
new-instance v0, Ljava/security/cert/CertificateException;
const-string v1, "there were one more certificates but no trust manager found."
invoke-direct {v0, v1}, Ljava/security/cert/CertificateException;-><init>(Ljava/lang/String;)V
throw v0
.end method
简单解释下,可以看到函数中有多处条件跳转,如:cond_0,:cond_1,:cond_2和:goto_0;:cond_处都有对应的处理,我们不用管做啥处理,可以发现只有:goto_0是return-void,不做任何处理,而我们也是要此函数不做任何处理,这样就能达到我们的目的,所以最简单的修改就是将所有的条件跳转位置都改成:goto_0,这样就能导致此函数不管是什么结果都返回空值,当然还有其它的修改方式。
重新编译,签名,安装成功后,就可以抓到对应的数据包了;当然这个也可以直接用Xposed + JustTruestMe的方式来抓取数据包,但是需要root环境的测试设备,而改数据包的好处就是不需要。其中的原理都是一样的,当然能够抓取数据包的方式肯定不止这两种。
现在抓不到数据包的原因又多了,还有双向证书认证的,突破方式是在app中找本地保存的证书文件,导入到抓包工具即可,但是有的有密码认证,需要额外反编译app,找到其中的密码。
还有抓不到数据包的原因是app走的不是http、https,走tcp、socket、udp、mqtt、蓝牙等其他协议。