各位观众老爷们,大家好!我是今天的主讲人,一个在代码堆里摸爬滚打多年的老码农。今天咱们来聊点刺激的——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,生活愉快!咱们下次再见!