抓包对抗与实战分析
发布日期:2024-05-19 浏览次数: 专利申请、商标注册、软件著作权、资质办理快速响应热线:4006-054-001 微信:15998557370
目录
抓包对抗与实战分析
VPN验证绕过
HTTPS到底是什么
一图讲解单项认证和双向认证
抓包工具抓HTTP包的原理
抓包工具抓HTTPS包的原理
服务端校验客户端绕过
客户端校验服务端绕过
总结语
VPN验证绕过
目前主流的方法是开启 VPN 抓包,所以挂 wifi 代理,不在本文讨论中。
随着 VPN 抓包趋势的增加,同时很多 App 会增加对 VPN 的检测。
java.net.NetworkInterface.getName() 是检测 VPN 的 API。
Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); while (networkInterfaces.hasMoreElements()) { NetworkInterface next = networkInterfaces.nextElement(); logOutPut("getName获得网络设备名称=" + next.getName()); logOutPut("getDisplayName获得网络设备显示名称=" + next.getDisplayName()); logOutPut("getIndex获得网络接口的索引=" + next.getIndex()); logOutPut("isUp是否已经开启并运行=" + next.isUp()); logOutPut("isBoopback是否为回调接口=" + next.isLoopback());}
发现当开 VPN 时,会检测到 tun0 的网卡,可以用来监测是否开启 VPN。
可以通过 hook java.net.NetworkInterface.getName() 绕过 VPN 检测。
function hook_vpn(){ Java.perform(function() { var NetworkInterface = Java.use("java.net.NetworkInterface"); NetworkInterface.getName.implementation = function() { var name = this.getName(); console.log("name: " + name); if(name == "tun0" || name == "ppp0"){ return "rmnet_data0"; }else { return name; } } })}
如果开启 VPN ,NetworkCapabilities.hasTransport 会返回 true。
所以 hook ,使其返回 false。
var NetworkCapabilities = Java.use("android.net.NetworkCapabilities");NetworkCapabilities.hasTransport.implementation = function () { return false;}
开启 VPN,NetworkCapabilities.hasTransport 会返回含有 VPN 的信息。
分析源码后给出绕过方法:
NetworkCapabilities.appendStringRepresentationOfBitMaskToStringBuilder.implementation = function (sb, bitMask, nameFetcher, separator) { if (bitMask == 18) { console.log("bitMask", bitMask); sb.append("WIFI"); }else { console.log(sb, bitMask); this.appendStringRepresentationOfBitMaskToStringBuilder(sb, bitMask, nameFetcher, separator); }}
这三种方式可以过掉大部分 VPN 检测,如果还有少数 VPN 检测过不掉,可以通过 Objection Hook 的方式,大范围 hook java 库中网络或系统函数,找出检测点,进行绕过。
HTTPS到底是什么
HTTP 作为一种被广泛使用的传输协议,也存在一些的缺点:
1.无状态(可以通过 Cookie 或 Session 解决)
2.明文传输
3.不安全
为了解决 “明文” 和 “不安全” 两个问题,就产生了 HTTPS。HTTPS 不是一种单独的协议,它是由 HTTP + SSL/TLS 组成。
一图讲解单项认证和双向认证
抓包工具抓HTTP包的原理
1.首先抓包工具会提供出代理服务,客户端需要连接该代理。
2.客户端发出 HTTP 请求时,会经过抓包工具的代理,抓包工具将请求的原文进行展示。
3.抓包工具使用该原文将请求发送给服务器。
4.服务器返回结果给抓包工具,抓包工具将返回结果进行展示。
5.抓包工具将服务器返回的结果原样返回给客户端。
抓包工具就相当于个透明的中间人,数据经过的时候它一只手接到数据,然后另一只手把数据传出去。
抓包工具抓HTTPS包的原理
这个时候抓包工具对客户端来说相当于服务器,对服务器来说相当于客户端。在这个传输过程中,客户端会以为它就是目标服务器,服务器也会以为它就是请求发起的客户端。
1.客户端连接抓包工具提供的代理服务。
2.客户端需要安装抓包工具的根证书。
3.客户端发出 HTTPS 请求,抓包工具模拟服务器与客户端进行 TLS 握手交换密钥等流程。
4.抓包工具发送一个 HTTPS 请求给客户端请求的目标服务器,并与目标服务器进行 TLS 握手交换密钥等流程。
5.客户端使用与抓包工具协定好的密钥加密数据后发送给抓包工具。
6.抓包工具使用与客户端协定好的密钥解密数据,并将结果进行展示。
7.抓包工具将解密后的客户端数据,使用与服务器协定好的密钥进行加密后发送给目标服务器。
8.服务器解密数据后,做对应的逻辑处理,然后将返回结果使用与抓包工具协定好的密钥进行加密发送给抓包工具。
9.抓包工具将服务器返回的结果,用与服务器协定好的密钥解密,并将结果进行展示。
10.抓包工具将解密后的服务器返回数据,使用与客户端协定好的密钥进行加密后发送给客户端。
11.客户端解密数据。
服务端校验客户端绕过
App 服务端和客户端是一个团队开发的,因此他们就有这个能力来验证客户端的证书。
在 HTTPS 双向认证的图片中可以看到,客户端需要将自己的证书和公钥发送给服务器,这也就产生了服务器校验客户端。
以 某社交App 为例,如果开启抓包工具,在登录时会报错 400 No required SSL certificate was sent。出现上述字符串的报错,就是证明,服务器在校验客户端。
客户端读取证书常用的代码是:
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException { try { KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); InputStream in = null; keyStore.load(in, password); return keyStore; } catch (IOException e) { throw new AssertionError(e); }}
编写代码进行 hook ,将输入流读取转行成输出流存储,并将秘钥打印出来。
function hook_KeyStore_load() { Java.perform(function () { var ByteString = Java.use("com.android.okhttp.okio.ByteString"); var myArray = new Array(1024); var i = 0 for (i = 0; i < myArray.length; i++) { myArray[i] = 0x0; } var buffer = Java.array('byte', myArray); var StringClass = Java.use("java.lang.String"); var KeyStore = Java.use("java.security.KeyStore"); KeyStore.load.overload('java.security.KeyStore$LoadStoreParameter').implementation = function (arg0) { console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); console.log("KeyStore.load1:", arg0); this.load(arg0); }; KeyStore.load.overload('java.io.InputStream', '[C').implementation = function (arg0, arg1) { console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); console.log("KeyStore.load2:", arg0, arg1 ? StringClass.$new(arg1) : null); if (arg0) { var file = Java.use("java.io.File").$new("/sdcard/Download/" + String(arg0) + ".p12"); var out = Java.use("java.io.FileOutputStream").$new(file); var r; while ((r = arg0.read(buffer)) > 0) { out.write(buffer, 0, r) } console.log("save success!") out.close() } this.load(arg0, arg1); }; console.log("hook_KeyStore_load..."); });} function main() { hook_KeyStore_load()}setImmediate(main);
运行代码后,可以拿到证书和密码。
导出的证书可以用 KeyStore Explorer 进行打开,还可以利用此工具导出其他的证书形式。
导入 charles - Proxy- SSL Proxying Settings 添加客户端证书
启动 ssl 抓包,Charles 将对任意服务器发送该客户端证书。
正向开发中,证书是可以导出的。
// 生成KeyPair public static KeyPair generateKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); return keyPairGenerator.genKeyPair(); } // 合成p12证书 public static void storeP12(PrivateKey pri, String p7, String p12Path, String p12Password) throws Exception { CertificateFactory factory = CertificateFactory.getInstance("X509"); //初始化证书链 X509Certificate p7X509 = (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(p7.getBytes())); Certificate[] chain = new Certificate[]{p7X509}; // 生成一个空的p12证书 KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); ks.load(null, null); // 将服务器返回的证书导入到p12中去 ks.setKeyEntry("client", pri, p12Password.toCharArray(), chain); // 加密保存p12证书 FileOutputStream fOut = new FileOutputStream(p12Path); ks.store(fOut, p12Password.toCharArray()); }
那么也可以通过 hook 拿到证书。
用 js 代码实现上述 Java 代码:
r0capture 中有写。
客户端校验服务端绕过
由于服务器在绝大部分情况下,并不会校验客户端,所以这个问题一般不会存在。
比如服务器一般不会关心客户端到底是 Chrome 浏览器还是 IE 浏览器,是 Android App 还是 iOS App,比如谷歌浏览器就能够模拟移动端。
所以开发者会在客户端上做文章,就是客户端并不会默认信任系统根证书目录中的证书,而是在代码里再加一层校验,这就是证书绑定机制 —— SSL pinning,如果这段代码的校验过不了,那么客户端还是会报证书错误。
You may need to configure your browser or application to trust the Charles Root Certificate. See SSL Proxying in the Help menu.
常用的过检测方案,原理并不算难,就是去 hook 没有混淆的网络框架中,固定的写证书和验证主机的代码点。
绕过方案 1:
Xposed 框架 + JustTrustMe 绕过:
开源项目地址:https://github.com/WooyunDota/DroidSSLUnpinning/tree/master/JustTrustMeSigned
绕过方案 2:
Frida + ObjectionUnpinningPlus
开源项目地址:https://github.com/WooyunDota/DroidSSLUnpinning/tree/master/ObjectionUnpinningPlus
绕过方案 3:
使用 objection 的 ssl pinning diable 功能
objection -g 包名 explore -s "android sslpinning disable"
绕过方案 4:
如果以上方案都不好用,证明采用了代码混淆,因为以上方案是在代码未混淆时的检测点,换了类名和方法名就不管用了。
其实不管混淆与否,要验证证书,肯定要打开证书文件,那么就会调用 File 类的构造方法。
可以对 File 类的构造方法进行全部 hook 。
objection -g 包名 explore -s "android hooking watch class_method java.io.File.$init --dump-args --dump-backtrace --dump-return"
然后在调用栈中找关键词,从而定位到要 hook 的方法。
以一款任务计划 App 为例:
最后说一下 OKHttp3。
目前最常见的网络框架就是 OKHttp3 了,OKHttp3 是一个第三方库,编译的时候会打包到 App 中。
第三方库是可以混淆的,那靠 hook 未混淆的方法是搞不定的。但是 Java 库是不可以混淆的,可以从这方面入手。
解决 OKHttp3 混淆过网络验证的方法常用的有:
1.经过分析 OKHttp3 源码后,OKHttp3 在使用证书时,会计算证书的 pin,这里用到了 hash 算法,因此可以 hook MessageDigest.digest 方法。
2.网络无法访问会抛出错误,也可以直接 hook 错误,执行 App 时打开日志,会看见错误,比如 javax.net.ssl.SSLHandshakeException.$init。
3.OKHttp3 会将 pin 加入到 ArrayList 中,因此可以 Hook ArrayList 的 add 方法。
总结语
本文对整个抓包中遇见的困难,都进行了分析和从原理上给出了绕过方案。
分别有:VPN检测的绕过,客户端验证服务端,服务端验证客户端的绕过,以及 OKHttp3 混淆后如果定位。
原文:https://forum.butian.net/share/2392
黑白之道发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担!
如侵权请私聊我们删文
END