抓包技术分析及SSL PINNING保护方案

要分析抓包的技术首先要介绍数字证书是什么,一切的抓包手段都是围绕数字证书做文章

数字证书

TLS 握手的作用之一是身份认证,被验证的一方需要提供一个身份证明,在 HTTPS 的世界里,这个身份证明就是 TLS 证书,或者称为 数字证书

JDK 中用 java.security.cert.X509Certificate 来表示一个证书,它继承自抽象类 java.scurity.cert.Certificate,通过 X509Certificate 我们可以获取证书的信息,例如x509Certificate.getSubjectDN().getName()


抓包技术分析及SSL PINNING保护方案_第1张图片
数字证书构成.png

x509Certificate.getSubjectDN().getName()获取到的是证书的Subject's Name的信息即证书拥有者,内容是一组符合 X.500 规范的 DN(Distinguished Name)

CN=*,OU=IT Department,O=China Merchants Bank Co., Ltd,L=Shenzhen,C=CN

这是从我们证书中获取到的信息,DN的属性含义如图所示

抓包技术分析及SSL PINNING保护方案_第2张图片
DN参数

通过命令openssl x509 -in base64.cer –text 查看证书信息,输出如图所示
可以看出,一个 Certificate 由 Data 和 Signature 两部分组成。其中 Data 包含的内容有:
证书版本号:X.509v3
序列号:一个 CA 机构内是唯一的,但不是全局唯一
签名算法:签名的计算公式为RSA(sha256(Data), IssuerPrivateKey)
签发者:DN(Distinguished Name)
有效期:证书的有效期间 [Not Before, Not After]
证书拥有者:也是一个 DN公钥长度一般是 2048bit,1024bit已经被证明不安全
扩展字段:证书所携带的域名信息会配置在 SAN 中(X509v3 Subject Alternative Name)
Signature 位于证书最末尾,签名算法 sha256WithRSAEncryption 在 Data 域内已经指明 ,而 RSA 进行非对称加密所需的私钥(Private Key)则是由 Issuer 提供,Issuer 是一个可以签发证书的证书,由证书权威 CA 提供,CA 需要保证证书的有效性。
因为 Signature 是 RSA 算法生成的,那么客户端拿到 TLS 证书之后,需要 Issuer 的公钥(Public Key)才能解码出 Data 的摘要。
然而证书只携带了 Issuer 的 DN,并没有公钥,为了弄清楚客户端如何获取公钥,我们需要先搞明白 Certificate Chain(证书链)。


抓包技术分析及SSL PINNING保护方案_第3张图片
证书信息

证书链

一个完整的证书链一般由三种类型的证书组成

  • Root Certificate (DigiCert Global Root CA) :
    根证书
    Root Certificate是由Root CAs 发布,一个Root CAs 下包含多个Intermediate CAs ,同理一个Root Certificate下可以包含多个Intermediate Certificate。根证书是CA自己的证书,是证书验证链的开头
  • Intermediate Certificate (DigiCert SHA2 Secure Server CA):
    中间证书或者叫做中介证书
    Intermediate Certificate是由Intermediate CAs 发布,一个Intermediate CAs 下可以包含多个Intermediate CAs ,同理一个Intermediate Certificate下可以包含多个Intermediate Certificate也可以包含End-entity Certificate。CA会使用Intermediate Certificate 替代Root Certificate去做服务器端的证书签名,确保根证书密钥绝对不可访问
  • End-entity Certificate (user.cmbchina.com):
    终端证书即用户证书
    包含用来加密传输数据的公钥的证书,是HTTPS中使用的证书。 End-entity Certificate上面几级证书都是为了保证End-entity Certificate未被篡改,保证是CA签发的合法证书,进而保证End-entity Certificate中的公钥未被篡改


    抓包技术分析及SSL PINNING保护方案_第4张图片
    证书链

证书链验证

1.客户端得到服务端返回的证书,通过读取得到 服务端证书的发布机构(Issuer)
2.客户端去操作系统查找这个发布机构的的证书,如果是不是根证书就继续递归下去直到拿到根证书。
3.匹配客户端保存的可信任CA与根证书的CA,确保根证书的合法性
4.用 根证书的公钥 去 解密验证 上一层证书的合法性,再拿上一层证书的公钥去验证更上层证书的合法性;递归回溯。
5.最后验证服务器端的证书是 可信任

抓包技术分析及SSL PINNING保护方案_第5张图片
证书链验证

关于证书链信任认证过程可以参考我的另一篇文档

抓包原理-中间人攻击

前面基础性内容已经铺垫完毕,现在正式分析抓包技术
根据流程可以得出需要让中间人攻击生效,必须让客户端的证书连验证能够通过,并且现在几乎所有攻击手段都是从这入手,实现原理上可以分为两大类:
1.攻击客户端CA信任库,让系统在误认为伪证书是可以信任的,从而向中间人发送信息。手段包括
手机系统中安装伪证书
root手段导入伪证书至证书库
修改rom中系统证书库并烧录系统
例如Charles,Burp,Magisk,Packet Capture(VPN)都是使用这种方法
SSL PINING正是对抗这种技术的方法

抓包技术分析及SSL PINNING保护方案_第6张图片
抓包流程

2.通过脚本或者框架hook验证证书的方法,主要手段有:
修改系统checkServerTrusted或者TrustManager等涉及验证或者整理证书的相关代码,返回他们需要的结果
针对有自己的证书验证逻辑的第三方框架例如OKHttp,则hook修改CertificationPinner等方法
APP使用自己封装的通信方法,则通过逆向代码hook相关方法
Xposed+JustTrustMe或者Frida使用的是这种方法,针对这种技术则要避免将涉及通信的代码暴露,可以使用混淆,加壳等多种手段

抓包技术分析及SSL PINNING保护方案_第7张图片
hook脚本代码

SSL PINNING与双向认证

绿色表示ssl pinning在正常HTTPS通信过程中增加的操作
红色表示双向认证在正常HTTPS通信过程中增加的操作


ssl pinning及双向认证流程
抓包技术分析及SSL PINNING保护方案_第8张图片
ssl pinning与系统认证的区别

SSL PINNING本地证书管理

SSL PINNING唯一缺点即是如何对本地绑定的证书进行更新,针对该问题研究了一种解决方案方案:

  • 证书服务器:搭建一个CA服务器专门用于更新证书或者在验证证书时通过访问CA服务器验证
  • 与证书服务器通信内容及证书间比对都采用不可逆加密信息,防止中间人拿到通信内容反推出明文
  • 建立客户端及证书服务器端黑名单,尽可能减少与通信次数并增加中间人攻击成本
  • 证书服务器与客户端采用相同加密算法,保证加密后信息一致


    抓包技术分析及SSL PINNING保护方案_第9张图片
    证书服务器

Hook对抗方案

在攻击手段中,hook代码是一个较难防范的点,针对常用的Xposed框架可以做一下防范措施:

  • 检测进程中使用so名中包含关键"hack|inject|hook|call" 的信息


  • 检测手机是否被root:含有su程序和ro.secure是否为1


    抓包技术分析及SSL PINNING保护方案_第10张图片
  • 防止被Hook的方式就是可以查看XposedBridge这个类,有一个全局的hook开关,所有有的应用在启动的时候就用反射把这个值设置成true,这样Xposed的hook功能就是失效了

  • Xposed的hook原理的就是在程序启动都注入jar功能,所以安装hook模块之后,每个应用内部都包含了这个Xposed功能jar,就相当于你的应用中有了Xposed的所有功能类,所以在应用中反射Xposed的类是可以成功的


  • 如果应用被Xposed进行hook操作之后,抛出的异常堆栈信息中就会包含Xposed字样,所以可以通过应用自身内部抛出异常来检测是否包含Xposed字段来进行防护


    image.png
  • 如何在代码中执行shell命令并返回结果

public static ArrayList executeCommand(String[] shellCmd){
        String line = null;
        ArrayList fullResponse = new ArrayList();
        Process localProcess = null;
        try {
            Log.d("gscgsc","excute cmd");
            localProcess = Runtime.getRuntime().exec(shellCmd);
        } catch (Exception e) {
            return null;
        }
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
        BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
        try {
            while ((line = in.readLine()) != null) {
                Log.d("gscgsc","Line received: " + line);
                fullResponse.add(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.d("gscgsc","–> response was: " + fullResponse);
        return fullResponse
}

SSL PINING实现

String hostname = "xxx.com";
CertificatePinner certificatePinner = new CertificatePinner.Builder()
        .add(hostname, "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
        .add(hostname, "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
        .add(hostname, "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
        .build();

OkHttpClient client = new OkHttpClient.Builder()
        .certificatePinner(certificatePinner)
        .build();

Request request = new Request.Builder()
        .url("https://" + hostname)
        .build();

client.newCall(request).execute();

SSL PINING实现原理

证书校验时机
private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
    //包装socket
    sslSocket = (SSLSocket) sslSocketFactory.createSocket(
            rawSocket, address.url().host(), address.url().port(), true /* autoClose */);
    //配置socket参数
    ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
    //建立握手
    sslSocket.startHandshake();
    Handshake unverifiedHandshake = Handshake.get(sslSocket.getSession());
    //证书校验
    address.certificatePinner().check(address.url().host(),
            unverifiedHandshake.peerCertificates());
    //检验无异常则保存连接
    String maybeProtocol = connectionSpec.supportsTlsExtensions()
            ? Platform.get().getSelectedProtocol(sslSocket)
            : null;
}

//校验证书与本地证书
public void check (String hostname, List < Certificate > peerCertificates)
throws SSLPeerUnverifiedException {
    for (int p = 0, pinsSize = pins.size(); p < pinsSize; p++) {
        //加密后本地证书的存储
        Pin pin = pins.get(p);
        //sha256加密
        if (pin.hashAlgorithm.equals("sha256/")) {
            if (sha256 == null) sha256 = sha256(x509Certificate);
            if (pin.hash.equals(sha256)) return; // Success!
        }
        //sha1加密
        else if (pin.hashAlgorithm.equals("sha1/")) {
            if (sha1 == null) sha1 = sha1(x509Certificate);
            if (pin.hash.equals(sha1)) return; // Success!
        }
    }
    //校验失败抛出异常
    throw new SSLPeerUnverifiedException(message.toString());
}

结果如下


结果

你可能感兴趣的:(抓包技术分析及SSL PINNING保护方案)