首页 资讯 安全 查看内容

从客户端游戏漏洞看开发中的安全隐患

2015-5-28 13:05 |原作者: 毕月乌|来自: wooyun.org 761 0

摘要: 虽然现在的应用开发越来越趋向于web应用,大型软件也大量使用了现有的框架,随着现有框架和引擎的完善,绝大多数安全问题已经被解决。但是遇到一些定制需求时,开发人员还是不得不从底层一点点进行设计。这时,没有 ...
关键词: nbsp 脚本 模型 函数 开发 代码 功能 但是 一个 物品

虽然现在的应用开发越来越趋向于web应用,大型软件也大量使用了现有的框架,随着现有框架和引擎的完善,绝大多数安全问题已经被解决。但是遇到一些定制需求时,开发人员还是不得不从底层一点点进行设计。这时,没有安全经验的开发人员很容易犯下错误,导致严重的安全隐患。本文以一款自主引擎的大型网络游戏为例,展示开发中容易被忽略的隐患。

Lua作为一种功能强大又轻量级的脚本语言,可以非常容易地嵌入其他语言的程序中,越来越多的游戏引擎使用了Lua来实现具体的逻辑过程。这使得我们避开复杂的逆向工程直接分析游戏功能的逻辑成为可能。Lua脚本的一些特性甚至让我们可以直接调试游戏中的脚本,比如使用著名的Decoda Lua调试器。如果配合简单逆向资源包等手段,我们可以轻松获取游戏中的脚本代码(有可能需要反编译),这使得我们的分析过程进一步简化,由黑盒变成了半白盒。

首先我们对游戏进行了简单的逆向来简化脚本分析的难度。通过逆向工程得知,游戏引擎对Lua脚本的依赖度非常高,C++只是用于基本的类和方法以及游戏渲染,几乎所有逻辑都是由脚本完成。同时为了更加方便我们的分析工作,对游戏的资源文件包进行了简单的逆向,成功解包出了游戏客户端的Lua脚本,至此,准备工作全部完成。

下列例子按照被发现的时间排序:

  1. 隐身
  2. 任意传送
  3. 大并发刷金币
  4. 使用不存在的道具
  5. 远程代码执行

0x01 隐身(意料之外的“正常功能”)


这个严格来说不算是漏洞,只是一个非常有意思的BUG。

在开发功能的时候,开发人员通常只会考虑实现功能和功能内部基本的安全性。这样并没有错,但是却有一种情况是这种开发模式无法防御的:正常功能特性被滥用。一个典型的案例就是下面这个隐身的BUG。红框中玩家模型处于不可见状态,无法通过点击模型选中,在选中他之前也无法发现他的存在。

这种隐身从逻辑角度来看肯定是不正常的,但不是从代码角度来看,它却又是正常的,所以在测试中也很难被发现。我们来看看实现隐身的代码:

enter image description here

这段代码其实只做了一件事情,那就是频繁的修改人物外观的显示状态,那么为什么人物会消失呢?这就得从基础说起了:游戏的渲染机制是每次模型外观出现改变,将模型删除,使用新的参数重新创建模型。这样,在删除模型和重新创建之间有一个时间差,这段时间内对应的玩家模型是不存在的!

在正常情况下这并不是什么大问题,但是如上面那一段代码,如果极其频繁地修改模型,就会导致模型根本没有机会显示出来,而这种问题如果在设计阶段没有引起注意,到了开发阶段就很难被发现。

0x02 任意传送(危险的未完成功能)


这是本文中最有意思的漏洞了。某一次大更新后,例行解包客户端看看更新了什么内容,无意中看到一个服务器脚本(不要惊讶,我也不知道为什么,但是客户端里确实有一部分服务器脚本~)中增加了一个名为OnCanJoinNormalMap的远程调用函数,看名字似乎是跟进入地图有关。

enter image description here

难道这是传说中的任意传送!小伙伴们惊呆了!实际测试一下,真的可以传送到任意指定坐标!

在一阵惊喜过后,脑洞大开的小伙伴们还写了一个完整的利用工具来实现真正意义上的任意传送(虽然不到一周时间漏洞就修了~):

enter image description here

修复以后的脚本变成了这样:

enter image description here

从注释内容来看,这个接口似乎在上线时根本没有实际使用,这也是开发人员经常犯的错误之一——上线一些根本没有使用但是却已经实现的接口。由于这些接口出于开发阶段,可能并没有完备的防护机制,比如上图这个接口实现,没有任何过滤。这些接口一旦被意外找到,事情就开始变得不可控了。

0x03 使用道具技能(不同操作共用底层实现带来的风险)


我们知道,游戏里通常都有一些可使用的道具,但是很少有人关心过物品使用这一过程是如何实现的,其中可能存在什么问题。

下面我们来看看物品使用的过程到底发生了什么。

首先点击物品的函数是这样的:

enter image description here

函数结尾的OnUseItem才是使用物品的关键,那么这个函数又是如何实现的呢?

enter image description here

可以看到一些物品的处理是由UseItem函数完成,这个函数不再是lua写的函数,而是C++函数了,部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
KItem::UseItem(DWORD dwBox, DWORD dwX, KTarget& rTarget)
{
    ......
 
    pItem = GetItem(dwBox, dwX);
 
    ......
 
    if (pItem->m_dwSkillID != 0 && pItem->m_dwScriptID == 0)
    {
        eRetCode = UseItemSkill(pItem, rTarget);
        KG_PROCESS_ERROR_RET_CODE(eRetCode == uircSuccess, eRetCode);
    }  
 
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论