该漏洞属于 Resource-Exhaustion DoS via Unbounded RPC Flood
游戏版本: 0.16.7.421589
资源版本: 75506 ~ 75885
在B站看到一个外挂的视频,介绍了一种方法可以使同房间内的玩家游戏崩溃,经初步观察得出如下触发条件
经过长达半小时的分析和手搓之后,复现得到了同样的崩溃结果,顺带测试了一下 stuffType 设为0-9,只有 8 会导致崩溃
logcat日志里还会出现大量的 [W] Invalid PurchaseFxItem3D configuration
既然有崩溃那么自然会产生崩溃文件转储,保存在/data/user/0/com.xxxxxx.sky/files/uniTrace/crashhunter/目录下,可以提取出来分析
杂活交给大肥鱼去干,过了 1 分钟就得到了崩溃点
IDA分析一下,对应函数是 WingBuffVisual::Initialize,在调用 fx->flags 的时候用于空指针导致了异常,前面也没有对空指针进行校验
找交叉引用回到上层PurchaseFx::Initialize,大致重要的代码如下,顺带解释了前面日志里大量的Invalid PurchaseFxItem3D configuration字符串来源,只要stuffType不为1那么就会有这条日志,看起来和崩溃没有直接关系
stuffType 0~7走的都是同一个模型改改颜色和大小,9是换了个模型,只有8是特殊的。注意到在调用WingBuffVisual::Initialize初始化前,会先调用一个工厂函数WingBuffBarn::CreateWingBuffVisual
跟进分析一下,可以发现当 purchaseFxPool 内的对象数量大于容量后,函数就会返回空,而在调用WingBuffVisual::Initialize前也没有校验过,最终就会触发空指针异常崩溃
到这一步,受害者客户端里的崩溃链路就完整了。那么这些事件是怎么从攻击者那边跑到受害者那边去的呢
回到 PlayPurchaseFX。PlayPurchaseFX::OnStart 会创建一个子事件
一次脚本调用就是一个子事件、一个 stuffType=8 的 item。子事件创建出来时 broadcastEnabled 默认是 true。PurchaseFx 的构造函数 sub_221B504 里可以确认
在 PurchaseFx::Update 里有一段RPC发送逻辑
PurchaseFxRpc::Run是接收端对应的处理函数,只校验了发送者身份和有没有可见角色,没校验频率,也没校验报文里的 stuffType
所以大致是这么一个链路
那么 purchaseFxPool 为什么会满而不会自己清理呢
回到 PurchaseFx::Update 里,fx->elapsed 是每帧累加事件的存活时间,PurchaseFx 的整个生命周期都靠它驱动
把 j = 0 带入计算(该特效只会有一个item), 第 0 个item的 release 条件是 elapsed / 4.0 > 0.95,意味着至少要到 3.8 秒才会释放
所以我们可以推测一下,既然接收端是没有限速的,那么攻击者只要在3.8s这个窗口内尽可能多的发送RPC即可。回到崩溃日志,查看Invalid PurchaseFxItem3D configuration数量按时间分一下组,数据刚好对上,只花了大概 1.12 秒就资源耗尽了
同时我们还注意到,攻击者在执行有效的攻击后自己本身并不会崩溃,这里推测是因为加速挂把游戏时钟也一起加快了。下面是AI分析的小结论
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
最后于 16小时前
被fu11p7r编辑
,原因: 喵喵喵