记录一次绕过 Android 服务端的证书校验的详细过程

news/2024/9/24 10:15:04/

前言

本来想挑一个 APP 抓包练练手,因为基础不是很好,想提升实战水平,结果一不小心挑了个不寻常的(对于我而言,大佬勿喷),但因为自己比较固执,不死心,花了几天时间总算搞定了,但还是有些问题,希望有懂行大佬指点一下。

该 APP 使用了 org.conscrypt 库,据了解,这一个封装基于 OpenSSL 的库,在 Github 上也有 1.3k Star 但是网上并没有相关的文章,很少,最终还是通过翻阅源码找到一个关键的 So 层函数作为 Hook 点将私钥导出。

本文章 Hook 脚本均参考了网上的文章以及借助 ChatGPT 所编写,并且经过许多次调试,因为自己不是特别熟悉 frida JS API,还需要多练,多实战,因此写了这篇文章记录自己的过程,以分享自己的思路,给有需要的人一些参考,避免踩坑。

详细过程

设置代理

目前手机已 root,已安装 Burp 证书至系统,当然用 JustTrustMe 也可以干掉客户端的证书校验,比较简单也没检测 VPN,随后开启热点,使用安卓端 proxifier 开启 VPN 让指定 APP 走 Burp

设置 Burp 代理:

图片

指定 APP

图片

发现服务端的证书校验

进入 APP,发现一切正常:

图片

在输入框中随便输入,点击加入,服务端返回 400,并且是 No required SLL certifucate was sent

图片

图片

解包寻找 APK 中的证书

使用 Jadx-gui 对 APK 进行反编译,发现资源部分有几个关键证书:

图片

图片

但这些 grp_sp.bks 、hmsincas.bks 、hmsrootcas.bks 都是 SDK 相关的证书,而 trust.crt 有点可疑,使用 XCA 对 trust.crt 进行查看:

图片

trust.crt 包含多个公钥证书,还有一些 CA 证书,应该是证书信任链,不是客户端证书,因此解包寻找证书无果。

尝试大佬的 frida 自吐脚本和 r0capture

自吐脚本

于是我参考了这位大佬的文章:https://xz.aliyun.com/t/12993,使用其中的 frida 自吐脚本尝试 Hook 看有没有发现,脚本链接:https://github.com/WithSecureLabs/android-keystore-audit/blob/master/frida-scripts/tracer-keystore.js

这里我用的是 JsHook,使用 frida 还是非常方便的:

图片

开启 frida-server 服务及端口转发,复制自吐脚本,并以 spawn 模式启动 App:

# 端口转发
adb forward tcp:28042 tcp:28042# 以spawn启动
frida -H 127.0.0.1:28042 -f <包名> -l hook.js

发现啥也没有:

图片

尝试手动抛出异常打印堆栈:

console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));

图片

也没发现,不过有一行看起来跟证书相关的类,这里先记一下:

ak.im.module.AkeyChatX509PrivateCA.clientBootstrapCertInfo(AkeyChatX509PrivateCA.java:8)

r0capture 通杀脚本

一句话启动:

python r0capture.py -H 127.0.0.1:28042 -f <包名> -v

图片

还是无果,内容还是加密的,证书也未导出,看调用堆栈可知,使用了 org.conscrypt 库,搜了一下发现是 Google 开发的一个基于 OpenSSL 封装的 SSL/TLS 加密库,有点用,先记着。

反编译 Hook 证书

用了大佬们的脚本都无果,其实到这有点想放弃了,不过还没仔细看代码,根据上文的堆栈信息查找与证书有关的类名:

图片

发现有个函数返回了证书,尝试 hook 它试试,jadx-gui 很方便,可以右键直接复制 Hook

代码:

function hook() {Java.perform(function () {let AkeyChatX509PrivateCA = Java.use("ak.im.module.AkeyChatX509PrivateCA");AkeyChatX509PrivateCA["clientBootstrapCertInfo"].implementation = function () {console.log(`AkeyChatX509PrivateCA.clientBootstrapCertInfo is called`);let result = this["clientBootstrapCertInfo"]();console.log(`AkeyChatX509PrivateCA.clientBootstrapCertInfo result=${result}`);return result;};});
}

结果打印了一个证书,从 X509 Extend Usage 中的 Web Client Authentication 看以及关键字眼 android,可以判断这个就是客户端证书:·

图片

将其 dump 出来:

function hook() {Java.perform(function () {let AkeyChatX509PrivateCA = Java.use("ak.im.module.AkeyChatX509PrivateCA");AkeyChatX509PrivateCA["clientBootstrapCertInfo"].implementation = function () {console.log("AkeyChatX509PrivateCA.clientBootstrapCertInfo is called");// 调用原始方法,获取返回的 X509Certificate 对象let result = this.clientBootstrapCertInfo();console.log("AkeyChatX509PrivateCA.clientBootstrapCertInfo result:", result)// 获取 DER 编码的字节数组let cert = result.getEncoded();let bytes = Memory.readByteArray(cert,cert.length)const file = new File("/sdcard/Download/clent.crt", "wb");file.write(bytes);// 返回原始结果return result;};});
}hook();

图片

有个问题,这证书从哪来的? 只能先抛开不谈,现在证书有了,私钥呢?常规 APP 一般不都打包成 bksp12jks 这类的文件然后设置个密码,但这 APP 不走寻常路,于是我尝试了各种 Hook,如下图,涉及私钥和证书的 Java 层方法都尝试过:

图片

以及 javax.net.ssl.* org.conscrypt 库的一些 Java 层的关键方法,要么 Hook 不到(这里可能是 Hook 时机不对或者没调用,很多都没 Hook 到),要么为 null,心态崩了,但我还是不想放弃,于是去找 native 层。

寻找 So 层 Hook 点

下载 org.conscrypt 的源码:https://github.com/google/conscrypt,使用 VScode 打开寻找关键词,尝试 Hook,最终找到了一处:

图片

其中 keyJavaBytes 根据语义以及函数名可以判断这个跟私钥有关,但是我发现这个函数在 Java 层有被调用,尝试 Hook 他们却没反应

图片

先暂时不管,直接尝试 Hook 这个函数,打印 jbyteArray 的内存数据,找到 APP 加载的 So 文件名为:libconscrypt_jni.sofrida 脚本代码如下:

function hookFunc(funcAddr, name) {Interceptor.attach(funcAddr, {onEnter: function (args) {const key = args[1]const dump = hexdump(key, { offset: 0, length: 0x1000,headers: true, ansi: true })console.log(hex);},onLeave: function (retval) {},});
}function hook() {Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {onEnter: function (args) {var pathptr = args[0];if (pathptr !== undefined && pathptr != null) {var path = ptr(pathptr).readCString();if (path.indexOf("libconscrypt_jni.so") !== -1) {console.log("dlopen: " + path);this.path = path;}}},onLeave: function (retval) {if (this.path !== undefined) {// 获取模块的 base 地址var baseAddress = Module.findBaseAddress(this.path);if (baseAddress !== null) {console.log("Module base address: " + baseAddress);// 遍历导出表console.log("Listing exports in " + this.path);Module.enumerateExports(this.path, {onMatch: function (symbol) {if (symbol.name.indexOf("EVP_parse_private_key") !== -1) {console.log(symbol.name + "---" + symbol.address.toString());hookFunc(symbol.address, symbol.name)}},onComplete: function () {}});}}}});
}hook();

图片

这里我尝试用很多中办法都没法得到 jbyteArray 的长度,调用 JNI 的 GetArrayLength 方法会导致闪退,具体原因未知,只能预先 dump 大小为 0x1000,有需要也可以更大,将其 dump 至 /sdcard/Download 下,替换上方的 hook 函数:

function hook(funcAddr, name) {Interceptor.attach(funcAddr, {onEnter: function (args) {console.log(name + " enter");const bytes = Memory.readByteArray(args[1], 0x1000);const file = new File("/sdcard/Download/private.pem", "wb");file.write(bytes);},onLeave: function (retval) {},});
}

私钥如下:

图片

导入 XCA,查看之前的证书,可以发现私钥对应上了:

图片

使用 XCA 直接导出为 p12

图片

导入至 burp

图片

发包,成功:

图片

不知道这种方法能不能对使用了 org.conscrypt 库的 App 通杀,后续研究一下。

参考链接

https://xz.aliyun.com/t/12993

https://bbs.kanxue.com/thread-280089.htm

https://bbs.kanxue.com/thread-281584.htm


http://www.ppmy.cn/news/1508959.html

相关文章

网站地图制作有什么好处?Sitemap地图如何制作?

网站地图又叫站点地图&#xff0c;我们可以理解为一个包含网站中所有页面链接的容器&#xff0c;主要是帮助搜索引擎快速抓取网站的中的所有页面。很多新人可能对地图了解还不够深入&#xff0c;今天小编详细讲下网站地图制作的好处&#xff0c;还有Sitemap地图如何制作&#x…

Qt .qm文件的加载和使用

在Qt应用程序中&#xff0c;你可以使用QTranslator类来加载和使用.qm文件。以下是一个简单的示例&#xff1a; #include <QCoreApplication> #include <QTranslator> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, ar…

【WebRTC指南】数据通道

数据通道 WebRTC 标准还涵盖用于通过 RTCPeerConnection 发送任意数据的 API。可通过对 RTCPeerConnection 对象调用 createDataChannel() 来完成此操作,该方法会返回 RTCDataChannel 对象。 const peerConnection = new RTCPeerConnection(configuration); const dataChann…

C++结构体指针强制转换以处理电力系统IEC103报文

前言 最近依旧是开发规约解析工具的103篇&#xff0c;已经完成了通用分类服务部分的解析&#xff0c;现在着手开始搞扰动数据传输&#xff0c;也就是故障录波的传输。 在103故障录波&#xff08;扰动数据&#xff09;的报文中&#xff0c;数据是一个数据集一个数据集地存放&a…

WiFi模块无线通信交互,乐鑫ESP32物联网方案,启明云端乐鑫代理商

随着物联网(IoT)技术的飞速发展&#xff0c;我们正步入一个智能化、互联化的世界。在这一进程中&#xff0c;无线WiFi模块作为连接物理世界与数字世界的桥梁&#xff0c;扮演着至关重要的角色。 WiFi模块是一种基于WiFi协议的无线模块&#xff0c;它可以实现设备之间的无线通信…

2024年翻译工具新风尚:实时翻译与精准度并进

语言交流的障碍随着全球化的不断深入日益成为连接不同文化和国家的挑战。然而&#xff0c;在科技日新月异的今天&#xff0c;类似谷歌翻译这样的工具正在高速发展这。这次我们来一起探讨深受用户喜欢的翻译工具有哪些。 1.福昕在线翻译 链接直达&#xff1a;https://fanyi.pd…

Unity数据持久化 之 Json序列化与反序列化

语法规则可以看这篇文章&#xff1a;Unity数据持久化 之 Json 语法速通-CSDN博客 Q:Unity是通过什么来对Json文件进行处理的&#xff1f; A:JsonUtility&#xff1a;Unity 提供了 JsonUtility 类&#xff0c;用于将对象序列化为 JSON 字符串或将 JSON 字符串反序列化为对象。…

初识C++ · C++11(3)

前言&#xff1a; 本文介绍的是包装器以及线程库的简单了解&#xff0c;但是呢&#xff0c;线程是基于对Linux有一定的了解&#xff0c;所以本文就是简单介绍一下&#xff0c;介绍完包装器以及线程库的简单理解之后C11的特性就到此为止&#xff0c;当然C11远不止于此&#xff…