从原理到实战:一文读懂SSL Pinning及其在代理抓包中的攻防策略

张开发
2026/4/17 18:43:56 15 分钟阅读

分享文章

从原理到实战:一文读懂SSL Pinning及其在代理抓包中的攻防策略
1. HTTPS与SSL握手协议基础当你用手机打开一个银行APP时数据传输安全是首要考虑的问题。这就要提到HTTPS和它的安全基石——SSL/TLS协议。简单来说HTTPS就是在HTTP外面套了层加密外壳而SSL Pinning就是给这个外壳加装的防盗锁。先看个生活场景你去银行开户柜员服务端和你客户端要确认彼此身份。SSL握手就像这个确认过程你出示身份证Client Hello发送随机数和支持的加密算法柜员核对后出示工作证Server Hello返回随机数和选定算法银行展示营业执照Certificate服务端发送公钥证书你填写开户申请表并用柜员提供的笔密封Client Key Exchange双方开始用专属密码交流Session Ticket关键问题出在第3步传统HTTPS验证只检查证书是否由可信机构签发就像只检查营业执照是不是工商局发的却不核对是不是这家银行的分支机构。中间人攻击就是伪造个高仿营业执照而SSL Pinning就是要核对营业执照上的防伪编码。2. SSL Pinning的工作原理SSL Pinning本质上是个证书指纹核对员。普通HTTPS验证就像小区门禁只认物业发的门禁卡而SSL Pinning还会核对卡上的芯片序列号是否在物业备案的名单里。具体实现方式主要有三种证书锁定Certificate Pinning直接存储服务端证书的副本。就像把银行营业执照复印件存在保险箱每次交易拿出来对比。公钥锁定Public Key Pinning只存储证书中的公钥。类似只记录营业执照编号不存整个证件。哈希锁定Hash Pinning存储证书的SHA-256哈希值。相当于只记营业执照的防伪码。Android开发者常用这些实现方案// OkHttp示例证书锁定 CertificatePinner pinner new CertificatePinner.Builder() .add(example.com, sha256/AAAAAAAAAAAAAAAA) .build(); // Android网络配置公钥锁定 network-security-config domain-config domain includeSubdomainstrueexample.com/domain pin-set pin digestSHA-256BBBBBBBBBBBBBBBB/pin /pin-set /domain-config /network-security-config实际项目中我发现金融类APP常采用多层防御第一层OkHttp默认证书校验第二层WebView自定义证书校验第三层Native代码实现证书验证第四层定期更新证书指纹应对证书到期轮换3. 主流SSL Pinning绕过方案面对SSL Pinning这座城墙安全研究人员开发了多种攻城锤。根据对抗强度我把它们分为三个难度等级3.1 新手难度通用Hook工具JustTrustMe就像万能钥匙原理是Hook常见网络库的证书验证方法# Frida脚本示例绕过OkHttp验证 Java.perform(function() { var CertificatePinner Java.use(okhttp3.CertificatePinner); CertificatePinner.check$okhook.implementation function() { console.log(Bypassing SSL Pinning!); return; }; });实测可用工具清单工具名称适用场景优点缺点JustTrustMeXposed环境下的通用解决方案支持多网络库需要RootDroidSSLUnpinningFrida环境下的动态Hook无需修改APP对抗混淆能力弱Objection集成化测试工具命令简单对Native层无效我在测试某电商APP时发现JustTrustMe对OkHttp3有效但遇到自定义WebView时就需要升级方案。3.2 进阶级证书提取与替换当通用工具失效时就需要外科手术式的精准打击。操作流程使用Apktool反编译APKapktool d target.apk -o output_dir在assets/res目录搜索.pem/.cer/.der文件用keytool转换证书格式keytool -importcert -file server.cer -keystore charles.jks在Charles中加载自定义证书最近测试某银行APP时发现他们把证书藏在so库里。解决方案是用IDA Pro动态调试在SSL_CTX_new函数下断点打印出内存中的证书数据。3.3 专家级定制化Hook开发面对大厂的自研网络库需要量体裁衣的解决方案。以某IM应用为例先用frida-trace定位验证函数frida-trace -U -i SSL_* com.example.app分析堆栈找到关键验证点编写定制Hook脚本Interceptor.attach(Module.findExportByName(libcustom.so, ssl_verify_cert), { onLeave: function(retval) { retval.replace(1); // 强制返回验证成功 } });这种方案的难点在于对抗代码混淆。我的经验是结合字符串搜索如certificate、pin等关键词和交叉引用分析逐步缩小目标范围。4. 实战中的疑难问题解决真实环境中会遇到各种意外状况。分享几个踩坑案例4.1 双向认证的破解某政务APP采用双向认证除了常规方案外还需要提取客户端证书通常在assets或代码硬编码转换PKCS12格式openssl pkcs12 -export -in client.pem -inkey client.key -out client.p12在BurpSuite中加载客户端证书遇到证书密码保护时可以尝试Hook KeyStore.load方法获取密码var KeyStore Java.use(java.security.KeyStore); KeyStore.load.overload(java.security.KeyStore$LoadStoreParameter).implementation function(param) { console.log(KeyStore password: param.getProtectionParameter().getPassword()); return this.load(param); };4.2 安卓7的证书信任问题新版Android的网络安全配置变更导致用户安装的证书不被信任。解决方案矩阵方案所需条件操作难度适用场景修改APK的networkSecurityConfig需重打包APK★★★☆☆测试环境移动证书到系统目录需要Root★★★★☆长期使用Hook证书验证链需要Xposed/Frida★★☆☆☆动态测试推荐组合方案先用Frida脚本临时绕过再对测试机做永久性修改# 将用户证书复制到系统目录 cp /data/misc/user/0/cacerts-added/123456.0 /system/etc/security/cacerts/ # 修改权限 chmod 644 /system/etc/security/cacerts/123456.04.3 非代理环境抓包方案当APP完全禁用代理时可以尝试透明代理用iptables转发流量iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination 127.0.0.1:8080VPN抓包使用Packet Capture等工具路由镜像在路由器上做端口镜像最近测试某视频APP时发现他们检测到iptables规则后会主动关闭连接。最终方案是在内核层拦截网络调用// Linux内核模块示例 static struct nf_hook_ops nfho { .hook hook_func, .pf PF_INET, .hooknum NF_INET_PRE_ROUTING, .priority NF_IP_PRI_FIRST };5. 防御方的升级策略作为开发者如何构建更坚固的防御分享几个有效方案动态证书加载从服务器获取证书指纹避免硬编码。示例流程APP启动时向安全接口请求最新指纹使用AES加密存储到私有目录每次网络请求前动态加载代码混淆反射调用// 原始代码 CertificatePinner pinner new CertificatePinner.Builder() .add(example.com, sha256/AAAAAAAAAAAAAAAA) .build(); // 混淆后代码 Class? clazz Class.forName(okhttp3.CertificatePinner); Object builder clazz.getMethod(Builder).invoke(null); Method addMethod builder.getClass().getMethod(add, String.class, String.class); addMethod.invoke(builder, decodeStr(aG9zdC5jb20), decodeStr(c2hhMjU2L0FBQUFBQUE9)); Object pinner builder.getClass().getMethod(build).invoke(builder);环境检测增强检测Xposed/Frida进程校验内存中的证书是否被篡改监控关键函数是否被Hook某支付APP的防御方案值得参考启动时检测调试状态随机延迟加载证书定期校验内存完整性异常行为触发自毁机制6. 工具链与自动化方案提高效率的实战技巧自动化测试脚本# 自动化测试流程示例 def test_ssl_pinning(app): start_frida_server() load_hook_script(bypass_ssl.js) capture start_packet_capture() run_app_flow(app) if capture.has_encrypted_traffic(): return SSL Pinning Active return Vulnerable推荐工具组合静态分析Jadx-GUI GDA动态调试Frida IDA Pro流量分析Wireshark Charles自动化Appium mitmproxy在持续集成环境中可以搭建这样的检测流水线自动解包APK扫描证书动态注入测试脚本流量特征分析生成安全报告某次渗透测试中我使用如下命令快速验证多个APPfor apk in *.apk; do apktool d $apk -o temp/ grep -r CertificatePinner temp/ frida -U -f $(basename $apk .apk) -l bypass.js --no-pause done7. 法律与道德边界技术是把双刃剑需要特别注意只测试授权范围内的应用不保留敏感业务数据发现漏洞后遵循合规披露流程建议的测试流程规范获取书面授权使用专用测试设备测试完成后擦除数据提交详细的报告某次金融APP测试中我们严格遵循这样的流程所有操作在隔离网络进行测试数据使用脱敏样本报告通过加密渠道传送测试完成后销毁虚拟机

更多文章