转载自:Android抓包总结 - 先知社区 https://xz.aliyun.com/t/6551
Se8s0n / 2019-10-19 10:22:01 /
这篇文章算是总结一下我之前抓包遇到的一些问题, 个人属性里带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, 能够改包和劫持双向认证的应用传输的数据包, 感觉还是蛮强大的。
设置连接
设置HTTPS
用ipconfig查看当前主机的ip
手机和电脑在同一局域网中即可, 手机端设置WLAN种给网络设置代理, 选择对应的WLAN, 选中修改网络, 手动设置代理, 主机名填上面电脑ip地址, 端口写fiddler默认端口8888。
手机端用浏览器访问http://电脑IP:8888(http://电脑IP:端口), 观察网络是否访问成功, 成功之后, 点击"FiddlerRoot.certificate"下载Fiddler的证书并安装。
如果上述步骤都原原本本做完了, 还是不能出现上图的效果, 可以换个路由或者直接手机开热点。我当时遇到不能访问的问题, ping了一下, 一直显示destination unreachable, 应该是路由器安全规则的限制, 换成了手机开热点就ok了。
继续进行测试的时候, 发现不管是修改密码还是用验证码进行登录, 我都抓不到那些包。想不出是哪里出了问题.....大概找了一下, 发现是SSL Pinning的机制阻止了我抓包。使用了Xposed+JustTrustMe, 就抓取到数据包了, 数据包如下:
如果知道Fiddler怎么抓包了, 不知道怎么改包, 可以用Fiddler左下角的黑框框中断请求, 修改之后再发出, 比如输入bpu baidu.com
就可以中断所有发向baidu.com的请求。
之后查看中断的数据包会出现如下效果, 修改完点击Run to Completion就可以把请求发出去了。
关闭电脑防火墙
打开注册表(cmd-regedit), 在HKEY_CURRENT_USER\Software\Microsoft\Fiddler2下创建一个DWORD, 值置为80(十进制)[在空白处右键即可创建]。
if (oSession.host.toLowerCase() == "webserver:8888")
{
oSession.host = "webserver:80";
}
Burpsuite改包的步骤就不在这里赘述了, 网上有很多教程, 接下来我们要设置burpsuite, 以求抓取到数据包, 设置如下:
提示, 监听的端口号、电脑内网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还有其他的代理证书的安装方法都差不多, 最后将
下面是具体步骤, 先在设置本地代理, 将burpsuite证书下载下来
打开浏览器输入本地地址, 下载.der格式的证书
此处参照文章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
上图中的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权限, 但是我具体操作的时候没有添加权限也成功了, 如果按照我上面的步骤出错了, 可以尝试给文件添加权限。重启之后可以看到证书安装成功。
第一次安装证书的时候出现了不能访问使用https协议的网站, 应该是我测试的手机环境出现了问题, 我重新刷机再按照上面的步骤走一遍就成功了, 如果你们也遇到访问https网站失败的问题, 可以尝试一下使用这个方法。
抓包最重要的是看能不能抓取到数据包, 想要抓到包就要看app使用什么传输协议了, 一般情况下使用HTTP都是能抓到包的, 这也就不难理解, 为什么google坚持推广HTTPS了。为什么说使用HTTPS会抓不到包?现在的HTTPS都是基于TLS协议的, 它的特点就是需要确认传输双方的身份。确认了身份之后再传输数据, 这样就能避免中间人攻击了。下面来看看HTTPS, 是怎么进行数据传输的, 发现HTTPS需要先建立连接才能传输数据。
讲到要认证对方的身份, 我就想起了之前翻译的一篇HTTP安全, 里面就有提及到在使用HTTPS协议的过程中, 客户端和服务器通过证书来判断对方的身份。之前没有怎么理解, 现在才对证书的作用有比较深刻的理解。
文章中举了个例子, Chrome浏览器通过判断是否有证书来判断你访问的网站是否安全的, 并不是你访问的网站真的是安全的。提及这个是因为app使用HTTPS传输也是看证书的, 只不过有的app限制的比较严格只信任自带的证书, 有的app安全要求没那么高, 直接信任系统证书。
上面是大概的排查思路, 具体的细节可能有些差异。如果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实际上是客户端锁定服务器端的证书, 在要与服务器进行交互的时候, 服务器端会将CA证书发送给客户端, 客户端会调用函数对服务器端的证书进行校验, 与本地的服务器端证书(存放在\
目录或\res\raw
下)进行比对。
而双向认证是添加了客户端向服务器发送CA证书, 服务器端对客户端的证书进行校验的部分, 具体详情可看文章扯一扯HTTPS单向认证、双向认证、抓包原理、反抓包策略的单向认证、双向认证部分的内容。
昨天刚好遇到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。
其实SSL双向校验是在SSL单向校验的基础上, 在说明这部分内容的时候同时也会有绕过SSL单向校验详细的步骤。参考文章Android平台HTTPS抓包解决方案及问题分析, 我们可以先用sxxl.app来练练手。
在手机上设置完代理之后, 点击完确认, 发现app出现如下弹窗:
在这个时候查看Fiddler会发现应用没有发出任何请求, 这是因为app会对服务器端的证书进行校验, 这时候我们前面安装的Fiddler证书就不起作用了, 应用在发现证书是伪造的情况下拒绝发送请求。根据这个报错+抓不到包, 我们可以确定应用是存在单向校验的, 也就是SSL pinning, 让我们先来解决SSL pinning的问题。使用JustTrustMe可以绕过客户端的证书校验, 下面勾选上JustTrustMe, 在Xposed框架下使用JustTrustMe绕过SSL pinning。
绕过SSL pinning之后, 就能使用Fiddler抓取到HTTPS的数据包了。
我随便输入了一个手机号码, 按下确定之后, 服务器回传了400的状态码过来, 说需要发送证书以确认客户端的身份。到这一步基本能确定是存在双向校验的了, 接下来的工作就是绕过SSL服务器端的校验了。
如果服务器端会对客户端证书进行校验, 证书应该就直接存放在apk里, 网上与SSL双向校验相关的文章都将证书放到
目录下, 也就是app的资源目录下, 也有可能放在/res/raw
目录下。直接将app解压之后, 发现证书的位置如下:
如果找半天没找到就用关键词.p12/.pfx
搜索证书文件。
在我们要使用该证书的时候, 需要输入安装证书的密码。这时候就需要从源码中获取安装证书的密码了。可能是因为多个dex文件的原因, 直接用JEB反编译的时候出错了, 所以我用GDA反编译来分析应用的源代码
发现通过关键词"PKCS12"能够定位到加载证书的位置。
上图第二个红框中的load函数的第二个参数其实就是证书的密钥, 追根溯源, 我们可以知道v1参数是下图中调用的函数的返回值。
上图的函数的功能就是传递p0参数, 也就是说p0参数就是证书安装密码。想获取这个密码, 关键在于Auto_getValue函数。到这一步, 只要跟进Null_getStorePassword函数看看就好了。
跟进去发现调用了native层的函数, 查看init函数中具体加载的是哪个so文件:
用IDA反编译soul-netsdk之后, 搜索字符串"getStorePassword", 就定位到函数getStorePassword上了, F5之后, 获得伪代码和密钥:
HttpCanary添加客户端证书进行抓包的过程可以参照文章Android平台HTTPS抓包解决方案及问题分析, 在自己头昏的时候也感谢这篇文章的作者MegatronKing点醒我。下面主要讲解Fiddler和burpsuite添加客户端证书的方法。
fiddler操作过程
尝试一下用Fiddler处理这部分的内容来安装客户端的证书, 用来绕过双向认证。
用Fiddler抓取该应用的数据包的时候, 发现Fiddler出现了上面的弹窗, 提示要添加ClientCertificate.cer, 才能抓取到传输的数据包, 不然只会出现400的状态码。而我们文件目录下只能找到client.p12
和client.crt
两种格式的证书文件, 所以我们需要将已有的client证书转换成.cer
格式的证书。
好像应用中只出现.p12
格式的证书的情况比较常见, 所以下面只会提及如何使用openssl将.p12
格式的证书转换成.cer/.der
格式的证书。(.der和.cer格式的证书仅有文件头和文件尾不同)
下面的命令实现了证书的格式转换, .p12
->.pem
->.cer
, 在生成.pem
格式的证书之后, 需要输入证书的密码, 也就是我们上面逆向获取的证书密码。最后将ClientCertificate.cer
移动到之前Fiddler弹窗出现的目录下, 也就是
下。
# 将.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
将证书安装到本地试试看。
burp操作过程
手机的burpsuite证书安装成功之后, 我们会发现只能抓取到400的状态码。
因为要绕过服务器端对证书的验证, 我们还需要在这里添加上面我们在asset目录下找到的证书。
安装完就能正常抓取数据包了。