Android 平台 HTTPS 抓包全方案
配置 networkSecurityConfig
如果我们想抓自己的 App,只需要在 AndroidManifest 中配置 networkSecurityConfig 即可:
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
<application android:networkSecurityConfig="@xml/network_security_config"
... >
...
</application>
</manifest>
<?xml version="1.0" encodin
g="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
这样即表示,App 信任用户 CA 证书,让系统对用户 CA 证书的校验给予通过。更多相关信息,详见
Network security configuration:
https://developer.android.com/training/articles/security-config
调低 targetSdkVersion < 24
如果想抓一个 App 的包,可以找个历史版本,只需要其 targetSdkVersion < 24 即可。然而,随着 GooglePlay 开始限制 targetSdkVersion,现在要求其必须>=26,2019 年 8 月 1 日后必须>=28,国内应用市场也开始逐步响应这种限制。绝大多数 App 的 targetSdkVersion 都将大于 24 了,也就意味着抓 HTTPS 的包越来越难操作了。
平行空间抓包
如果我们希望抓 targetSdkVersion >= 24 的应用的包,那又该怎么办呢?我们可以使用平行空间或者 VirtualApp 来曲线救国。平行空间和 VirtualApp 这种多开应用可以作为宿主系统来运行其它应用,如果平行空间和 VirtualApp 的 targetSdkVersion < 24,那么问题也就解决了。
在此,我推荐使用平行空间,相比部分开源的 VirtualApp,平行空间运行得更加稳定。但必须注意平行空间的版本 4.0.8625 以下才是 targetSdkVersion < 24,别安装错了。当然,HttpCanary 的设置中是可以直接安装平行空间的。
安装到系统 CA 证书目录
对于 Root 的机器,这是最完美最佳的解决方案。如果把 CA 证书安装到系统 CA 证书目录中,那这个假 CA 证书就是真正洗白了,不是真的也是真的了。由于系统 CA 证书格式都是特殊的.0 格式,我们必须将抓包工具内置的 CA 证书以这种格式导出,HttpCanary 直接提供了这种导出选项。
操作路径:设置 -> SSL 证书设置 -> 导出 HttpCanary 根证书 -> System Trusted(.0)。
PS. 很不幸的 HttpCanary v2.8.0 前导出的证书名称可能不正确,建议升级到 v2.8.0 以上版本操作。
导出.0 格式的证书后,可以使用 MT 管理器将.0 文件复制到/etc/security/cacerts/目录下,或者通过 adb remount 然后 push 也可(这里稍微提一下,别在 sdcard 里找这个目录)。
/ Firefox 证书安装 /
火狐浏览器 Firefox 自行搞了一套 CA 证书管理,无论是系统 CA 证书还是用户 CA 证书,Firefox 通通都不认可。这种情况,我们需要将 CA 证书通过特殊方式导入到 Firefox 中,否则 Firefox 浏览网页就无法工作了。
HttpCanary v2.8.0 版本提供了 Firefox 证书导入选项。在设置 -> SSL 证书设置 -> 添加 HttpCanary 根证书至 Firefox 中:
点击右上角复制按钮将 url 复制到粘贴板,然后保持此页面不动,打开 Firefox 粘贴输入复制的 url。
出现下载证书弹框后,一定要手动勾上:信任用来标志网站和信任用来标志电子邮件用户。然后确定即可。
/ 公钥证书固定 /
证书固定(Certificate Pinning)是指 Client 端内置 Server 端真正的公钥证书。在 HTTPS 请求时,Server 端发给客户端的公钥证书必须与 Client 端内置的公钥证书一致,请求才会成功。
在这种情况下,由于 MITM Server 创建的公钥证书和 Client 端内置的公钥证书不一致,MITM Server 就无法伪装成真正的 Server 了。这时,抓包就表现为 App 网络错误。已知的知名应用,比如饿了么,就采用了证书固定。
另外,有些服务器采用的自签证书(证书不是由真正 CA 发行商签发的),这种情况 App 请求时必须使用证书固定。
证书固定的一般做法是,将公钥证书(.crt 或者.cer 等格式)内置到 App 中,然后创建 TrustManager 时将公钥证书加进去。很多应用还会将内置的公钥证书伪装起来或者加密,防止逆向提取,比如饿了么就伪装成了 png,当然对公钥证书伪装或者加密没什么太大必要,纯粹自欺欺人罢了。
证书固定对抓包是个非常麻烦的阻碍,不过我们总是有办法绕过的,就是麻烦了点。
JustTrustMe 破解证书固定
Xposed 和 Magisk 都有相应的模块,用来破解证书固定,实现正常抓包。破解的原理大致是,Hook 创建 SSLContext 等涉及 TrustManager 相关的方法,将固定的证书移除。
基于 VirtualApp 的 Hook 机制破解证书固定
Xposed 和 Magisk 需要刷机等特殊处理,但是如果不想刷机折腾,我们还可以在 VirtualApp 中加入 Hook 代码,然后利用 VirtualApp 打开目标应用进行抓包。当然,有开发者已经实现了相关的功能。详见:
案例 1:
案例 2:
不过,这里 CertUnpinning 插件的代码有点问题,要改改。
导入真正的公钥证书和私钥
如果 Client 固定了公钥证书,那么 MITM Server 必须持有真正的公钥证书和匹配的私钥。如果开发者具有真正服务端的公钥证书和私钥,(比如百度的公钥证书和私钥百度的后端开发肯定有),如果真有的话,可以将其导入 HttpCanary 中,也可以完成正常抓包。
在设置 -> SSL 证书设置 -> 管理 SSL 导入证书 中,切换到服务端,然后导入公钥证书+私钥,支持.p12 和.bks 格式文件。
/ 双向认证 /
SSL/TLS 协议提供了双向认证的功能,即除了 Client 需要校验 Server 的真实性,Server 也需要校验 Client 的真实性。这种情况,一般比较少,但是还是有部分应用是开启了双向认证的。比如匿名社交应用 Soul 部分接口就使用了双向认证。使用了双向认证的 HTTPS 请求,同样无法直接抓包。
关于双向认证的原理
首先,双向认证需要 Server 支持,Client 必须内置一套公钥证书 + 私钥。在 SSL/TLS 握手过程中,Server 端会向 Client 端请求证书,Client 端必须将内置的公钥证书发给 Server,Server 验证公钥证书的真实性。
注意,这里的内置的公钥证书有区别于前面第 5 点的公钥证书固定,双向认证内置的公钥证书+私钥是额外的一套,不同于证书固定内置的公钥证书。
如果一个 Client 既使用证书固定,又使用双向认证,那么 Client 端应该内置一套公钥证书 + 一套公钥证书和私钥。第一套与 Server 端的公钥证书相同,用于 Client 端系统校验与 Server 发来的证书是否相同,即证书固定;第二套 SSL/TLS 握手时公钥证书发给 Server 端,Server 端进行签名校验,即双向认证。
用于双向认证的公钥证书和私钥代表了 Client 端身份,所以其是隐秘的,一般都是用.p12 或者.bks 文件+密钥进行存放。由于是内置在 Client 中,存储的密钥一般也是写死在 Client 代码中,有些 App 为了防反编译会将密钥写到 so 库中,比如 S 匿名社交 App,但是只要存在于 Client 端中都是有办法提取出来的。
双向认证抓包
这里以 S 匿名社交 App 为例,讲解下如何抓取使用了双向认证的 App 的 HTTPS 包。如果服务器使用了 Nginx 且开启了双向认证,抓包时会出现 400 Bad Request 的错误,如下:
有些服务器可能不会返回 404,直接请求失败。接下来看,如何使用 HttpCanary 配置双向认证抓包。
首先,解压 APK,提取出.p12 或者.bks 文件,二进制的文件一般存放都在 raw 或者 assets 目录。
将 client.p12 文件导入手机,然后在 HttpCanary 的设置 -> SSL 证书设置 -> 管理 SSL 导入证书中,切换到客户端(因为需要配给 MITM Client),然后导入.p12 文件。
由于双向认证的公钥证书和私钥是受密钥保护的,所以需要输入密码:
一般通过逆向可以从 APK 中提取出密钥,具体操作这里略过。输入密钥后,需要输入映射域名,这里使用通配符*映射所有相关域名:
导入完成后如下:
可以点进证书详情查看细节,这个 client.p12 文件包含公钥证书和私钥,是用于双向认证的。
配置完成后,重新进行抓包,看看效果。
可以看到,之前 400 Bad Request 的两个要求双向认证的请求成功了!
/ SSL 重协商 /
有些服务器可能会开启 SSL 重协商,即 SSL/TLS 握手成功后发送请求时服务器会要求重新握手。这种情况一般比较少,但是也不排除,已知的应用比如 10000 社区 就使用了 SSL 重协商。
由于 Android 系统对 SSL 重协商是有限支持,所以部分系统版本抓包会失败,表现为网络异常。在 Android 8.1 以下,SslSocket 是完全支持 SSL 重协商的,但是 SSLEngine 却是不支持 SSL 重协商的,而 HttpCanary 解析 SSL/TLS 使用的是 SSLEngine。在 Android 8.1 及以上,SSLEngine 和 SslSocket 统一了实现,故是支持 SSL 重协商的。
评论