HTTPS抓包进阶:SSL Pinning绕过原理、证书双向验证破解、Android 7.0+与iOS 14+系统级证书信任机制

说实话,很多搞了几年安全测试的朋友,一遇到HTTPS抓包就头疼。特别是现在App越来越精,你装个代理它就罢工,你挂个证书它就报错。嗯,这背后其实就是SSL Pinning和证书验证在作怪。今天咱们就把这几个硬骨头啃下来。

核心认知:HTTPS抓包的难点从来不在协议本身,而在客户端对证书的校验逻辑。绕过校验,你就拿到了明文。

2.1 SSL Pinning——App的“证书指纹锁”

SSL Pinning,说白了就是App在代码里写死了它信任的证书或公钥。你抓包工具自己签发的证书,App根本不认。我在项目中遇到过好几次,明明Charles/Fiddler都配好了,App就是死活不走代理,一查日志,全是证书校验失败。

2.1.1 两种常见的Pinning方式

类型 绑定对象 绕过难度 典型场景
Certificate Pinning 完整的服务端证书 中等 证书不常变动的金融类App
Public Key Pinning 证书中的公钥(或公钥哈希) 较高 证书轮换频繁的大型互联网App

为什么公钥Pinning更难?因为证书可以换,但公钥对(RSA或ECC)通常几年才换一次。你想想看,就算你伪造了证书,公钥对不上,App照样拒绝连接。

2.1.2 绕过Pinning的常规思路

绕过Pinning,本质上就两条路:要么Hook掉校验函数,要么把抓包工具的证书塞进App信任的根证书列表里。我个人习惯先用Hook方案,因为快,不用改App包体。

  • Hook方案(推荐):使用Frida或Xposed,Hook掉SSLContext.init()、TrustManager.checkServerTrusted()等方法,让校验永远返回成功。
  • 重打包方案:反编译App,在res/xml/network_security_config.xml里添加用户证书信任,或者直接替换App内置的Pinning公钥。
  • JustTrustMe/SSLUnpinning:现成的Xposed模块,一键绕过常见Pinning库(OkHttp、HttpClient等)。

我的经验:遇到OkHttp 3.x以上版本,直接Hook CertificatePinner.check()最稳。Frida脚本大概10行代码就能搞定,比折腾重打包快得多。

2.2 证书双向验证——服务器也要查你的“身份证”

单向验证是客户端验服务端,双向验证是服务端也要验客户端。说白了,服务器会要求你出示一张它认可的客户端证书,拿不出来就断开连接。我在做某银行App测试时就遇到过,代理配好之后,连接直接RST,抓包工具连Hello都发不出去。

2.2.1 双向验证的握手流程

  1. 客户端发起ClientHello
  2. 服务端返回ServerHello + 服务端证书 + CertificateRequest(要求客户端证书)
  3. 客户端发送ClientCertificate + ClientCertificateVerify
  4. 服务端验证客户端证书的签发者、有效期、是否吊销
  5. 验证通过,继续密钥协商;验证失败,直接Alert并断开

为什么会这样?因为服务端在握手阶段就要求客户端证书,你抓包工具作为中间人,拿不出合法的客户端证书,自然就被拒了。

2.2.2 破解双向验证的两种方法

方法 适用场景 操作要点
提取客户端证书 App内嵌了.p12或.bks证书文件 反编译App,从assets或raw目录提取证书文件,导入抓包工具
Hook证书加载逻辑 证书通过代码动态加载或由服务器下发 Hook KeyManagerFactory.init(),替换或导出证书

注意:提取到的客户端证书通常有密码保护。密码可能在App代码里硬编码,也可能通过算法生成。我曾经遇到过密码是设备IMEI+时间戳拼接后MD5的情况,折腾了一下午才跑出来。

2.3 Android 7.0+与iOS 14+的系统级证书信任机制

这两个版本是个分水岭。Android 7.0开始,系统默认不再信任用户安装的CA证书;iOS 14开始,即使安装了描述文件,系统也不自动信任。很多朋友在这上面栽过跟头——证书装了,代理配了,App就是不走。

2.3.1 Android 7.0+:Network Security Configuration

Android从7.0(API 24)引入了网络安全配置。默认情况下,只有系统预装证书才被信任。用户安装的Charles/Fiddler证书,对targetSdkVersion >= 24的App来说,等于废纸。

  • debug模式:在AndroidManifest.xml的application标签加android:networkSecurityConfig="@xml/network_security_config",然后在xml文件里配置信任用户证书。
  • release模式:要么重打包修改network_security_config.xml,要么用Frida绕过。我个人习惯用Frida,因为不用改包,省去签名校验的麻烦。

避坑指南:我曾经在重打包时忘了处理签名校验,结果App启动就闪退。后来学乖了,先用Frida跑一下,确认是证书问题还是签名问题,再决定要不要重打包。

2.3.2 iOS 14+:证书信任开关

iOS 14之后,安装描述文件只是第一步。你还需要去“设置 → 通用 → 关于本机 → 证书信任设置”里,手动打开抓包工具证书的开关。这一步很多人会漏掉。

而且iOS对证书的校验更严格:

  • 证书有效期必须合法
  • 证书链必须完整
  • 部分App会额外校验证书的SHA-256指纹

嗯,这里要注意:iOS上如果App使用了NSURLSession并设置了serverTrust,即使系统信任了你的证书,App照样可以拒绝。这时候还是得上Frida或SSLKillSwitch2。

2.4 知识体系总览

下面这张图把HTTPS抓包进阶的核心逻辑串起来了。你可以把它当作排查问题的路线图:遇到抓包失败,先定位是哪个环节出了问题。

HTTPS抓包进阶知识体系 HTTPS抓包进阶核心逻辑 HTTPS抓包失败 SSL Pinning拦截 双向证书验证 Hook校验函数 重打包改配置 提取客户端证书 Hook证书加载 系统级证书信任机制 Android 7.0+ NSC iOS 14+ 信任开关 Frida Hook绕过 重打包修改 安装描述文件 手动信任证书 提示:优先尝试Hook方案(Frida),无需改包,效率最高 重打包方案需处理签名校验,适合Hook方案无效的场景

2.5 实战建议与避坑清单

最后,我把自己这些年踩过的坑整理成了一份清单。你遇到问题的时候,可以对照着排查。

  • 先确认App是否使用了Pinning:挂上代理,看报错是“证书不信任”还是“连接被拒绝”。前者是系统级问题,后者多半是Pinning。
  • Frida脚本不要贪多:一次只Hook一个目标函数,Hook多了容易冲突。我遇到过同时Hook了checkServerTrusted和CertificatePinner.check,结果App直接崩溃。
  • iOS证书信任开关别忘了:装了描述文件不等于信任了。去设置里手动打开,这一步漏掉的人最多。
  • 双向验证优先找客户端证书:反编译App,搜.p12、.bks、.pfx后缀,或者搜KeyStore、PKCS12等关键词。找到了就成功了一半。
  • Android 7.0+重打包后要处理签名:用apktool重打包后,记得用uber-apk-signer重新签名。否则App安装不上,或者运行时被检测到签名变更。

重要提醒:绕过SSL Pinning和证书验证,仅限用于合法授权的安全测试、漏洞挖掘或自己的设备调试。未经授权对他人App进行抓包和破解,可能违反计算机信息系统安全相关法律法规。