9

[翻译]利用Frida绕过Certificate Pinning

lumou 2017-10-31 10:32 2130
 

亲爱的读者:

 

一言以蔽之,进行移动设备的bug bounty(漏洞报告奖励)超难的。在这篇文章中,我想告诉你一种更简单的方法来绕过部分目标Android手机的Certificate Pinning(证书锁定)。这里所描述的方法是基于 Piergiovanni Cipolloni.写的这篇超赞的博文和研究。凡是提及绕过 Certificate Pinning都离不开对应用程序的SSLContext的相关操作。研究人员利用Frida脚本控制SSLContext能全面绕过Android上的certificate pinning的脚本。

 

概括起来说,脚本的功能包括:

  1. 从文件系统下载一个流氓证书(Burpsuit的CA 证书)。
  2. 创建自己的KeyStore,其中包含我们的信任CA.
  3. 最后一步它创建一个信任我们的KeyStore中CA的TrustManager。

当应用程序初始化其SSLContext时,Frida脚本将会拦截SSLContext.init(),并且在其被调用时会用我们自己的TrustManager取代它的第二个参数。在Android中,SSLContext函数是这么被调用的:SSLContext.init(KeyManager, TrustManager, SecuRandom)).

 

对于新手来说,要理解 Piergiovanni Cipolloni这篇文章可能会比较困难,所以,在这篇博文中会有相应的介绍,以助读者能正确地配置好环境。

 

接下来的内容包括:
 如何在你的设备上安装Burp的证书;
 如果在移动设备上部署Burpsuite;
 如何在Android设备上安装Frida;
 如何运行Frida远程服务器;
 如何在你的服务器上安装Frida;
 如何在应用程序中运行Frida脚本。

为什么要Certificate Pinning

通过Certificate Pinning,开发者能确保他们的应用程序不会接受伪造的证书,而是真的由官方证书授权机构签发的。Android系统只检查证书的层次结构是否正确以及该CA (Certificate Anthority 证书授权机构 )是否在所谓的“信任商店的证书”列表中。但是,如果有个坏人,他在可信商店中安装了伪造的CA,又会发生什么呢?最起码,他可以拦截整个应用程序的HTTPS通信。因此,开发人员要想得比那些他们坏人更远,也就是将真实的证书编译到app中,并确保只使用这个证书。
事先准备:
 你需要一台Android虚拟机(或真机)
 一点空闲时间
 安装并配置好的ADB/Android tools

Setup – Part 1 安装 Burp证书

在浏览器上运行Burpsuite(默认:127.0.0.1:8080)并点击CA Certificate

 

这一步会下载一个名为cacert.der的文件,获取该文件并重命名为cacert.cer。如果你想知道其中的区别,请移步:http://www.gtopia.org/blog/2010/02/der-vs-crt-vs-cer-vs-pem-certificates/。简单说的话,它就只是另一种格式而已。

 

如果你的机器是Mac,那默认下载目录应该是Downloads。你要做如下操作:
1) cd/Downloads ——将当前目录转到Downloads。
2) mv cacert.der cacert.cer ——重命名证书
3) adb push cacert.cer /mnt/sdcard/DCIM/ ——将证书复制到手机的SD卡上。

 

并不难,对吧?

 

在设备(不管是虚拟机还是真机)中,为了要将它加入到Android的信任证书商店中,我们得安装该证书。

 

这一步我们要这样做:
点击菜单按钮——设置(Settings)——下拉到安全(Security)——选择从SD卡安装(Install from SD card)

点击cacert.cer,将其命名为Burp.
如果一切顺利,在 设置 (Settings)-> 安全 (Security)-> 可信证书 (Trusted credentials)-> 用户(User)后,你会看到下面的画面:

 

~啦啦啦~——你现在已经在自己的系统中安装了自己的CA。

Setup – Part 2 配置Burpsuite

现在我们已经在我们的系统中安装了PortSwigger CA,要拦截从应用程序到服务器的通信我们还需要安装代理。

设置代理

再次来到设置(Settings)—>点击Wi-Fi—>长按并点击WiredSSID—>点击修改网络(Modify Network)—>点击代理(Proxy)—>选择手动(Manual)

 

然后你就可以看到下面的表单:

在这里你要填的是:

 

代理主机(Proxy Hostname)——填(在你的PC上的)Burp Suite应用程序的IP地址(本地IP地址)
端口(Port)——Burp Suite运行的端口(默认是8080)。

设置Burp

Burp默认会打开一个运行在localhost:8080的代理。为了拦截移动通信我们需设置Burp去监听外部IP地址。打开Burpsuite,打开代理(Proxy)—>选项(Options)选择当前配置(如下图所示),点击编辑(Edit)

现在选择指定地址(Spcific Address),然后选择你自己的本地IP地址(我自己的是192.168.0.193)。你的可能不同,取决于你的DHCP服务器是如何分配地址的。

 

如果你不确定,可以打开终端(Terminal),并输入(在OSX) ifconfig en0 | grep inet

 

如果你跟着教程走到了这一步——你现在就可以拦截没有Certificate Pining的应用程序的通信了!下一章就是关于如何绕过Certificate Pining。

Setup ——Part 3 在Android设备上安装Frida

要使用此方法绕过Certificate Pining,我们需要在Android设备上安装Frida-Server的副本。要达到这一步,我们需要最新版本的Frida-Server,这可以从Frida在Github上的发布页面下载: https://github.com/frida/frida/releases。你现在要考虑你需要安装的是哪个版本……x86 ? x86_64? arm?——感谢天感谢地,我们只需一条命令就好了。只需 adb shell getpro ro.product.cpu.abi就能找到适合你的版本,我自己的是x86。所以,对我来说,合适的版本就是frida-server-10.6.15-android-x86.xz。有经验的读者会注意到.xz扩展名是一个文件夹,所以,在我们使用该二进制文件之前,我们得先解压。对于喜欢使用terminal命令行的人,可以使用tar –xJf frida-server-10.6.15-android-x86语句进行解压,对于其他人, 可以用the Unarchiver解压。在解压完这个二进制文件后,我们还需再次使用terminal。

 

1) mv frida-server-10.6.15-android-x86 frida-server ——将其重命名为frida-server
2) adb root ——确保是在root下运行
3) adb push frida-server /data/local/tmp/ ——将这个frida-server二进制文件复制到设备中
4) adb shell “chmod 755 /data/local/tmp/frida-server” ——修改该二进制文件的读写权限
5) adb shell “/data/local/tmp/frida-server&” ——后台运行frida-server
5.1) adb shell “/data/local/tmp/frida-server –listen 0.0.0.0 &” ——如果你希望frida-server在外部IP上运行的话,就这么设置。

在你的机器上安装frida

不管是哪一种平台,你需要做的就是 sodu pip install frida.

 

为证实Frida的安装过程(本地和远程),你需要用到frida-ps命令。这里的ps命令会列出设备上的当前进程。

 

如果是通过USB连接则用:frida-ps U

 

如果你在外部地址使用则用:frida:frida-ps –H 192.x.x.x 这些x取决于你的手机地址。

 

你会看到类似下图的输出:

 

然后你要选择一个,并准备绕过certificate pinning。

 

为进行概念验证,我这里就随机选择一个应用程序啦,如果你想看看真实世界中移动平台的bug bounty是怎样的,你可以去 HackeroneBugcrowdSynackZerocopter看看。

 

要开始绕过certificate pinning,我们需要 Piergiovanni Cipolloni的关于Android SSL Repinning 的frida 脚本,可以在这里这里或者在本文底部找到。

 

使用frida绕过certificate pinning
首先,我们需要在设备上安装要绕过的目标程序,可以通过一下两种方式完成:

 

1) 从Google Store下载安装
2) 用 Apkpureapk-dl 下载该应用程序
在下载完成后,打开terminal,并用adb install com.company.whatever.apk命令安装该程序。完成。
下一步是从所有的应用程序中选择我们的目标,就像之前那样,如果是远程frida 服务器就用:frida-ps –H 192.x.x.x,如果通过USB连接,则用frida-ps –U.我们下一步要做的就是,复制先前生成的cacert.cer(如果你是按教程走到这一步的话,那这个文件应该还是在你的Downloads文件夹下)。

 

最后(我保证,真的是最后了!)打开你的terminal:

 

1) cd Downloads ——来到“cacert.cer”所在的Downloads文件夹
2) mv cacert.cer burpca-cert-der.crt ——重命名该文件,以对应脚本中的文件
3) wget https://techblog.mediaservice.net/wp-content/uploads/2017/07/frida-android-repinning_sa-1.js ——下载该frida 脚本
4) adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt ——将伪造的证书push到设备中
5) frida -U -f it.app.mobile -l frida-android-repinning_sa-1.js --no-pause ——完成!

 

如果你看到如下的输出:

 

这就意味着你都做对了,你现在可以拦截那些bug bounty项目的通信了。

多谢各位!

 

Patrik @itsecurityguard

 

PPS:出于校对的目的,向EdOverflow https://twitter.com/EdOverflow 喊个话。

/* 
   Android SSL Re-pinning frida script v0.2 030417-pier 

   $ adb push burpca-cert-der.crt /data/local/tmp/cert-der.crt
   $ frida -U -f it.app.mobile -l frida-android-repinning.js --no-pause

   https://techblog.mediaservice.net/2017/07/universal-android-ssl-pinning-bypass-with-frida/
*/

setTimeout(function(){
    Java.perform(function (){
        console.log("");
        console.log("[.] Cert Pinning Bypass/Re-Pinning");

        var CertificateFactory = Java.use("java.security.cert.CertificateFactory");
        var FileInputStream = Java.use("java.io.FileInputStream");
        var BufferedInputStream = Java.use("java.io.BufferedInputStream");
        var X509Certificate = Java.use("java.security.cert.X509Certificate");
        var KeyStore = Java.use("java.security.KeyStore");
        var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
        var SSLContext = Java.use("javax.net.ssl.SSLContext");

        // Load CAs from an InputStream
        console.log("[+] Loading our CA...")
        cf = CertificateFactory.getInstance("X.509");

        try {
            var fileInputStream = FileInputStream.$new("/data/local/tmp/cert-der.crt");
        }
        catch(err) {
            console.log("[o] " + err);
        }

        var bufferedInputStream = BufferedInputStream.$new(fileInputStream);
          var ca = cf.generateCertificate(bufferedInputStream);
        bufferedInputStream.close();

        var certInfo = Java.cast(ca, X509Certificate);
        console.log("[o] Our CA Info: " + certInfo.getSubjectDN());

        // Create a KeyStore containing our trusted CAs
        console.log("[+] Creating a KeyStore for our CA...");
        var keyStoreType = KeyStore.getDefaultType();
        var keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore...");
        var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        var tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);
        console.log("[+] Our TrustManager is ready...");

        console.log("[+] Hijacking SSLContext methods now...")
        console.log("[-] Waiting for the app to invoke SSLContext.init()...")

           SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) {
               console.log("[o] App invoked javax.net.ssl.SSLContext.init...");
               SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c);
               console.log("[+] SSLContext initialized with our custom TrustManager!");
           }
    });
},0);

原文链接:https://blog.it-securityguard.com/the-stony-path-of-android-%F0%9F%A4%96-bug-bounty-bypassing-certificate-pinning/
本文由看雪翻译小组 lumou 编译



快讯:[看雪招聘]十八年来,看雪平台输出了大量安全人才,影响三代安全人才!

最新回复 (3)
liangafu 2017-10-31 11:34
2

gestic 2017-11-3 15:30
3
666
走FKJ 2018-4-27 15:20
4
没有成功。。方便加个qq或者wx沟通一下吗?
返回