想想还是发自己网站舒服,想怎么写就怎么写,某个社区认为这篇文章在鼓励使用网站漏洞不给予参加写作计划。发在社区里那篇的我甚至都做了完整打码,Hook代码也没贴。

就在今天,我吉他调音器不小心摔坏了,想到现在有调音器app,所以到小米应用市场搜了一下下载下来,但是调吉他一弦和六弦竟然是收费的

31199-d4svx6hzwg9.png

然后看了一眼这个app是没有加固的,也不知道为什么,看到没加固我就想反编译。不看不知道,一看吓一跳。整个分析过程,就像老奶奶钻被窝,给爷整笑了。

槽点1:代码内置VIP账户

使用jadx反编译代码,通过app内的Toast提示词定位到判断vip的方法,这个方法硬编码了一个手机号,只要是这个手机号,就直接给VIP。可能是开发人员的,也可能是老板的。

64241-cuci07d7bod.png

槽点2:登录不管是否成功,都返回账户的正确密码

我原本是想测下这个登录是否有漏洞点,结果输入错误密码后,接口竟然返回了明文的正确密码。

01888-jnu17nlvx9h.png

等等,那根据前面逆向得到的硬编码的账户,我是不是可以拿到永久VIP账户?

答案不言而喻

79188-w9mv194e56.png

槽点3:购买会员无校验

从点击弹出优惠页面,到点购买跳转到支付宝,一路回溯到支付代码。发现参数异常简单,于是试着Hook该支付方法,在调用支付SDK之前,把金额修改为¥0.01,写成了xposed模块放到手机上试下,结果如图

72391.png

72391-bx5tbv6b4jd.png

槽点4:会员传染

当你购买了会员后,退出登录再登录任意非会员账户,此时非会员账户会成为会员,且被记录到云端(换手机登录也是VIP,是真的持久化)

逆向代码可以看到几个问题

1、因为购买完成后需要把购买成功消息通知服务器,所以代码里总是优先取本地的VIP状态,然后发送给服务器(服务器才知道有个用户购买成功了,要记录下来)
2、代码中VIP等级+过期时间是全局变量,并且在退出登录时不销毁
3、因为1和2,任意非会员账户登录,app就会认为他购买了会员并通知服务器记录
4、此种情况持续到app被关闭才会消失

60729-cld8itzu1y7.png

总结

这个app在用户认证、支付安全、状态管理等方面存在严重设计缺陷,事实上问题远不止我列出来的这些。

相关Hook代码

package com.sumver.fdtune;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import android.content.Context;

public class hackvip implements IXposedHookLoadPackage {

private static final String TAG = "HackVip";

@Override
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
    if (!lpparam.packageName.equals("com.fd.tune")) {
        return;
    }
    XposedBridge.log(TAG + ": 找到吉他调音器+"); 

    try {
        XposedHelpers.findAndHookMethod(
                "com.fd.uservip.activity.VipBvActivity",
                lpparam.classLoader,
                "m", 
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        Object activityInstance = param.thisObject; 

                        // 获取当前 VIP 类型b
                        int currentVipType = (int) XposedHelpers.getObjectField(activityInstance, "B");
                        XposedBridge.log(TAG + ": Hook m(): Current VIP Type (B) is: " + currentVipType);

                        // 检查是否是永久会员 (B == 3)
                        if (currentVipType == 3) {
                            // 获取当前价格c
                            double currentPrice = (double) XposedHelpers.getObjectField(activityInstance, "C");

                            // 修改价格为 0.01
                            XposedHelpers.setObjectField(activityInstance, "C", 0.01);

                        }
                    }
                }
        );

    } catch (NoSuchMethodError e) {
        XposedBridge.log(TAG + ": NoSuchMethodError: " + e.getMessage());
    } catch (XposedHelpers.ClassNotFoundError e) {
        XposedBridge.log(TAG + ": ClassNotFoundError: " + e.getMessage());
    } catch (Exception e) {
        XposedBridge.log(TAG + ": Hook 过程中发生异常: " + e.getMessage());
        e.printStackTrace();
    }
}
}

这只是刚好挖到的漏洞,事实上要解锁会员功能,都不用修改支付相关函数,直接Hook判断VIP的函数,让他始终返回为true就行了

    try {
        // Hook 关键的 VIP 检查方法: c.w(Context)
        XposedHelpers.findAndHookMethod(
                "o4.c",
                lpparam.classLoader,
                "w",
                Context.class,
                new XC_MethodHook() {
                    @Override
                    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                        param.setResult(true); // 将返回值设为 true,表示用户始终是 VIP
                        XposedBridge.log(TAG + ": VIP 检查被绕过!方法 c.w() 被强制返回 true。");
                    }
                }
        );

文章目录