0
点赞
收藏
分享

微信扫一扫

Fomo3D 游戏的第一轮是如何结束的

从游戏设定看达成”攻击“所需的必要条件

Fomo3D 是近一个多月以太坊上最火爆的应用,也是个赌博游戏,本文的目的是做技术分析,所以这里只介绍其结束的设定:

  • 游戏启动后从 24 小时开始倒计时;倒计时结束时,最后一个够买 key 的玩家将获得奖池中 48% 的奖金;
  • 每有一个玩家购买 key,倒计时会增加 30 秒。

所以,获胜条件实际上很简单:在自己购买 key 之后到游戏倒计时结束,不再有其他人购买 key。在现实世界中,要做到这点不那么容易,除非所有玩家都没钱了;但在区块链的世界中,具体到以太坊上,是可以通过“技术手段”做到不让其他人购买的(也就是不让其他人的“购买交易”得到“网络确认”)。这就是大家耳熟能详的“拒绝服务攻击”(Denial of Service,DoS)。

攻击的原理

在目前成熟的 Web 服务技术里,制造 DoS 攻击一般是通过大量的并发请求和/或大数据量的独立请求,将 Web 服务的带宽/服务资源占满,而使其无法再相应正常的数据请求。在以太坊中,则可以通过制造大量的“垃圾合约调用”来达到同样的效果。

这里需要来讲一个机制了:交易池(transaction pool)。在矿工/矿池节点上,通常都会有一个交易池,网络上广播的所有新的交易都会被首先加入这个“池”,而后再由矿工/矿池选择那些“经济性更好”的交易优先打包确认。这里说的“经济性”,即由交易发送者在交易数据中指定的 gasPrice,gasPrice 越高,执行交易所附带的合约代码的执行费用也就越高,而这些费用通常是会作为手续费支付给矿工的。所以,矿工/矿池会从交易池中选取那些 gasPrice 明显高于其他交易的交易来优先打包执行(确认)。并且,矿工并不能从技术上判断一个交易中附带的程序代码是否是“垃圾合约调用”(它们也没有这个“责任”),它们仅仅选取那些执行费用更高的交易来优先执行。基于这个原理,就允许了攻击者通过调高包含了“垃圾合约调用”的交易的 gasPrice,来在短时间内用这些“无效交易”占用区块的可用 gas,以使其他“正常交易”无法被打包进区块。

这里还有几个基础知识需要科普一下:

  • 以太坊中的区块可包含的交易(计算量)是由区块的 gasLimit 来控制的,而并不是像比特币那样用数据大小来限制;以太坊中目前区块的 gasLimit 上限是 800 万 gas,矿工可以做 5% 以内的上下浮动;区块内能包含多少交易,是看这些交易执行所消耗的总 gas 是否达到这个区块的 gasLimit;
  • 以太坊中执行交易的费用,是用交易基础执行费用的 21000 gas,加上交易中附加的代码的字节大小的费用(这里有一个折算公式,不详细讲了)以及实际执行代码所消耗的 gas 的总和乘以交易中指定的 gasPrice 来计算的;这个交易费用,会从交易发送者账户中自动扣除;如果交易发送者账户余额不足,交易不会被打包进区块;
  • 以太坊中的交易的实际执行所要消耗的 gas 是可以根据交易执行时的“世界状态”明确知道的,也就是这个交易的实际执行费用是明确知道的,矿工就是据此来判断打包交易的“经济性”。

在 Fomo3D 游戏的后期(即奖池金额已经很高),大多数玩家都会选择在倒计时的最后数分钟内才去购买 key,以让游戏能继续下去。这时,如果有一个机会,在攻击者自己购买了 key 之后(这只会给剩余时间增加 30 秒),能在其后数分钟内让网络不再确认其他人的购买交易,攻击者就可以让游戏结束从而赢得大奖。

攻击的过程

下面我们就来根据区块链浏览器中可以查到的实际数据来看看这个攻击是如何发生的:

  • 区块号 6191896:确认了一个由 0xa169… 到 Fomo3D:Long 的交易,调用了 buyXid 函数(即购买了若干 key);这个区块的时间戳是 06:48:22(UTC)。
  • 区块号 6191897 到 6191902:攻击者开始使用“垃圾合约调用”来填充区块(大概占用了这几个区块中的一半左右的可用 gas),但没有刻意调高 gasPrice(使用的是平均水平,20 GWei 左右),这是个非常有耐心、也非常大胆的处理,在看到 6 个区块的时间内没有其他人购买 key 之后,攻击者知道机会来了!
  • 区块号 6191903 到 6191908:从 6191903 开始,攻击者将“垃圾合约调用”交易的 gasPrice 提高到了 190 GWei,即平均水平的 8 倍以上,后续交易更是设置了 500 GWei 的超高 gasPrice,开始了真正的 DoS 攻击!直到 6191908 区块,这 6 个区块中只包含了不到 10 个简单的转账交易(即不包含合约执行的简单交易,固定消耗 21000 gas),其他可用 gas 完全被这些高 gasPrice 的“垃圾交易”占用。
  • 区块号 6191909:网络状况恢复正常。这个区块的时间戳是 06:51:17(UTC)。在这个区块中,我们可以看到数个调用了Fomo3D:Long 的 buyXaddr 和 buyXid 的交易,但因为游戏合约内的时间戳判定条件已经达到游戏结束,所以这些购买当然就没有效果了。

可以看到,攻击者的计划、准备周密,很有耐心,且技术处理上几乎无懈可击,完美地达成了必要的 DoS 攻击(短时间内阻止了其他玩家的交易被确认),从而“技术性获胜”。

攻击中的几个技术细节

首先,我们可以看到在上边提到的这十几个区块中包含了很多“失败”的交易,这些失败的交易有个共同的特点,都是由 Bad Instruction 导致的。这里的 Bad Instruction 也就是以太坊协议里预设的 EVM 操作码 0xfe(无效指令)。

攻击者用来完成 DoS 攻击的合约源代码并不是公开的,但我们可以从实际的合约字节码中看到一些端倪(因为过于技术化,这里不再展开讨论)。

然后,从这些“垃圾交易”的整体设计上看,也是很有学问的。这些交易的 gasLimit 并不都是一样的,而是从十几万、几十万到几百万这样的离散值。这是因为在启动攻击的时候,网络状况仍然是正常状况,所以各大矿工/矿池可能已经有了打包了一半的区块,这时,当它们收到了新交易之后,除了判断经济性以外,还会判断其 gas 消耗能否在当前区块的剩余可用 gas 中包含。比如有些矿池打包的区块中已经只剩不到 50 万 gas,这时那些超过百万的大交易自然就不能包含进去;这样,如果没有适合的 gas 量的“垃圾交易”来填充,就有可能让其他玩家的正常购买交易填充进去。所以,从攻击的角度讲,这些 gasLimit 比较小的“垃圾交易”同样是非常重要也是非常必要的!我们不得不佩服攻击者思路的缜密。

最后,要完成这样精确的攻击,攻击者需要很多技术准备。

他们需要若干能连接到前五乃至前十矿池(或者能连接到与这些矿池节点在“网络上”非常接近的全节点),这一点非常重要。因为要实施这样的攻击,你必须具备能实时获知各大矿池节点最新区块数据的能力,以便在发起最终的 DoS 攻击之前能确定没有其他人的正常购买交易被打包!也就是刚刚提到的 6191897 到 6191902 区块的等待期,在越多的大矿池节点数据中得到确认,攻击成功的几率越高。

然后,在发起攻击的时候,一定要在短时间内将用来攻击的数十个“垃圾交易”同时发送到前五乃至前十矿池,让他们把这些交易加入“交易池”;以最大限度地避免因为网络延迟导致其他玩家的购买交易被某个大矿池先打包的情况;这同样对攻击的完成至关重要!

以上这两点,需要攻击者同时拥有数个可以联动的定制化的客户端,并且有相应的程序进行监控(检查区块数据)并发起实际攻击(连续发送数十个预设的交易),这大概不是通过单个客户端或者简单地用几个脚本就可以做到的。

小结

从 Fomo3D:Long 第一轮游戏的结束来看,虽然我们可以搞懂整个过程以及其中的技术细节,但能不能先于别人实施、考虑到尽可能多的细节、尽量提高成功的概率就是个纯粹的技术活儿了;也需要大量的时间和精力以及资金支持。不过这个例子也给了我们更大的动力去研究技术、去学习细节,只有掌握了足够多的细节才能做到一击必中!不是么?

举报

相关推荐

0 条评论