各位观众老爷们,大家好!我是今天的主讲人,一个在代码堆里摸爬滚打多年的老码农。今天咱们来聊点刺激的——HTTPS SSL Pinning的绕过。
首先声明,咱们今天讨论的是技术,目的是了解安全机制,不是教唆大家去干坏事!学技术是为了更好的保护自己,可别用来搞破坏啊!
SSL Pinning 是个啥?
简单来说,SSL Pinning就像给你的App加了个“白名单”。它会把服务器的SSL证书(或者证书的公钥、哈希)直接“钉”在你的App代码里。这样,App只会信任与这个“白名单”里的证书匹配的服务器。
为啥要这么做呢?因为普通的HTTPS连接,App会信任任何经过可信CA(证书颁发机构)签发的证书。但CA也可能被攻破,或者被某些邪恶势力控制,从而签发假的证书。有了Pinning,即使CA被攻破,攻击者也无法用假证书欺骗你的App。
绕过Pinning,为啥这么难?
Pinning把信任锚点从整个CA体系缩小到了特定的证书,大大提高了安全性。想要绕过它,意味着你需要找到方法让App信任一个非“白名单”里的证书,或者直接修改App的行为,让它忽略Pinning的检查。
那么,如何“优雅”地绕过它呢?(注意,是“优雅”,不是“暴力破解”)
绕过SSL Pinning的方法有很多,咱们可以从不同的角度入手:
一、修改App(难度系数:看情况)
这是最直接,也是最“脏”的方法。但如果能拿到App的源码,或者能够反编译App并修改它的代码,那就可以为所欲为了。
-
反编译App:
首先,我们需要拿到App的安装包(比如.apk文件,如果是iOS,就是.ipa文件)。然后,使用反编译工具(比如
apktool
、dex2jar
+jd-gui
,或者Hopper Disassembler
、IDA Pro
)把App的代码反编译成可读的Java代码(或者汇编代码)。# 反编译apk apktool d your_app.apk
反编译出来的代码通常是混淆过的,阅读起来比较困难,但我们可以通过一些技巧来找到关键的代码。
-
定位Pinning代码:
Pinning通常会在网络请求的代码中实现。我们可以搜索一些关键词,比如
SSLContext
、TrustManager
、HostnameVerifier
、Certificate
、pinning
等,来找到相关的代码。在Android中,OkHttp、Retrofit、Volley等网络库都可能被使用,Pinning的实现方式也会有所不同。
-
OkHttp: OkHttp使用
CertificatePinner
类来实现Pinning。// 示例代码(可能混淆过) CertificatePinner certificatePinner = new CertificatePinner.Builder() .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") .build(); OkHttpClient client = new OkHttpClient.Builder() .certificatePinner(certificatePinner) .build();
-
TrustManager: 有些App会自己实现
TrustManager
来验证证书。// 示例代码(可能混淆过) TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { // Pinning 逻辑 // ... } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
-
-
修改Pinning代码:
找到Pinning的代码后,就可以开始修改了。
-
禁用Pinning: 最简单粗暴的方法就是直接注释掉Pinning相关的代码,或者修改条件判断,让Pinning永远不生效。
-
添加自定义证书: 可以修改代码,让App信任我们提供的证书,或者信任所有证书。
// 示例代码(修改TrustManager,信任所有证书) TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { // 信任所有证书 } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); OkHttpClient client = new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager)trustAllCerts[0]) .hostnameVerifier((hostname, session) -> true) // 信任所有hostname .build();
-
-
重新打包App:
修改完代码后,需要重新编译并打包App。
# 重新编译apk apktool b your_app # 签名apk (需要keystore) jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore your_keystore.jks your_app.apk your_alias
重新打包的App需要签名才能安装到设备上。
二、修改操作系统(难度系数:高)
这种方法需要在操作系统层面修改证书信任链,让操作系统信任我们提供的证书。这种方法比较通用,可以绕过大多数App的Pinning。
-
安装自定义CA证书:
我们可以自己创建一个CA证书,然后把这个证书安装到操作系统的受信任根证书列表中。这样,任何由这个CA签发的证书都会被操作系统信任。
- Android: 可以通过Settings -> Security -> Install from SD card来安装证书。
- iOS: 可以通过AirDrop或者邮件把证书发送到设备上,然后按照提示安装。
-
使用Frida Hook:
Frida是一个动态代码插桩工具,可以让我们在运行时修改App的行为。我们可以使用Frida来Hook SSL/TLS相关的函数,修改证书验证的逻辑。
# Frida 脚本 (Python) import frida import sys def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) def main(): package_name = "your.app.package.name" # 替换成你的App包名 try: device = frida.get_usb_device(timeout=10) session = device.attach(package_name) except frida.TimedOutError: print("[-] Could not connect to the device. Is USB debugging enabled?") sys.exit(1) except frida.ProcessNotFoundError: print("[-] The app is not running. Please start it.") sys.exit(1) script = session.create_script(""" // Hook X509TrustManager.checkServerTrusted Java.perform(function () { var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager'); X509TrustManager.checkServerTrusted.implementation = function (chain, authType) { console.log('[+] Bypassing TrustManager checkServerTrusted()'); return; // Trust everything! }; // Hook CertificatePinner.check var CertificatePinner = Java.use('okhttp3.CertificatePinner'); CertificatePinner.check.implementation = function (hostname, peerCertificates) { console.log('[+] Bypassing CertificatePinner check()'); return; // Bypass CertificatePinner! }; //Hook HostnameVerifier.verify var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier'); HostnameVerifier.verify.implementation = function(hostname, session){ console.log('[+] Bypassing HostnameVerifier verify()'); return true; // Trust all hostnames! } }); """) script.on('message', on_message) script.load() sys.stdin.read() if __name__ == '__main__': main()
这个Frida脚本会Hook
X509TrustManager.checkServerTrusted()
、CertificatePinner.check()
和HostnameVerifier.verify()
函数,让它们直接返回,从而绕过证书验证。
三、修改网络流量(难度系数:中)
这种方法通过拦截和修改App的网络流量,来绕过Pinning。
-
使用代理工具:
我们可以使用一些代理工具(比如Charles、Burp Suite、Fiddler)来拦截App的网络流量。这些工具可以让我们查看App发送和接收的数据,甚至可以修改这些数据。
- 配置代理: 需要在App或者操作系统中配置代理,让App的网络流量经过代理工具。
- 安装代理证书: 代理工具会生成一个自签名证书,需要把这个证书安装到App或者操作系统的受信任根证书列表中。
-
修改SNI:
SNI(Server Name Indication)是TLS协议的一个扩展,用于在客户端与服务器建立连接时,告诉服务器客户端要访问的域名。有些App会根据SNI来判断是否需要进行Pinning。
我们可以修改SNI,让App认为我们访问的是一个不需要Pinning的域名,从而绕过Pinning。
- 使用Burp Suite: 在Burp Suite中,可以配置拦截规则,修改SNI的值。
-
中间人攻击(MITM):
我们可以使用代理工具,进行中间人攻击。
- 拦截HTTPS流量: 代理工具会拦截App的HTTPS流量,然后使用自己的证书与App建立连接。
- 修改流量: 可以修改App发送和接收的数据,甚至可以伪造服务器的响应。
各种方法的对比:
方法 | 优点 | 缺点 | 难度系数 |
---|---|---|---|
修改App | 最直接,效果最好 | 需要反编译App,代码混淆,重新打包,可能破坏App的功能,容易被检测 | 看情况 |
修改操作系统 | 通用性强,可以绕过大多数App的Pinning | 需要root权限或者越狱,风险较高,可能影响系统稳定性 | 高 |
修改网络流量 | 不需要修改App或者操作系统,相对安全 | 需要配置代理,只能拦截和修改网络流量,可能被检测 | 中 |
一些注意事项:
- 代码混淆: 大多数App都会对代码进行混淆,增加了反编译和修改的难度。
- 反调试: 一些App会加入反调试机制,防止被调试和修改。
- 完整性校验: 一些App会对自身进行完整性校验,防止被篡改。
- Pinning方式: Pinning的实现方式有很多种,需要根据具体情况选择合适的绕过方法。
- 安全检测: 一些App会检测是否存在代理或者Hook,如果检测到,可能会阻止App运行或者限制某些功能。
总结:
SSL Pinning是一种有效的安全机制,可以防止中间人攻击。但是,没有绝对的安全,只要有足够的时间和资源,任何安全机制都有可能被绕过。
绕过SSL Pinning的方法有很多,每种方法都有其优缺点。选择哪种方法取决于具体情况,比如App的复杂程度、Pinning的实现方式、以及我们掌握的工具和技术。
希望今天的讲座能让大家对SSL Pinning和绕过方法有更深入的了解。记住,技术是把双刃剑,要用它来保护自己,而不是伤害别人!
最后,祝大家代码无Bug,生活愉快!咱们下次再见!