使用 Caido 和 Frida 拦截移动端应用程序流量
文章描述了一位安全研究员使用 Caido HTTP 代理工具测试移动应用程序流量的过程。通过配置实体设备、ADB 和 Wi-Fi 代理设置,并解决证书绑定问题(使用 Frida 绕过),成功拦截并分析 IoT 设备的应用程序流量。 2025-7-13 04:3:7 Author: www.freebuf.com(查看原文) 阅读量:24 收藏

作为一名安全研究员,我经常需要查看某些设备和应用程序的 HTTP 通信,以便进行安全测试。为此,HTTP 代理是一种不可或缺的工具,它允许安全测试人员读取、修改和重新发送各种 HTTP 请求。Caido 是市面上最新的 HTTP 代理工具,拥有一些打动我的关键特性。它是用 Rust 编写的,从底层设计上就考虑了不占用大量内存和计算资源。此外,它在 Linux 上运行良好,这对我来说非常重要。因此,在本文中,我们将动手测试一下,使用 Caido 拦截我们的 IoT 设备移动应用程序的流量。

有人会问,为什么我在测试时使用实体设备而不是安卓模拟器。虽然模拟器很好用,但在这个案例中,我需要使用能够通过 BLE 与 IoT 设备通信的真实设备。

我们需要通过 USB 数据线将安卓设备连接到 Linux 电脑,并在手机上启用开发者模式。同时需要确保在开发者选项中开启 ADB(Android 调试桥)。另外一个实用的小技巧是,在开发者选项中开启 USB 连接时保持屏幕常亮,以防止设备休眠。

要检查是否成功通过 ADB 连接到手机,可以运行以下命令:

adb devicesList of devices attached25281JEGR02143    device

同样值得注意的是,本文中的所有操作都是在一台未 Root 的安卓设备上完成的。

在下载并安装 Caido 之后,我们需要创建一个实例。我们将把实例配置为在本地运行,监听 localhost 和 TCP 端口 8080。

图片

然后点击我们刚刚创建的新实例上的 Start 按钮启动它。

图片

之后,只需创建并选择一个项目。

图片

最后,我们可以点击左侧的 HTTP History 选项卡,此时会提示我们尚未拦截到任何流量。

图片

现在我们需要将移动应用程序下载到电脑上,然后安装到设备上。你当然可以直接从 Google Play 商店下载应用程序,但我更喜欢从第三方网站下载,例如 apkpure。

首先,让我们下载 iHealth MyVitals 应用程序,并将其保存为名为 ihealth-myvitals-4.8.0.apk 的文件。

接下来,我们可以使用 adb 将该应用程序安装到移动设备上:

adb install ihealth-myvitals-4.8.0

现在我们需要将 Wi-Fi 网络配置为使用手动 HTTP 代理。为此,在 Wi-Fi 网络详情页面点击“修改”按钮,它是右上角的铅笔图标。

图片

在接下来的页面中,在“代理”下拉菜单中选择“手动”,然后将“代理主机名”填写为 127.0.0.1,将“代理端口”填写为 8080

图片

等等!为什么我们把代理主机名设置为 127.0.0.1?Caido 可不是在手机上运行的!这是对的,但我们可以使用 adb 的一个很酷的功能,将手机上的 localhost:8080 反向代理到电脑上的 8080 端口。

接下来,我们需要将 Caido 的 CA 证书添加到手机的信任存储中。首先,在手机浏览器中访问 http://localhost:8080/ca.crt,下载 CA 证书。在新版安卓系统中,不能直接从浏览器页面安装 CA 证书,因为系统会认为这是安全风险。你需要进入手机的“加密与凭据”设置,选择“安装证书”,然后按照提示安装并信任 Caido 的 CA 证书,该证书应该在你的手机下载文件夹中。

图片

adb reverse tcp:8080 tcp:8080

现在,当安卓设备尝试将流量发送到 localhost:8080 时,它会通过 USB 数据线将流量转发到我们电脑上的 8080 端口,也就是 Caido 正在运行的地方!

现在一切都已配置完成,按理说我们应该可以通过 Caido 代理接收到流量,但在启动 iHealth 移动应用程序时,出现了问题。

图片

让我们检查一下安卓设备的应用程序日志,看看可能出了什么问题:

adb logcat[ ------- TRUNCATED ------- ]08-23 00:06:56.005  9896 10088 W System.err: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.[ ------- TRUNCATED ------- ]

找不到认证路径的信任锚。 这意味着尽管安卓操作系统信任我们的根 CA 证书,但应用程序出于某种原因并不信任。这是因为安卓应用实现了 CA 证书绑定(Certificate Pinning)。CA 证书绑定是 TLS 客户端对服务器证书进行更严格验证的过程,它通过使用更小范围的根 CA,甚至直接将服务器证书与存储在应用代码中的预期证书进行比对,来验证其真实性。

为了绕过证书绑定,我们将使用 Frida。Frida 是一个动态插桩工具包。我们的最终目标是运行来自 Frida 代码分享平台 的脚本,修改正在运行的 iHealth 应用,使其不执行证书绑定。如前所述,我的所有分析都是在未 Root 的安卓手机上进行的。如果手机已 Root,我们可以使用 Frida 附加到手机上运行的进程。相反,我们需要将 Frida 的 Gadget 库 添加到 iHealth 移动应用中,并安装该修改后的应用到设备上。

我经常执行这些修改步骤,因此在这里将其全部记录在一个 GitHub 仓库 中。

首先,我们需要使用 apktool 解包 APK 文件:

apktool d -o unpack ihealth-myvitals-4.8.0.apk

然后,我们需要将 Frida 的 Gadget 库下载到正确的原生库目录下:

<application android:allowBackup="false" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:extractNativeLibs="false" android:icon="@mipmap/mv_icon_app_launcher" android:label="@string/app_name" android:largeHeap="true" android:name="com.ihealth.base.MyApplication" android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/mv_icon_app_launcher" android:supportsRtl="true" android:theme="@style/Pro.Theme.NoActionBar" android:usesCleartextTraffic="true">

android:extractNativeLibs=“false” 注意 android:extractNativeLibs 被设置为 false,我们需要将其改为 true。 接下来,我们要查找带有 LAUNCHER 分类元素的 Activity 名称。

<activity android:exported="true" android:launchMode="standard" android:name="com.ihealth.business.common.welcome.WelcomeActivity"

这让我们得到了启动 Activity 名称:com.ihealth.business.common.welcome.WelcomeActivity。这实际上是安卓应用程序的入口点。现在,让我们在解包后的应用程序目录中查找该 WelcomeActivity

grep -r 'com.ihealth.business.common.welcome.WelcomeActivity'smali_classes2/com/ihealth/broadcastReceiver/LanguageReceiver.smali:    const-class v0, Lcom/ihealth/business/common/welcome/WelcomeActivity;smali_classes2/com/ihealth/base/BaseActivity.smali:    const-class v1, Lcom/ihealth/business/common/welcome/WelcomeActivity;smali_classes2/com/ihealth/business/common/guide/device/bpm1/e.smali:    check-cast v1, Lcom/ihealth/business/common/welcome/WelcomeActivity;smali_classes2/com/ihealth/business/common/guide/device/bpm1/e.smali:    sget v3, Lcom/ihealth/business/common/welcome/WelcomeActivity;->e:Ismali_classes2/com/ihealth/business/common/guide/device/bpm1/e.smali:    iget-object v3, v1, Lcom/ihealth/business/common/welcome/WelcomeActivity;->a:Ljava/lang/String;smali_classes2/com/ihealth/business/common/guide/device/bpm1/e.smali:    iget-object v5, v1, Lcom/ihealth/business/common/welcome/WelcomeActivity;->b:Ljava/lang/String;smali_classes2/com/ihealth/business/common/guide/device/bpm1/e.smali:    iget-object v6, v1, Lcom/ihealth/business/common/welcome/WelcomeActivity;->c:Ljava/lang/String;smali_classes2/com/ihealth/business/common/guide/device/bpm1/e.smali:    iget v7, v1, Lcom/ihealth/business/common/welcome/WelcomeActivity;->d:Ismali_classes2/com/ihealth/business/common/trampoline/TrampolineActivity.smali:    const-class v2, Lcom/ihealth/business/common/welcome/WelcomeActivity;smali_classes2/com/ihealth/business/common/welcome/WelcomeActivity.smali:.class public Lcom/ihealth/business/common/welcome/WelcomeActivity;Ssmali_classes2/com/ihealth/business/common/welcome/WelcomeActivity.smali:    iput-object v2, p0, Lcom/ihealth/business/common/welcome/WelcomeActivity;->a:Ljava/lang/String;smali_classes2/com/ihealth/business/common/welcome/WelcomeActivity.smali:    iput-object v2, p0, Lcom/ihealth/business/common/welcome/WelcomeActivity;->c:Ljava/lang/String;smali_classes2/com/ihealth/business/common/welcome/WelcomeActivity.smali:    iput-object v2, p0, Lcom/ihealth/business/common/welcome/WelcomeActivity;->b:Ljava/lang/String;smali_classes2/com/ihealth/business/common/welcome/WelcomeActivity.smali:    iput v0, p0, Lcom/ihealth/business/common/welcome/WelcomeActivity;->d:Ismali_classes2/com/ihealth/business/common/welcome/WelcomeActivity.smali:    const-class v4, Lcom/ihealth/business/common/welcome/WelcomeActivity;AndroidManifest.xml:        <activity android:exported="true" android:launchMode="standard" android:name="com.ihealth.business.common.welcome.WelcomeActivity" android:screenOrientation="portrait" android:theme="@style/AppStart">grep: build/apk/classes2.dex: binary file matches

虽然有一些误报,但 smali_classes2/com/ihealth/business/common/welcome/WelcomeActivity.smali 这个文件很可能定义了 WelcomeActivity 类。我们需要修改该类,添加一段代码,使应用程序一启动就调用我们的 Frida Gadget 库。 我们在该文件中搜索 .method public constructor <init>()V,以找到需要修改的方法:

# direct methods.method public constructor <init>()V    .locals 0    .line 1    invoke-direct {p0}, Lcom/trello/rxlifecycle3/components/support/RxAppCompatActivity;-><init>()V    .line 2    .line 3    .line 4    return-void.end method

我们将把该文件修改成如下所示的内容:

# direct methods.method public constructor <init>()V    .locals 1    const-string v0, "frida-gadget"    invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V    .line 1    invoke-direct {p0}, Lcom/trello/rxlifecycle3/components/support/RxAppCompatActivity;-><init>()V    .line 2    .line 3    .line 4    return-void.end method

注意,我们将本地变量的数量从 0 改为 1,并添加了以下两行代码:

const-string v0, "frida-gadget"invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

这段代码会执行我们的 Frida Gadget 库。接下来,我们回到解包应用的位置,准备重新打包 APK 文件。 运行以下命令重新打包 APK 文件:

apktool b --use-aapt2 -o mod-app.apk unpack/

然后对 APK 文件进行 ZIP 对齐:

zipalign 4 mod-app.apk final-mod-app.apk

所有 APK 文件都需要用密钥签名。运行以下命令创建一个 Java 密钥库(JKS):

keytool -genkey -v -keystore custom.keystore -alias mykeyaliasname -keyalg RSA -keysize 2048 -validity 10000

接着使用新创建的 JKS 对 APK 文件进行签名:

apksigner sign -ks custom.keystore final-mod-app.apk

接下来,手动卸载原版 iHealth 移动应用,然后使用 adb 安装我们刚刚修改的应用:

adb install final-mod-app.apk

现在,当我们启动修改后的安卓应用时,它会在启动屏幕暂停:

图片

应用程序已成功执行 Frida Gadget 库,并暂停等待通过 ADB 连接的 Frida。

接下来,我们将运行一个 Frida Codeshare 脚本以绕过证书绑定:

frida -U gadget --codeshare sowdust/universal-android-ssl-pinning-bypass-2

现在应用程序恢复运行,我们可以在 Caido 中看到数据流量了!

图片

这非常棒,但有大量发送到各种谷歌服务的无关数据,我们不需要关注。让我们把它过滤掉。 在 Caido 中,你可以创建范围(scopes),用来定义哪些请求应该显示在 HTTP History 窗口中。 我将创建一个名为 ihealth 的范围,范围内包含的条目为 *.ihealthlabs.com

图片

现在,当我们返回到 HTTP History 窗口时,可以在屏幕左上角选择我们创建的 ihealth 范围。这样我们就只会看到我们想看到的内容!

图片

现在我可以使用该移动应用程序来发现所有存在的 API 端点,从而寻找移动端 API 的漏洞,并全面了解该物联网系统。


文章来源: https://www.freebuf.com/articles/web/439073.html
如有侵权请联系:admin#unsafe.sh