THUAI7 THUBG saiblo.net 漏洞解析 外挂 writeup

清华大学人工智能比赛 - 未来战场 外挂解析

清华大学五月十几号到五月二十几号的 THUAI-7 未来战场 THUBG 在 saiblo.net 上举行。本来准备炼丹,但是赛制非常不友好,并且发现服务器等都是开源的,突然感觉开挂比较好玩

官方后端源码地址
外挂 目前获取对手 token 的功能已经失效

游戏简介

在一个 256*256 的二维平面上,坐标是浮点数,有 1x1 的障碍物,两个(娱乐赛有多个)玩家,一秒 20 ticks,可以走路、捡物资、开枪、扔手雷等。获取自己的整数坐标使用向下取整。

基于浮点数的攻击

浮点数移动攻击

首先,交互流程大概是用 websocket 传 JSON。以上是处理选手移动的入口,每 tick 执行一次。

以下是 Position Normalize:

于是我们有了第一个漏洞:如果 x=1e-200, y=1e-200 我们可以使得 Normalize 之后变成 (inf, inf)。把其中一个变成 0 可以获得 (inf, nan) 等组合,负数就是 -inf。

接下来,我们从走路的入口寻找漏洞:


以下是 GetRealEndPosition 的源码,比较长,可以不管:

这里引入了一个 DoS 漏洞。这个函数会先递归一次并且 independentAxisCalculating=true。以下是 GetXaxisBlock:


C# 一个特性是,inf nan -inf 转 int 之后全都是 -2147483648。省略一些过程之后(实际上只需要把所有方向的组合试一遍),我们有了第一个 DoS 漏洞:

出生点选择在 (0.0, 0.0),然后走到 (-1e-200, 0) 服务器处理的时候会陷入一个从 -2147483648 到 0 的循环,把这里面所有数都加到一个动态数组里。实测由于在 docker-compose 里加入了内存限制 4096M,会导致服务器几乎无法动弹,十几秒钟处理一个 tick 并丢出一个错误。如果没有内存限制会直接崩溃。

这样我们有了一个赢得天梯比赛的思路:使用 DoS 让服务器一直出错,十分钟后服务器自动退出,这时如果对手发现连接断开并且自己主动退出了,评测系统会算每退出的那个人赢。如果对手不退出,自己也不退出,会算评测失败不扣分。这样理论上来说这个 DoS 是永远不会扣分的。本人懒得实现这个了,后面还有一个更强的 DoS,有兴趣的可以去天梯炸鱼

浮点数开枪攻击

流程:出生在 (0.0, 0.0) 朝 (1e-200, 1e-200) 打拳。关键代码如下:

问题出在前面的 Normalize。

打拳(开枪)的逻辑是,算出 targetPosition - playerPosition 并且 Normalize,然后算对手是否被攻击到了。由于 Normalize 已经被污染了,我们会传两个类似 inf 的东西进去,然后机缘巧合地这个代码就死循环了。distance 会算出 inf,然后服务器就会彻底卡死。用这个方法 DoS 服务器然后用前面那个方法去天梯上炸鱼应该不错,而且这个方法更优的地方在于不会在服务器日志中生成任何信息。嘿嘿

这里另一个漏洞是,站在 (0, 0) 可以提高射程,或者站在 x=0 或 y=0 朝垂直方向开枪也可以提高射程。具体原理是这样的:浮点数标准中有一个最小能被表示的浮点数,后面被 truncated 的东西会取整(至于取整朝哪个方向取整,RFC 并没有规定)。Normalize 是同时除以模长,模长由模长平方的根号得到。

我们可以精心构造一个很小的浮点数,使得模长平方开根号前的值被取整,让模长变小,从而让射程变大。令 m m m 表示最小的正浮点数,则我们构造一个 p p p 使得 p 2 < m × 1.5 p^2 < m \times 1.5 p2<m×1.5 并且尽可能大。这样,一个向量 Normalize 之后,它的长度就会与 1 偏差一些,具体来说我们最大能得到的是 1.5 ≈ 1.22 \sqrt{1.5}\approx 1.22 1.5 1.22。也就是说我们卡在墙的时候可以提升 22% 射程,注意是朝垂直方向开枪。卡在 (0, 0) 墙角的时候,可以朝任意方向开枪,会比较复杂,但确实可以实现。

还有一个老版本卡墙的漏洞,可惜 C# 的浮点数取整太魔性了。经过很长时间的瞪眼,我发现一个漏洞:a+b-b 不一定等于 a。这是老版本服务器代码里唯一一个可能卡墙的漏洞。我们考察 python 中的一个例子。辛苦了好久才想出来的

服务器中的逻辑是:

每次走 0.02,如果碰到墙就用减法回到上一步。这里因为 a+b-b != a,我们可以精心构造一组方案。我至少构造了六个小时吧,最后被 C# 给杀了,因为 C# 的浮点数取整跟 python 不一样,所以这个漏洞最终被语言特性给拦截了。如果浮点数处理跟 python 一样的话,我们可以在 2 的整数次方的位置卡墙。代码也在 github 上,文件名叫 glitch_2pow。换加法减法方向还是不行,只能等服务器代码更新了。果不其然更新之后新增了有漏洞。

基于网络的攻击

管理员非常贴心地给了我 docker-compose 用于“本地调试”。以下是 docker-compose:


我们直接新建一个类似的,然后试试能不能监听到通信。

直接监听发现不能,但是有一些很有趣的广播,形如 ARP Who is …? Tell … 看得出来 arp 表是没有初始化的,这里漏洞也是比较简单:arpspoof。用一些简单的手段获取到服务器和对手的 IP,然后就可以监听他们之间的通信。google websocket payload decoder 出来一个 0 star 的库 https://github.com/tdhoward/decode-websocket-packets,只有几十行 python,居然他妈就是我想要的代码。现在是 1 star 了。然后试着装各种 python 监听库发现都要 NET-ADMIN 权限,我没有啊,只能用最朴实的办法 tcpdump -w victim.pcap -U {filter} 然后读 victim.pcap。于是这个外挂可以直接控制对手出生在手雷把自己原地炸死。

决赛前那个月黑风高的晚上一点钟,有个人把还在调试的几个人分别开了十场测试自己的 AI,当时的效果如下:

(没有截图,现在找不到了)效果大概就是,那个人 20 场全输了,零胜率……

当时因为外挂打有些人出问题,调了很久也不知道怎么回事。后来发现大概是拦截的 timeout 太小了。然后决赛前一天中午被修了,可恶

Bonus

决赛前交 DoS 发现交不上去,大概问题是我不是我们队的队长,然后用 burpsuite 把名字改成队长的名字就可以修改已经交了的 AI 了。整个过程大概就几分钟,还是比较熟练了

其他可能的漏洞:

  • 平台判断胜负 由于后端代码不知道谁是平台上的玩家1 玩家2,平台判断胜负大概率是一个额外的脚本。群里有管理员说漏嘴了,大概是一个 regex 判断胜负。但是我没法把我输出的内容不分行,也就是我输出的内容每一行都是 client0/1 开头的,这个没有仔细研究,可能最后无法实现。
  • ICMP redirect 这是网络安全空间导论课程的一个附加实验,但是我本地测都默认开了防御机制还关不掉。不知道行不行,而且现在不知道管理员针对 arp spoof 做了什么措施,可能需要设置一个 reverse ssh 看看能不能获取对手 IP 等消息,以及是否有防火墙或网络隔离。

总结

花了很多时间,当时用 arpspoof 拿到对手 token 的时候非常激动!!大概是这辈子除了竞赛之外最激动的时候了!可惜社会心理学不过关,赛前一天没绷住……

好像现在天梯关了,不能炸鱼了

  • 30
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值