使用AFL模糊测试《反恐精英:全球攻势》地图文件
RealWorldCTF 2018有一个非常有趣的挑战叫做"P90 Rush B",这是对Valve游戏《反恐精英:全球攻势》中一种战术的影射。该挑战要求发现并利用CS:GO服务器使用的地图文件加载器中的漏洞。
在CTF期间,我利用了一个栈缓冲区溢出漏洞,后来另一个团队在他们的报告中很好地描述了这个漏洞。由于这个漏洞也影响了官方的CS:GO Windows客户端,符合Valve的漏洞赏金计划条件,实际上它只是一个旧报告的小变种,所以我在CTF结束后很快报告了它,并迅速得到了修复。
BSP文件格式与攻击面
CS:GO(可能所有Source引擎游戏)使用的地图文件格式称为BSP,是二进制空间分割的缩写,这是一种方便的n维空间对象表示方法。然而,这种格式支持的远不止3D信息。BSP文件由服务器和客户端共同处理,因为两者都需要地图信息的某个子集来执行各自的任务。这是一个远程攻击面,因为客户端会在服务器发起的地图变更时从服务器下载未知地图。
模糊测试设置
为了简单起见,我决定模糊测试Linux服务器二进制文件,而不是实际的客户端(也可以在Linux上运行)。用这种方法显然无法发现客户端特有的问题,但我希望能找到共享代码中的低悬果实。
我编写了一个简单的包装器来处理服务器二进制文件使用的共享库,最重要的几个是:
- engine.so - 主要Source引擎代码(包含BSP解析器)
- dedicated.so - 专用服务器实现(包含应用程序入口点)
- libtier0.so - 可能与Steam/应用程序管理相关
AFL修改
我对AFL做了一些简单的修改:
- 输入文件必须以.bsp结尾才能被GetModelForName正确解析
- 需要能够指定自定义的fork服务器启动点
- 增加等待fork时的超时乘数
漏洞分类与根因分析
我们需要将"好"的漏洞与不感兴趣的漏洞(如纯越界读取)分开。我基于调用栈进行了简单的去重,然后在Valgrind中运行每个独特样本。
经验总结
从这个小型项目中我学到了:
- AFL在QEMU模式下非常灵活,可以攻击特定代码段
- 输入文件大小非常重要,从300KB降到16KB后性能至少提高了5倍
- 分类对于筛选以前未经过模糊测试的代码库非常重要
- 堆上的内存损坏不是一个安全问题 :)
示例漏洞:CVirtualTerrain::LevelInit中的堆缓冲区溢出
在CVirtualTerrain::LevelInit中发生堆缓冲区溢出,因为dphysdisp_t::numDisplacements变量可能大于g_DispCollTreeCount,而检查这种情况的断言在发布版本中不存在。攻击者可以很大程度上控制堆内容,因此很可能被利用,特别是在Windows 7上许多模块没有启用ASLR的情况下。
[附上一个BSP文件,其中numDisplacements = 0xffff且g_DispCollTreeCount = 2,可以可靠地使csgo.exe崩溃]