记一次帮丈母娘破解APP,满满的全是思路

admin 2025年6月3日10:07:01评论28 views字数 10295阅读34分19秒阅读模式

记一次帮丈母娘破解APP,满满的全是思路

事情是这样的,家里人从网上买了一个定位器,买之前也没问客服,结果到手之后一看竟然要收费才能使用,由于本来没多少钱后续就没退,然后我听了之后来了兴致,我想着能不能有什么漏洞能白嫖,于是有了本文,虽然最后解决也不难,不过我觉得最重要是满满的全是思路。

主要是下面两个东西

  • 定位器,里面有一张它出厂带的卡
  • 查询位置所用的app一个

定位器这东西,我是没有lot经验所以就先不用看了,直接看安卓app

0x01 初步研究app

拿出我的测试机,进入到app中会让你登录,登录的账号就是定位器的初始id,密码是123,进入后由于没有花钱,会出现一个弹窗,仔细看下图发现在弹窗后边我的设备显示在线,那是不是就意味着我只要让这个倒霉弹窗不再出现,我就可以点到后边我的设备,然后进行相关操作,于是想着关弹窗这种小活这不伸手就来,没想到这么简单,想想还有点小激动

记一次帮丈母娘破解APP,满满的全是思路

于是有了第一个思路,想着先用开发助手定位弹窗组件,然后用算法助手pro直接拦截掉,但是当我定位组件的时候发现这个组件没ID,如图这个id名字什么的竟然是空的,神奇。

记一次帮丈母娘破解APP,满满的全是思路

突然灵光一现,会不会是它不是个弹窗,由于后边的背景色是灰色,想着难不成直接弄了个Activity放到了前面

正想着,于是就有了第二个想法,直接用MT定位Activity,然后直接拦截这个Activity,太聪明了,没想到这么简单,想想还有点小激动

但是当我定位的时候发现,只有一个MainActivity这说明我的想法并不成立,难不成它还是个组件,只不过没名字

记一次帮丈母娘破解APP,满满的全是思路

算了,正想着着实有点头痛,干脆直接看它代码吧,于是第三个思路,通过待激活关键字定位到相关代码,于是我下意识的拿出了我的脱壳机,不过没想到这家伙竟然没壳

记一次帮丈母娘破解APP,满满的全是思路

正想着原来是我高看它了,那不分分钟手到擒来

直接拖到jadx搜索待激活,结果竟然没有

记一次帮丈母娘破解APP,满满的全是思路

搜索unicode编码也没有

记一次帮丈母娘破解APP,满满的全是思路

这让我百思不得其解,再尝试搜索了其余的几个关键字没有结果后,没有办法的我没了办法,于是想着那干脆从流量层面看看吧

0x02 流量侧心酸突破历程

直接设置系统代理到我的Burp,然后打开发现

记一次帮丈母娘破解APP,满满的全是思路

666,竟然还有代理检测,于是直接上Postern,走vpn代理到茶杯狐Charles的socks端口,结果还是不行

记一次帮丈母娘破解APP,满满的全是思路

没办法了,只好拿出了神器frida

adb shellsucd /data/local/tmp/#测试机,启动frida服务./frida-server-16.0.11-android-arm64

在本地新建了一个过代理检测的通杀脚本(来源:阿呆攻防)

functionwifi1_proxy_bypass(){    Java.perform(()=>{        var systemCls = Java.use('java.lang.System');        systemCls.getProperty.overload('java.lang.String').implementation = function (val{            var ret = this.getProperty(val);            if (val == "http.proxyHost") {                return""            }            if (val == "http.proxyPort") {                return"-1"// 这里改""/"0"/"-1",我这里留的-1是之前金融项目好几家都是-1            }            return ret        }    })}functionwifi2_proxy_bypass(){    Java.perform(function ({        var ConnectivityManager = Java.use('android.net.ConnectivityManager');        ConnectivityManager.getLinkProperties.implementation = function (network{            var linkProperties = this.getLinkProperties(network);            if (linkProperties) {                var ProxyInfo = Java.use('android.net.ProxyInfo');                var proxyInfo = ProxyInfo.$new(nullnull0);                 linkProperties.setHttpProxy(proxyInfo);             }            return linkProperties;        };    });}functionvpn1_bypass(){    Java.perform(()=>{        var ConnectivityManager = Java.use('android.net.ConnectivityManager');        ConnectivityManager.getNetworkInfo.overload('int').implementation = function (networkType{            var result = this.getNetworkInfo(networkType);            if (networkType === ConnectivityManager.TYPE_VPN.value) {                returnnull;            }            return result;        };    })}functionvpn2_bypass({    Java.perform(() => {        // 获取 NetworkCapabilities 类var NetworkCapabilities = Java.use('android.net.NetworkCapabilities');        // Hook hasTransport 方法        NetworkCapabilities.hasTransport.overload('int').implementation = function (transportType{            // 如果检测到 TRANSPORT_VPN,返回 falseif (transportType === NetworkCapabilities.TRANSPORT_VPN.value) {                console.log("[*] VPN 检测被绕过");                returnfalse;            }            // 否则调用原始方法returnthis.hasTransport(transportType);        };        console.log("[*] NetworkCapabilities.hasTransport 已 Hook");    });}functionbypass_proxy_main(){    wifi1_proxy_bypass()     vpn1_bypass()}setImmediate(bypass_proxy_main)

然后直接frida进行hook

.frida.exe -U  [APP进程] -l .hook.js

发现正常请求走的通了,不过这个时候我的茶杯狐还没有抓取https流量,也就是说https的流量会经过茶杯狐,不过没有抓取ssl会导致它并不会利用茶杯狐的ssl证书做中转,我也就看不到对应的http报文的明文,看到的都是密文,不过好在软件使用的服务器的域名是知道了

记一次帮丈母娘破解APP,满满的全是思路

域名到手

记一次帮丈母娘破解APP,满满的全是思路

既然如此直接装好证书并信任,抓ssl,不点开没事,一抓https又完犊子了,这个时候我尝试了别的https请求是能抓到的,就这个app抓不到

记一次帮丈母娘破解APP,满满的全是思路

666,竟然有校验,没关系,盲猜连壳都没有的app,撑死一个单向证书校验,把LSBJustTrustMe一开,心想这不就成了,没想到这么简单

记一次帮丈母娘破解APP,满满的全是思路

发现还是不行

难不成双向证书校验?由于我知道域名了,直接访问对方域名,发现提示400,不过报错信息跟正常的双向证书不一样,我想着一定是对方伪装了一下,兵法有云,实则虚之虚则实之,小小诡计岂能瞒得过我,双向证书校验,绝对双向证书校验

记一次帮丈母娘破解APP,满满的全是思路

然后我尝试搜了一下apk解包后有没有常见后缀p12cerjks等等的文件,发现并没有,按理说不能,这时想到难不成是伪装成了某个png文件,然后使用的时候在代码中又还原了出来?于是在代码中一顿找最后也没有。

思路转变一下,既然找不到证书,那么它加载本地证书的时候肯定是要读取本地证书文件的

既然如此废话不多说直接上r0capture,具体使用方法不多赘述,直接看它项目首页介绍https://github.com/r0ysue/r0capture

恭喜你猜对了又是0收获,既然如此我又用objection尝试HOOK了java.io.File.$init想着你总要读文件的吧

objection -g [app项目名] explore --startup-command "android hooking watch class_method java.io.File.$init  --dump-args

结果如你所想又是毫无收获,我彻底麻了,我原本以为一个连壳都不上的app能有多难搞,没想到这么强,于是跟朋友调侃了一下,就像斗地主一样,人家牌太好了直接明牌跟你玩不行吗

0x03 流量侧成功突破

实在没得办法了,于是又冒出一个想法既然java代码搜不到,是不是在so层里面,说着看了眼lib目录,正想着这么多我选那个先分析好呢,突然看到libflutter.so等会,我记得flutter不是个语言吗?

记一次帮丈母娘破解APP,满满的全是思路

于是直接开启上网冲浪模式,一顿冲浪下来,ok有解了

先说一下flutter,Flutter 是一个由 Google 开发的 开源 UI 框架,用于构建跨平台应用,也就是说,用一套代码可以同时生成 iOS、Android、Web 和桌面(Windows、macOS、Linux) 应用。

Flutter使用Dart编写,因此它不会使用系统CA存储,Dart使用编译到应用程序中的CA列表,Dart在Android上不支持代理,所以这就是为什么一开始使用系统代理没有生效的原因

当我们的应用存在libflutter.so的时候,其实就可以判断大概率为flutter的

还有一种方法是通过flutter的日志,如果有输出不仅可以判断出app是flutter写的,还看到对应的日志

adb shellsulogcat |grep flutter

可以看到这个应用的所有请求和响应都在日志中

记一次帮丈母娘破解APP,满满的全是思路

下面这个函数是flutter的证书校验

staticboolssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session,                                                      SSL_HANDSHAKE *hs,                                                      uint8_t *out_alert){  *out_alert = SSL_AD_INTERNAL_ERROR;  STACK_OF(X509) *const cert_chain = session->x509_chain;  if (cert_chain == nullptr || sk_X509_num(cert_chain) == 0) {    returnfalse;  }  SSL *const ssl = hs->ssl;  SSL_CTX *ssl_ctx = ssl->ctx.get();  X509_STORE *verify_store = ssl_ctx->cert_store;  if (hs->config->cert->verify_store != nullptr) {    verify_store = hs->config->cert->verify_store;  }  X509 *leaf = sk_X509_value(cert_chain, 0);  constchar *name;  size_t name_len;  SSL_get0_ech_name_override(ssl, &name, &name_len);  UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new());  if (!ctx ||      !X509_STORE_CTX_init(ctx.get(), verify_store, leaf, cert_chain) ||      !X509_STORE_CTX_set_ex_data(ctx.get(),                                  SSL_get_ex_data_X509_STORE_CTX_idx(), ssl) ||      // We need to inherit the verify parameters. These can be determined by// the context: if its a server it will verify SSL client certificates or// vice versa.      !X509_STORE_CTX_set_default(ctx.get(),                                  ssl->server ? "ssl_client" : "ssl_server") ||      // Anything non-default in "param" should overwrite anything in the ctx.      !X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()),                              hs->config->param) ||      // ClientHelloOuter connections use a different name.      (name_len != 0 &&       !X509_VERIFY_PARAM_set1_host(X509_STORE_CTX_get0_param(ctx.get()), name,                                    name_len))) {    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);    returnfalse;  } if (hs->config->verify_callback) {    X509_STORE_CTX_set_verify_cb(ctx.get(), hs->config->verify_callback);  }  int verify_ret;  if (ssl_ctx->app_verify_callback != nullptr) {    verify_ret =        ssl_ctx->app_verify_callback(ctx.get(), ssl_ctx->app_verify_arg);  } else {    verify_ret = X509_verify_cert(ctx.get());  }  session->verify_result = X509_STORE_CTX_get_error(ctx.get());  // If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.if (verify_ret <= 0 && hs->config->verify_mode != SSL_VERIFY_NONE) {    *out_alert = SSL_alert_from_verify_result(session->verify_result);    returnfalse;  }  ERR_clear_error();  returntrue;}

所以我们只需要hook这个函数,然后让其返回值为真即可,操作如下

  1. 首先找到lib目录下对应系统架构文件夹下的libflutter.so,拖入ida
  2. 然后看下方左下角等待ida加载完毕,如果没加载完毕就搜索很大概率搜不到
  3. Shift+F12打开字符串窗口,然后Ctrl+F搜索关键字ssl_server
记一次帮丈母娘破解APP,满满的全是思路
  1. 双击跳转过去后,按Ctrl+X查找交叉引用,如下图所示,6C4B4C就是函数对应地址
记一次帮丈母娘破解APP,满满的全是思路
  1. 编写hook函数
functionssl_attack(){      Java.perform(() => {        var base = Module.findBaseAddress("libflutter.so");        console.log("base: " + base);        var ssl_crypto_x509_session_verify_cert_chain = base.add(0x6c4b4c);        Interceptor.attach(ssl_crypto_x509_session_verify_cert_chain, {            onEnterfunction(args{            },            onLeavefunction(retval{            console.log("校验函数返回值: " + retval);            retval.replace(0x1);      }  });    });}

至此我们可以成功解决了流量侧的对抗问题,整体代码如下

functionwifi1_proxy_bypass(){    Java.perform(()=>{        var systemCls = Java.use('java.lang.System');        systemCls.getProperty.overload('java.lang.String').implementation = function (val{            var ret = this.getProperty(val);            if (val == "http.proxyHost") {                return""            }            if (val == "http.proxyPort") {                return"-1"// 这里改""/"0"/"-1",我这里留的-1是之前金融项目好几家都是-1            }            return ret        }    })}functionwifi2_proxy_bypass(){    Java.perform(function ({        var ConnectivityManager = Java.use('android.net.ConnectivityManager');        ConnectivityManager.getLinkProperties.implementation = function (network{            var linkProperties = this.getLinkProperties(network);            if (linkProperties) {                var ProxyInfo = Java.use('android.net.ProxyInfo');                var proxyInfo = ProxyInfo.$new(nullnull0);                 linkProperties.setHttpProxy(proxyInfo);             }            return linkProperties;        };    });}functionvpn1_bypass(){    Java.perform(()=>{        var ConnectivityManager = Java.use('android.net.ConnectivityManager');        ConnectivityManager.getNetworkInfo.overload('int').implementation = function (networkType{            var result = this.getNetworkInfo(networkType);            if (networkType === ConnectivityManager.TYPE_VPN.value) {                returnnull;            }            return result;        };    })}functionvpn2_bypass({    Java.perform(() => {        // 获取 NetworkCapabilities 类var NetworkCapabilities = Java.use('android.net.NetworkCapabilities');        // Hook hasTransport 方法        NetworkCapabilities.hasTransport.overload('int').implementation = function (transportType{            // 如果检测到 TRANSPORT_VPN,返回 falseif (transportType === NetworkCapabilities.TRANSPORT_VPN.value) {                console.log("[*] VPN 检测被绕过");                returnfalse;            }            // 否则调用原始方法returnthis.hasTransport(transportType);        };        console.log("[*] NetworkCapabilities.hasTransport 已 Hook");    });}functionssl_attack(){      Java.perform(() => {        var base = Module.findBaseAddress("libflutter.so");        console.log("base: " + base);        var ssl_crypto_x509_session_verify_cert_chain = base.add(0x6c4b4c);        Interceptor.attach(ssl_crypto_x509_session_verify_cert_chain, {            onEnterfunction(args{            },            onLeavefunction(retval{            console.log("校验函数返回值: " + retval);            retval.replace(0x1);      }  });    });}functionbypass_proxy_main(){    wifi1_proxy_bypass()    vpn1_bypass()    ssl_attack()}setImmediate(bypass_proxy_main)

再次利用frida进行hook,可以发现大功告成了

记一次帮丈母娘破解APP,满满的全是思路

进一步利用茶杯狐代理到Burp

记一次帮丈母娘破解APP,满满的全是思路

也是成功获取到了流量

记一次帮丈母娘破解APP,满满的全是思路

0x04 流量侧成功绕过收费限制

既然抓到流量了,那么接下来分析一下,登录之后可以看到它有一个请求,响应是下方这样,从响应中的参数不难看出,expired是否过期、showRechargeTip是否展示那个待激活的提示,还有过期时间等等参数

记一次帮丈母娘破解APP,满满的全是思路

最后经过测试改成下面的响应能成功绕过提示,并且功能点均可正常使用

{"code":200,"msg":"操作成功","data":{"deviceCount":1,"deviceInfo":{"imei":"xxxx","deviceName":"T1-xxxxx","activated":true,"vipService":true,"valueAddedService":false,"expired":false,"deviceStatus":99,"showRechargeTip":false,"serviceTime":99999999,"isNineDevice":false,"trackStorageDays":30,"gpsInstantModeExpiryTime":"2025-09-09"}}}

功能出来了

记一次帮丈母娘破解APP,满满的全是思路

点击查看定位直接就根据imei查询对应设备的坐标信息,不仅没校验是否有会员权限,而且没准还有水平越权,但是这个坐标位置是0.0

记一次帮丈母娘破解APP,满满的全是思路

我猜这个东西的工作原理大概就是每隔一段时间定位器发送给服务器一个坐标,然后app通过这个接口查询,不过现在很明显是定位器有限制,没有上传坐标所以是0.0

然后就是后话了,我尝试了将定位器里面的卡换成正常有费有流量的卡,然后还是不行,那么就点到为止了,这一路下来其实想过很多次拉倒了,放弃吧,但是实在是不甘心,然后当时搞到了凌晨4点可算是弄明白了

原文链接:https://forum.butian.net/share/4382

原文始发于微信公众号(富贵安全):记一次帮丈母娘破解APP,满满的全是思路

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月3日10:07:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   记一次帮丈母娘破解APP,满满的全是思路https://cn-sec.com/archives/4125987.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息