某网游刷坐骑、刷极品道具、刷经验漏洞实现分享

robots

 

前言

一款老网游,找传奇类网游偶遇分析之做个小测试,作为豹子头玩家找到相关漏洞注册几个新号试刷一下封存,半年后于21年4月复试发现漏洞依旧存在,提交官方现已修复。本次主要介绍3个漏洞:

刷坐骑

无限免费的各种坐骑,坐骑可分解可进阶,也就是无限的顶阶坐骑

刷极品道具

无限免费的强化石、锁星石、炼魔石等,高玩几十万做的装备套装可以基本不花钱做出来

刷经验

可迅速升级到全服第一,一年内的服1小时内搞定,几年的老服也就几小时的事
活动期间附带兑换币奖励,可以大量兑换包括通用元宝在内几十种珍惜道具

 

接入协议工具

逆向分析下,游戏明文发包Call如下:

明文包函数

看到call eax,显然不用犹豫了,虚表Hook比较安逸

搜索基址

while (0 == g_Param_MMSend_Ecx){
g_Param_MMSend_Ecx = g_InitIO.Api.MemSigScan(“game.exe”,”[1]g_Param_MMSend_Ecx,?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ,0x11,1,4”);
Sleep(1);
}

虚表Hook

void __stdcall HookMMSend(bool bHook){
void pRet;
DWORD dwOldProtect;
DWORD dwParent = (PDWORD)(*(PDWORD)g_Param_MMSend_Ecx) + 4;

VirtualProtect(LPVOID(dwParent),4,PAGE_EXECUTE_READWRITE,&dwOldProtect);
if (bHook){
    OldMMSend = (TMMSendProc)(*(PDWORD)dwParent);
    InterlockedExchange((PDWORD)dwParent, (DWORD)(&NewMMSend));
}
else{
    InterlockedExchange((PDWORD)dwParent,(DWORD)OldMMSend);
}
VirtualProtect(LPVOID(dwParent),4,dwOldProtect,&dwOldProtect);
}

 

漏洞实现

一、刷坐骑

游戏中有一种”坐骑蛋”的道具,打开后会随机获得坐骑,协议:
66660000 01000000 00FFFFFF
0x0 4字节 协议类型
0x4 4字节 坐骑蛋种类 普通坐骑蛋、高级坐骑蛋等
0x8 1字节 坐骑蛋位置 0表示第一格
经测试对第四字节,按一定的时间间隔进行Fuzz可把任意道具当坐骑蛋使用生成坐骑(任意未绑定道具可生成未绑定坐骑),坐骑本身可分解升级,于是就可有无限可交易的顶阶坐骑了。
这边有个要点,必须以大概的时间间隔Fuzz,太快或太慢都不行

模糊测试

坐骑背包

二、刷装备相关道具

游戏有个挑战系统挑战到一定层数可以获取到相关宝箱奖励,协议:

0x0 0x4 0x8 0xC
66660000 AA000000 BB000000 CC000000

相关奖励,条件未达成发送领取无效,条件达成重复发送领取依旧无效
字段较多,写个Lua脚本Fuzz跑一跑,示例如下:

脚本测试

跑结束一看背包多了些道具和礼包,降速重新跑一遍排查复现,最终实现与坐骑Bug类似,可把指定位置任意道具变成多种想要的珍惜道具和礼包。
以无限免费强化礼包为例,每个礼包包含武器、防具、首饰的强化石以及锁星石,意味着装备可以不降级一直强化到顶级,至此高玩几十万打造的强化套装可以基本不花钱就拥有了

道具仓库

新区图片中一个星星形状道具卖100+RMB,老区有人花数百万搞装备,感慨一下网游真是暴利!

三、刷经验

游戏中玩家经验来源主要有经验道具、打怪、任务、活动、成就等
测试了经验道具获取和使用、普通打怪、特殊打怪(特殊场景、特殊怪物)、成就奖励、活动奖励等,未发现可利用点,鉴于RPG网游任务系统是最容易出问题的,决定还是把重心放在任务系统上
对各种任务ModSend、ReSend、NotSend等一通测试,有收获但和速刷经验相去甚远,结果如下
1.部分任务可以远程交、接
2.主、支线任务 不能重复做
3.日常悬赏 可在次数和颜色级别上有所突破,但意义不大,多一点经验而已
4.每周皇榜 可特殊情况下快速秒整轮皇榜,也意义不大,不能额外增加经验
5.日常阵营 不能重复做
6.日常帮派 不能重复做
尝试变更帮派、变更阵营以及其他的一些角色初始或变更状态的特殊情况,依旧无有效收获
决定分析出游戏任务资源继续研究,逆向发现一个游戏内资源的通用获取接口

资源逆

写几个函数把需要的一些资源提取存储到文件,贴一下逆向对应的核心函数

//根据ID获取资源地址,道具0xC、怪物0x10、技能0x11、任务0x12
DWORD stdcall GetResAdr(DWORD dwType, DWORD dwID)
{
DWORD dwRet = 0;
try {
asm {
push dwID
push 0
push dwType
mov eax, CN_Adr_ResBase
mov edx, dword ptr ds : [eax]
mov ecx, eax
mov eax, dword ptr ds : [edx + 0x18]
call eax
mov dwRet, eax
}
}
catch (…){
}
}

资源TXT

根据任务信息继续分析,对一些特殊的任务再跑一遍相关方案依旧无收获,直到看到如上图所示的几个ID为14XX的任务,这几个看任务名称显然是任务总类,却有任务ID与之对应比较奇怪,详细测试一下
接一下14XX任务,跑一遍相关测试方案无收获
接一下再放弃14XX任务,跑一遍相关测试方案有收获,发现帮派任务在离开旧帮派预约了新帮派的情况下可以重复接(也就是需要特殊组合协议和特殊状态配合才可ReSet),至此任务次数上有突破,速刷经验初步可行,但光次数突破还不够,一次任务如果时间太长严重影响刷经验的效率。
回到帮派任务,正常情况下每天都有不同的帮派任务,而在临时帮派中每天的帮派任务是固定的钓鱼任务,于是我们只需提升钓鱼任务效率即可
帮派地图如下:

帮派地图

正常做1次钓鱼任务2分钟左右
1.走到帮派任务NPC接任务
2.走到池塘边,开始钓鱼几秒进度条时间到钓一条鱼,钓到5条鱼为止
3.走到提交道具NPC处,提交5条鱼完成任务
测试特殊任务和帮派钓鱼任务可以远程接,池塘边和提交任务NPC中间选合适的点,可以省去所有寻路耗时
对钓鱼的协议分析,发现时间间隔本地控制,钓鱼过程又可极速
于是完整利用流程基本确定,结合游戏内具体协议,定点后流程如下:
1.放弃帮派钓鱼任务(防止某一轮执行异常影响后续执行)
2.接特殊14XX任务
3.放弃特殊14XX任务
4.接帮派钓鱼任务
5.五轮钓鱼(每轮开始钓鱼、结束钓鱼2个协议)
6.打开NPC
7.对话提交任务物品
8.实际提交鱼(需要分析背包数据,插件中修改提交协议)
9.确认提交
全部组合利用一次需要用到18个协议,完成一轮任务时间缩小到1秒内,至此速刷经验流程OK
剩下的在工具和插件中实现自动化操作,为了便于展示,直接用工具重复发送N轮相关协议,一些需要用到游戏内存数据的协议,直接在插件中用滤镜处理即可,比如提交鱼协议,需要内遍历角色背包数据,根据背包鱼位置填充协议中5个对应的鱼位置

void __stdcall TaskBug_Exp(DWORD dwBuf, DWORD dwLen)
{
int i = 0;
int iCount = 0;
int iNeedNum = 0;
int iNeedPosNum = 0;
DWORD dwNow = 0;
DWORD dwBase = 0;

if (!Bug_Exp_Check(dwBuf,dwLen)){
    return;
}
try {
    //刷新背包,游戏中数组刷新到自定义数组
    dwNow = *PDWORD(*PDWORD(CN_Adr_PersonInfo) + CN_Oft_BagItem + 0x4);
    memset(&g_BagItemAll, 0, sizeof(g_BagItemAll));
    iCount = 0;
    for (i = 0; i < CN_Max_BagItem; i++) {
        dwBase = *PDWORD(dwNow + i * 8);
        g_BagItemAll.Items[iCount].dwBase = dwBase;
        memcpy(&g_BagItemAll.Items[iCount].NetID, PVOID(dwBase), 8);
        g_BagItemAll.Items[iCount].wT1 = *(PWORD)(dwBase + 8);
        g_BagItemAll.Items[iCount].wT2 = *(PWORD)(dwBase + 0xA);
        g_BagItemAll.Items[iCount].iNum = *(PWORD)(dwBase + 0x62);
        LogDbg("%d    %08X %08X %d",iCount,g_BagItemAll.Items[iCount].wT2,g_BagItemAll.Items[iCount].iNum);
        iCount++;
    }
    g_BagItemAll.iCount = iCount;
    //修改协议(遍历出的道具位置)
    for (i = 0; i < g_BagItemAll.iCount; i++) {
        //鱼类型用0x66,0x88替代演示
        if ((0x0066 == g_BagItemAll.Items[i].wT1) && (0x0088 == g_BagItemAll.Items[i].wT2)) {
            iNeedPosNum = g_BagItemAll.Items[i].iNum;
            while (iNeedPosNum > 0) {
                *(PDWORD)(dwBuf + 0x18 + iNeedNum * 4) = i;
                iNeedNum++;
                iNeedPosNum--;
                if (5 == iNeedNum) {
                    return;
                }
            }
        }
    }
}
catch (...) {
}
}

36到45

 

游戏服务端切忌以恶小而为之

这个游戏部分任务不可以远程交接、大部分采集耗时都是服务器判断,偏偏刷经验漏洞利用到的两处服务器都没有判断,客观上让漏洞利用效率提升数百倍,另外帮派任务刷的次数有统计且客户端可实时显示,但刷到256次会次数归0,也就是服务器后台即使每天进行帮派完成次数监控也不一定能监控到。这个游戏还有其他10多个漏洞已提交官方,实现过程没有特殊之处就略过了。

 

后记

网游系统繁多,服务端的多样性决定了出现的漏洞千奇百怪,任何时候有条件好好体检是必须的,质量上做到让有经验的安全测试人员短期内难以挖掘到比较有价值的漏洞即可,其他的可交给后台监控策略或者更有意思的陷阱系统。

(完)