前言

这篇文章算是总结一下我之前抓包遇到的一些问题, 个人属性里带bug, 所以遇到的问题会比较多, 算是给大家提供一个抓包抓不到应该如何解决的思路。

工具介绍

Android中可用的抓包软件有fiddler、burpsuite、Charls、HttpCanary、Packet Capture、tcpdump、wireshark等等。tcpdump和wireshark可以解决部分不是使用HTTP/HTTPS协议传输数据的app, 用tcpdump抓包, 用wireshark分析数据包。

如果想抓取三大运营商传输的数据包并分析, 因其路由规则的限制, 可能还是需要在android系统中利用iptables设置反向代理, 用Fiddler解密数据包之后分析, 不过好像Fiddler好像有自己的反向代理设置方法, 这部分了解不多。

Charls是Mac上常见的抓包工具, 我没用过, 不过网上蛮多教程的。HttpCanary和Packet Capture这两个工具与常规的电脑上的代理抓包不同的是, 能保证一定能抓取到数据包, 我一般都用Packet Capture来验证应用是否发送请求。HttpCanary被称为移动端的Fiddler, 能够改包和劫持双向认证的应用传输的数据包, 感觉还是蛮强大的。

Fiddler抓取Android数据包

基础设置

下载好Fiddler之后, 打开该软件, 生成证书。

设置连接

 

Android sip 抓包 安卓端抓包_Android sip 抓包

设置HTTPS

Android sip 抓包 安卓端抓包_android_02

用ipconfig查看当前主机的ip

Android sip 抓包 安卓端抓包_SSL_03

手机和电脑在同一局域网中即可, 手机端设置WLAN种给网络设置代理, 选择对应的WLAN, 选中修改网络, 手动设置代理, 主机名填上面电脑ip地址, 端口写fiddler默认端口8888。

Android sip 抓包 安卓端抓包_android_04

手机端用浏览器访问http://电脑IP:8888(http://电脑IP:端口), 观察网络是否访问成功, 成功之后, 点击"FiddlerRoot.certificate"下载Fiddler的证书并安装。

Android sip 抓包 安卓端抓包_网络_05

如果上述步骤都原原本本做完了, 还是不能出现上图的效果, 可以换个路由或者直接手机开热点。我当时遇到不能访问的问题, ping了一下, 一直显示destination unreachable, 应该是路由器安全规则的限制, 换成了手机开热点就ok了。

继续进行测试的时候, 发现不管是修改密码还是用验证码进行登录, 我都抓不到那些包。想不出是哪里出了问题.....大概找了一下, 发现是SSL Pinning的机制阻止了我抓包。使用了Xposed+JustTrustMe, 就抓取到数据包了, 数据包如下:

Android sip 抓包 安卓端抓包_网络_06

如果知道Fiddler怎么抓包了, 不知道怎么改包, 可以用Fiddler左下角的黑框框中断请求, 修改之后再发出, 比如输入bpu baidu.com就可以中断所有发向baidu.com的请求。

Android sip 抓包 安卓端抓包_Android sip 抓包_07

之后查看中断的数据包会出现如下效果, 修改完点击Run to Completion就可以把请求发出去了。

Android sip 抓包 安卓端抓包_抓包_08

Fiddler设置之后手机无法连接上代理

  1. 关闭电脑防火墙
  2. 打开注册表(cmd-regedit), 在HKEY_CURRENT_USER\Software\Microsoft\Fiddler2下创建一个DWORD, 值置为80(十进制)[在空白处右键即可创建]。
  3. Android sip 抓包 安卓端抓包_SSL_09


  4. 编写fiddlerScript rule:在Fiddler上点击Rules->Customize Rules, 用Ctrl+F查找OnBeforeRequest方法添加一行代码。
if (oSession.host.toLowerCase() == "webserver:8888") 
{
        oSession.host = "webserver:80"; 
}

Burpsuite抓取Android数据包

基础设置

urpsuite改包的步骤就不在这里赘述了, 网上有很多教程, 接下来我们要设置burpsuite, 以求抓取到数据包, 设置如下:

Android sip 抓包 安卓端抓包_抓包_10

提示, 监听的端口号、电脑内网ip要和手机上的代理设置一致, 电脑内网ip可以用ipconfig查看。用burpsuite一直抓取不到https的证书, 怀疑是我burpsuite证书没有安装到手机上, 所以我现在先将它装到系统证书中, 再看看能不能先抓取到https的证书。

安装证书至系统中

1、下载.der格式的证书, 将下载的cacert.der转换格式, 并获取证书hash值, 生成<证书hash>.0文件, 例如:7bf17d07.0

2、把<证书hash>.0证书push到/data/local/tmp目录下后移动至/system/etc/security/cacerts/
(mv操作出错之后, 先试一下“mount -o rw,remount /system”如果出现了报错“mount: '/system' not in /proc/mounts”, 再尝试“mount -o rw,remount /”, 就可以操作system目录了)

3、重启手机

只有root环境才能将proxy证书安装至android系统证书中, 这种方法好像能绕过应用本地证书校验, 其实burp和Fiddler还有其他的代理证书的安装方法都差不多, 最后将<hash证书>.0的文件mv至/system/etc/security/cacerts/目录下即可, 不建议直接将用户证书直接mv, 可能会导致环境出错也不好排查证书错误, 甚至可能导致android网络环境出错。</hash证书>

下面是具体步骤, 先在设置本地代理, 将burpsuite证书下载下来

Android sip 抓包 安卓端抓包_Android sip 抓包_11

打开浏览器输入本地地址, 下载.der格式的证书

Android sip 抓包 安卓端抓包_SSL_12

此处参照文章BrupSuit证书导入Android7.0以上手机, 因为我windows本地安装了ubuntu的子系统, 所以直接用ubuntu1604子系统对证书进行操作。

// 转换证书的格式
$ openssl x509 -in cacert.der -inform DER -out cacert.pem -outform PEM
// 提取证书的hash
$ openssl x509 -inform PEM -subject_hash -in cacert.pem

Android sip 抓包 安卓端抓包_android_13


上图中的7bf17d07就为证书的hash值, 将该目录下生成的7bf17d07.0文件push到手机中, 最后移动到/system/etc/security/cacerts/目录下

$ adb push 7bf17d07.0 /data/local/tmp
$ adb shell 
sailfish:/ $ su
sailfish:/ # mount -o rw,remount / # 拥有操作/目录的权限, 本意是要操作/system目录
sailfish:/ # mv /data/local/tmp/7bf17d07.0 /system/etc/security/cacerts/7bf17d07.0

按照原本的文章应该给7bf17d07.0文件添加644权限, 但是我具体操作的时候没有添加权限也成功了, 如果按照我上面的步骤出错了, 可以尝试给文件添加权限。重启之后可以看到证书安装成功。

Android sip 抓包 安卓端抓包_android_14

第一次安装证书的时候出现了不能访问使用https协议的网站, 应该是我测试的手机环境出现了问题, 我重新刷机再按照上面的步骤走一遍就成功了, 如果你们也遇到访问https网站失败的问题, 可以尝试一下使用这个方法。

Android抓包介绍

抓包最重要的是看能不能抓取到数据包, 想要抓到包就要看app使用什么传输协议了, 一般情况下使用HTTP都是能抓到包的, 这也就不难理解, 为什么google坚持推广HTTPS了。为什么说使用HTTPS会抓不到包?现在的HTTPS都是基于TLS协议的, 它的特点就是需要确认传输双方的身份。确认了身份之后再传输数据, 这样就能避免中间人攻击了。下面来看看HTTPS, 是怎么进行数据传输的, 发现HTTPS需要先建立连接才能传输数据。

Android sip 抓包 安卓端抓包_SSL_15

讲到要认证对方的身份, 我就想起了之前翻译的一篇HTTP安全, 里面就有提及到在使用HTTPS协议的过程中, 客户端和服务器通过证书来判断对方的身份。之前没有怎么理解, 现在才对证书的作用有比较深刻的理解。

文章中举了个例子, Chrome浏览器通过判断是否有证书来判断你访问的网站是否安全的, 并不是你访问的网站真的是安全的。提及这个是因为app使用HTTPS传输也是看证书的, 只不过有的app限制的比较严格只信任自带的证书, 有的app安全要求没那么高, 直接信任系统证书。

抓包出错排查思路

Android sip 抓包 安卓端抓包_抓包_16


上面是大概的排查思路, 具体的细节可能有些差异。如果proxy带有证书校验, 且JustTrustMe绕不过去, 可能要自己重新根据该应用定制hook模块, 去绕过其本地证书校验, 但是大部分应用都能通过将证书安装为系统证书绕过, 如果无法在root环境下运行, 文章《Intercepting traffic from Android Flutter applications》和JustTrustMe的源码应该能给你提供一点hook模块绕过证书校验的思路, 《Intercepting traffic from Android Flutter applications》讲的是如何绕过google开源框架Flutter中的证书校验进行抓包。

最后说抓不到包还有一种可能性, 就是要求一定要用SIM卡发出传输请求的数据包....不过这个应该应该只有使用了三大运营商的SDK或他们的应用才会出现这种情况, 这部分应该只能用反向代理才有可能抓取到传输的数据包了, 具体情况就要具体分析了。

当时尝试tcpdump+wireshark效果不怎么样, 因为所有的数据都经过了加密, 而wireshark不能解密, 所以对于加密传输的数据包这种方法可能有点鸡肋, 听说有mitmdump抓包工具专门处理linux环境下http/https的数据包, 不过我自己没用过, 之后要是接触了会进一步补充。

SSL pinning和双向认证的区别

SSL pinning实际上是客户端锁定服务器端的证书, 在要与服务器进行交互的时候, 服务器端会将CA证书发送给客户端, 客户端会调用函数对服务器端的证书进行校验, 与本地的服务器端证书(存放在\<app>\asset目录或\res\raw下)进行比对。

而双向认证是添加了客户端向服务器发送CA证书, 服务器端对客户端的证书进行校验的部分, 具体详情可看文章扯一扯HTTPS单向认证、双向认证、抓包原理、反抓包策略的单向认证、双向认证部分的内容。

抓取HTTPS的数据包

Frida绕过SSL单向校验

昨天刚好遇到JustTrustMe无法绕过SSL单向校验的情况, 这几天接触了Frida, 就尝试用DBI的方法绕过SSL的单向校验, 参考文章Universal Android SSL Pinning bypass with Frida这里就不详细地说明Frida的安装方法及使用方法了。

设置Fiddler代理, 在本地下载Fiddler的证书, 将证书直接重命名为cert-der.crt。之后将证书push到/data/local/tmp目录下, 在adb shell里输入./frida-server &再在PC端进行操作。

新建一个frida-android-repinning.js文件, 详细代码如下:

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...")
        var 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);

在cmd, 输入如下命令:

$ 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

在关闭应用的情况下(避免Magisk Hide处于开启状态), 可得到回显并绕过SSL pinning。

Android sip 抓包 安卓端抓包_抓包_17

绕过SSL双向校验

其实SSL双向校验是在SSL单向校验的基础上, 在说明这部分内容的时候同时也会有绕过SSL单向校验详细的步骤。参考文章Android平台HTTPS抓包解决方案及问题分析, 我们可以先用sxxl.app来练练手。

在手机上设置完代理之后, 点击完确认, 发现app出现如下弹窗:

Android sip 抓包 安卓端抓包_Android sip 抓包_18

在这个时候查看Fiddler会发现应用没有发出任何请求, 这是因为app会对服务器端的证书进行校验, 这时候我们前面安装的Fiddler证书就不起作用了, 应用在发现证书是伪造的情况下拒绝发送请求。根据这个报错+抓不到包, 我们可以确定应用是存在单向校验的, 也就是SSL pinning, 让我们先来解决SSL pinning的问题。使用JustTrustMe可以绕过客户端的证书校验, 下面勾选上JustTrustMe, 在Xposed框架下使用JustTrustMe绕过SSL pinning。

Android sip 抓包 安卓端抓包_SSL_19

绕过SSL pinning之后, 就能使用Fiddler抓取到HTTPS的数据包了。

Android sip 抓包 安卓端抓包_android_20

我随便输入了一个手机号码, 按下确定之后, 服务器回传了400的状态码过来, 说需要发送证书以确认客户端的身份。到这一步基本能确定是存在双向校验的了, 接下来的工作就是绕过SSL服务器端的校验了。

Android sip 抓包 安卓端抓包_SSL_21

如果服务器端会对客户端证书进行校验, 证书应该就直接存放在apk里, 网上与SSL双向校验相关的文章都将证书放到<app>/asset目录下, 也就是app的资源目录下, 也有可能放在/res/raw目录下。直接将app解压之后, 发现证书的位置如下:

Android sip 抓包 安卓端抓包_Android sip 抓包_22

如果找半天没找到就用关键词.p12/.pfx搜索证书文件。

在我们要使用该证书的时候, 需要输入安装证书的密码。这时候就需要从源码中获取安装证书的密码了。可能是因为多个dex文件的原因, 直接用JEB反编译的时候出错了, 所以我用GDA反编译来分析应用的源代码

获取安装证书的密码

发现通过关键词"PKCS12"能够定位到加载证书的位置。

Android sip 抓包 安卓端抓包_android_23

上图第二个红框中的load函数的第二个参数其实就是证书的密钥, 追根溯源, 我们可以知道v1参数是下图中调用的函数的返回值。

Android sip 抓包 安卓端抓包_抓包_24

Android sip 抓包 安卓端抓包_网络_25

上图的函数的功能就是传递p0参数, 也就是说p0参数就是证书安装密码。想获取这个密码, 关键在于Auto_getValue函数。到这一步, 只要跟进Null_getStorePassword函数看看就好了。

Android sip 抓包 安卓端抓包_android_26

跟进去发现调用了native层的函数, 查看init函数中具体加载的是哪个so文件:

Android sip 抓包 安卓端抓包_网络_27

用IDA反编译soul-netsdk之后, 搜索字符串"getStorePassword", 就定位到函数getStorePassword上了, F5之后, 获得伪代码和密钥:

Android sip 抓包 安卓端抓包_android_28

代理添加客户端证书

HttpCanary添加客户端证书进行抓包的过程可以参照文章Android平台HTTPS抓包解决方案及问题分析, 在自己头昏的时候也感谢这篇文章的作者MegatronKing点醒我。下面主要讲解Fiddler和burpsuite添加客户端证书的方法。

fiddler操作过程

尝试一下用Fiddler处理这部分的内容来安装客户端的证书, 用来绕过双向认证。

Android sip 抓包 安卓端抓包_网络_29

用Fiddler抓取该应用的数据包的时候, 发现Fiddler出现了上面的弹窗, 提示要添加ClientCertificate.cer, 才能抓取到传输的数据包, 不然只会出现400的状态码。而我们文件目录下只能找到client.p12和client.crt两种格式的证书文件, 所以我们需要将已有的client证书转换成.cer格式的证书。

Android sip 抓包 安卓端抓包_Android sip 抓包_30

好像应用中只出现.p12格式的证书的情况比较常见, 所以下面只会提及如何使用openssl将.p12格式的证书转换成.cer/.der格式的证书。(.der和.cer格式的证书仅有文件头和文件尾不同)

下面的命令实现了证书的格式转换, .p12->.pem->.cer, 在生成.pem格式的证书之后, 需要输入证书的密码, 也就是我们上面逆向获取的证书密码。最后将ClientCertificate.cer移动到之前Fiddler弹窗出现的目录下, 也就是<Fiddler安装路径>\Fiddler2下。

# 将.p12证书转换成.pem格式
$ openssl pkcs12 -in client.p12 -out ClientCertificate.pem -nodes
Enter Import Password:
# 将.pem证书转换成.cer格式
$ x509 -outform der -in ClientCertificate.pem -out ClientCertificate.cer

现在打开Fiddler尝试抓包, 发现原本显示400的数据包现在能够正常抓取到了, 如果还是不能正常抓取到, 双击client.p12将证书安装到本地试试看。

Android sip 抓包 安卓端抓包_Android sip 抓包_31

burp操作过程

手机的burpsuite证书安装成功之后, 我们会发现只能抓取到400的状态码。

Android sip 抓包 安卓端抓包_Android sip 抓包_32

因为要绕过服务器端对证书的验证, 我们还需要在这里添加上面我们在asset目录下找到的证书。

Android sip 抓包 安卓端抓包_android_33

Android sip 抓包 安卓端抓包_网络_34

安装完就能正常抓取数据包了。

抓取TCP的数据包

现在还不知道怎么能够获取TCP的数据包并对其中的内容进行解密, 不过之前在看雪上看到一篇分析使用TCP传输协议的文章, 我大概看了一下, 文章是从逆向的角度分析的, 具体怎么从渗透的角度发现是TCP协议传输的数据包还没有分析过, 看作者使用了wireshark抓取应用的数据包并进行分析, 这个还是要重新分析一下的。