找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1042

积分

0

好友

152

主题
发表于 3 天前 | 查看: 21| 回复: 0

随着移动安全攻防环境的不断升级,App渗透测试已成为网络安全领域的重要环节。本次我们将分享一次针对Flutter开发的Android应用的实战抓包过程。

应用初探与问题定位

在一次移动端渗透测试任务中,测试人员在常规抓包环节遇到了阻碍。应用功能正常,但在设备上设置了HTTP代理或VPN后,不仅无法抓到任何数据包,应用界面也变为一片空白,所有功能均无法访问。

初步判断,这很可能是因为应用进行了代理检测或VPN检测。然而,应用并未给出任何明确的提示信息,这促使我们进一步分析应用本身。

通过查看APK文件结构,在 AndroidManifest.xml 配置文件中发现了关键线索:io.flutter.embedding.android.FlutterActivity。这是Flutter 2.0的标准入口Activity,表明此应用很可能基于Flutter框架开发。

进一步的验证方法是检查应用的 lib 目录。如果存在 libapp.solibflutter.so 这两个核心库文件,即可基本确认其为Flutter应用。经确认,目标应用符合此特征。

Flutter应用抓包难点分析

对于Flutter开发的App,常规代理工具(如Burp Suite)抓不到HTTPS包的原因通常有以下几点:

  1. 不走系统代理:Flutter引擎的网络请求可能默认不遵循系统的代理设置。
  2. 不信任系统证书:即使流量经过代理,Flutter可能也不信任用户手动安装到系统根证书目录的Burp等代理CA证书。
  3. 内置SSL Pinning:证书校验逻辑可能被编译到原生库(.so文件)中,导致基于Xposed框架的通用绕过模块(如JustTrustMe、SSLUnpinning)失效。

因此,针对Flutter应用的抓包,通常需要借助Frida这样的动态Android Hook框架,从Java层和Native(原生)层同时绕过其证书校验机制。

使用Frida绕过多层SSL Pinning

以下Frida脚本尝试从多个层面(包括OkHttp、系统TrustManager、WebView以及原生BoringSSL/OpenSSL)绕过SSL Pinning,适用于大多数情况。

/* flutter_full_bypass.js
   Multi-layer SSL pinning bypass for Flutter apps:
   - Java layer: OkHttp CertificatePinner, TrustManager, HttpsURLConnection, WebView
   - Native layer: try to override common BoringSSL/OpenSSL verify functions (best-effort)
   Usage:
     frida -U -f com.package.name -l flutter_full_bypass.js --no-pause*/

setTimeout(function () {
    Java.perform(function () {
        console.log("***** Frida: Java layer hooking start *****");

        // ========== OkHttp3 CertificatePinner ==========
        try {
            var CertPinner = Java.use("okhttp3.CertificatePinner");
            CertPinner.check.overload('java.lang.String', 'java.util.List').implementation = function (host, pins) {
                console.log("[bypass] okhttp3.CertificatePinner.check => SKIP for " + host);
                return;
            };
            console.log("[ok] hooked okhttp3.CertificatePinner");
        } catch (e) {
            console.log("[no] okhttp3.CertificatePinner: " + e.message);
        }

        // ========== OkHttp (old) ==========
        try {
            var CertPinnerOld = Java.use("com.squareup.okhttp.CertificatePinner");
            CertPinnerOld.check.overload('java.lang.String', 'java.util.List').implementation = function (host, pins) {
                console.log("[bypass] okhttp.CertificatePinner.check => SKIP for " + host);
                return;
            };
            console.log("[ok] hooked com.squareup.okhttp.CertificatePinner");
        } catch (e) {
            console.log("[no] com.squareup.okhttp.CertificatePinner: " + e.message);
        }

        // ========== X509TrustManager / SSLContext ==========
        try {
            var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
            var SSLContext = Java.use("javax.net.ssl.SSLContext");
            var TrustManagerImpl = Java.registerClass({
                name: "org.frida.TrustManagerImpl",
                implements: [X509TrustManager],
                methods: {
                    checkClientTrusted: function (chain, authType) {
                        // no-op
                    },
                    checkServerTrusted: function (chain, authType) {
                        console.log("[bypass] TrustManagerImpl.checkServerTrusted");
                    },
                    getAcceptedIssuers: function () {
                        return [];
                    }
                }
            });
            SSLContext.init.overload(
                '[Ljavax.net.ssl.KeyManager;',
                '[Ljavax.net.ssl.TrustManager;',
                'java.security.SecureRandom'
            ).implementation = function (km, tm, sr) {
                console.log("[bypass] SSLContext.init -> replace TrustManagers");
                var tmArr = Java.array("javax.net.ssl.TrustManager", [TrustManagerImpl.$new()]);
                return this.init(km, tmArr, sr);
            };
            console.log("[ok] hooked SSLContext.init and installed TrustManagerImpl");
        } catch (e) {
            console.log("[no] SSLContext / TrustManager hook: " + e.message);
        }

        // ========== HttpsURLConnection ==========
        try {
            var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
            HttpsURLConnection.setDefaultHostnameVerifier.implementation = function (v) {
                console.log("[bypass] HttpsURLConnection.setDefaultHostnameVerifier called -> ignored");
                return;
            };
            console.log("[ok] hooked HttpsURLConnection.setDefaultHostnameVerifier");
        } catch (e) {
            console.log("[no] HttpsURLConnection hook: " + e.message);
        }

        // ========== WebView SSL ==========
        try {
            var WebViewClient = Java.use("android.webkit.WebViewClient");
            WebViewClient.onReceivedSslError.implementation = function (view, handler, error) {
                console.log("[bypass] WebView SSL error ignored");
                handler.proceed();
            };
            console.log("[ok] hooked WebViewClient.onReceivedSslError");
        } catch (e) {
            console.log("[no] WebViewClient hook: " + e.message);
        }

        // ========== okhttp3.OkHttpClient.Builder.sslSocketFactory ==========
        try {
            var OkBuilder = Java.use("okhttp3.OkHttpClient$Builder");
            OkBuilder.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager').implementation = function (sslsf, trustManager) {
                console.log("[bypass] OkHttpClient.Builder.sslSocketFactory called -> pass-through");
                return this.sslSocketFactory(sslsf, trustManager);
            };
            console.log("[ok] hooked OkHttpClient.Builder.sslSocketFactory (if present)");
        } catch (e) {
            console.log("[no] OkHttp builder hook: " + e.message);
        }

        console.log("***** Frida: Java layer hooking finished *****");
    });

    // 延迟后开始Native层Hook
    setTimeout(function () {
        try {
            console.log("***** native layer hooking start *****");
            // 尝试Hook常见的SSL验证函数(尽力而为,效果因设备/Android版本而异)
            var nativeCandidates = [
                "SSL_get_verify_result",
                "SSL_get_peer_certificate",
                "SSL_get_peer_cert_chain",
                "SSL_CTX_set_verify",
                "X509_verify_cert",
                "SSL_verify_client_post_handshake"
            ];
            nativeCandidates.forEach(function (sym) {
                try {
                    var addr = Module.findExportByName(null, sym);
                    if (addr) {
                        Interceptor.attach(addr, {
                            onEnter: function (args) {
                                console.log("[native] hit " + sym + " at " + addr);
                            },
                            onLeave: function (retval) {
                                // 如果函数返回整数类型的验证结果,强制改为成功 (0)
                                try {
                                    if (retval !== undefined) {
                                        retval.replace(0);
                                    }
                                } catch (e) {}
                            }
                        });
                        console.log("[ok] attached to native " + sym);
                    }
                } catch (e) {}
            });

            // Hook常见的BoringSSL函数名(尽力而为)
            var tryNames = ["SSL_read", "SSL_write", "SSL_connect", "SSL_accept"];
            tryNames.forEach(function (n) {
                try {
                    var a = Module.findExportByName(null, n);
                    if (a) {
                        Interceptor.attach(a, {
                            onEnter: function (args) {
                                // 轻量级日志,避免过多输出
                            },
                            onLeave: function (ret) {
                            }
                        });
                        console.log("[ok] attached to " + n);
                    }
                } catch (e) {}
            });
            console.log("***** native layer hooking finished *****");
        } catch (err) {
            console.log("native hooks failed: " + err);
        }
    }, 500);
}, 1000);

实战抓包与测试结果

目标应用未对Frida进行检测,因此可以顺利注入上述脚本。在Frida脚本成功运行后,同时开启设备的Wi-Fi代理并指向抓包工具(如Burp Suite)。

此时,应用的所有网络流量已被成功捕获,之前空白的功能页面也恢复了正常。通过对拦截到的HTTP/HTTPS请求进行分析,发现其请求参数并未进行额外的加密处理,这使得后续的漏洞挖掘工作得以顺利进行,并最终成功发现了高危安全漏洞。

通过本次Flutter App渗透测试实战可以看出,对于采用了强证书校验机制的应用,结合Frida框架进行动态Hook是突破SSL Pinning防御的有效手段。




上一篇:AgentThread实战:基于微软Agent Framework构建多轮对话AI智能体
下一篇:Python轻量级本地文件搜索库Whose使用指南与场景解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 16:30 , Processed in 0.134436 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表