一、前言

本文包括两个方面的漏洞安全介绍,第一部分我将以Fomo3D合约为原型,讲述一下由于合约的打包机制而导致的真实环境中的漏洞。第二个方面我会根据以太坊的特性,对漏洞的成因进行详细的分析。

二、Fomo3D事件攻击

1 Fomo3D介绍

在介绍漏洞之前,我们先简单的介绍一下Fomo3D是什么。

在我看来,Fomo3D的设计者是在深刻研究过博弈和人的贪婪性之后而设计的游戏。Fomo3D 是一款直接在以太坊网络上运行的分散式,无信任的区块链游戏。 该游戏由智能合约精心编程和部署,本身完全去中心化,不受任何人控制,它将自动运行直到以太坊网络死亡,无论底池有多么诱人,只会根据游戏规则进行支付。下面就是这个游戏的官方网站。

而这个游戏的规则就是:在 24 小时的倒计时结束时,最后一个购买钥匙的人会拿走所有资金池奖励的 48%,其余50%分配给本局参与的玩家、持有者和进入下一轮,剩余 2%留给社区,当每次有钥匙(什么是钥匙后面讲解)购买活动时,倒计时就会延时 30s,倒计时上限就是 24 小时。

为了让用户信服,这款类赌博游戏将源码公开。

Fomo3D游戏最最核心的规则就是最后一个购买的玩家获得最大的利益

其中主要规则有这么几条:

其中还有一些细致的规则:

F3D:LONG 通过游戏中的重要物品key,以24小时的时间为轮次。每当有人购买key的同时会使key的价格提高,同时恢复记时30秒,直到最后游戏倒计时结束,最后一个买入key的人会获取奖金池48%的eth,同时会把奖金池的50%会按照key的占有比例,分配给最后一名获胜者所在team的全部人员。

不同的team有不同的分配模式,这些资金会按比例额分配给下一轮的起始资金,本轮F3D玩家以及P3D Token持有者,从图中可以看到,对F3D玩家最为友好的是鲸鱼队,分红为30%。

而合约中需要购买的交换物叫做key,而key的价格随着购买方的变化而变化。

例如:一把钥匙的价钱是在已经有 n 个 key 的时候是 :

0.000074999921875 + 0.000000000015625 * n

公式是 f(x) = (0.000074999921875 ln(x) + 0.000000000015625 x)

获利是 (f(b) — f(a)) * 0.56 (0.56 是蛇队的分润),a 是开始时的 key 数,b 是结束时的 key 数。

/**
     * @dev calculates how many keys would exist with given an amount of eth
     * @param _eth eth "in contract"
     * @return number of keys that would exist
     */
    function keys(uint256 _eth) 
        internal
        pure
        returns(uint256)
    {
        return ((((((_eth).mul(1000000000000000000)).mul(312500000000000000000000000)).add(5624988281256103515625000000000000000000000000000000000000000000)).sqrt()).sub(74999921875000000000000000000000)) / (156250000);
    }

此处声明,这种游戏隐患风险极大。希望大家仅当做一种技术乐趣来进行分析,不要盲目入坑。一切问题的责任自行承担!!!

开发者在多处暗示参与玩家。比如,网站的名字叫做exitscam.me,即“逃离骗局”,邀请好友戏称为bad Advice(不良建议),代码中更是写明“WARNING”:THIS PRODOCT IS HIGHLY ADDICTIVE,IF YOU HAVE AN ADDICTIVE NATURE,DON'T PLAY(此款游戏容易致瘾,如果你很容易上瘾就别玩了)。里面还将以太币什么的称为pot(大麻)

只要知道并理解了游戏规则,就知道这个长局游戏不会停止。对,理论上不会停止,停止的情况也可以随意猜想,比如 ETH 遭遇黑客攻击,ETH 拥堵到站内分红都被用完场外资金无法进入等。所以最好还是不要涉猎这种类型的游戏。

2 事件分析

在了解了Fomo3D的合约内容后,我们来看第一轮游戏的最大赢家0xa169.....用户是如何赢得这个奖励的:10,469 eth的奖金,换算成人民币约2200万

下图为最终奖金发放的交易详情。我们在图中可以看到10,469.660003123933104565 EtherFromTo

然而根据我们第一部分的分析,这种游戏出现了最终的盈利者?为何会出现这样的问题?难道最后真的没有人继续投钱了来续约了吗?

我们针对这个合约进行具体的分析。

我们来根据交易的发生来具体追踪到底是谁运用了什么手段来获取到了最后一次交易的最终权。

根据我们的追踪,我们发现拿到最后大奖的钱包是如下钱包地址:

而我们对这个账户的交易进行追踪 。

我们发现红框中圈中的部分是合约与游戏直接进行的转账操作。而后合约内就多了好几千的以太币。然后我们会猜想,为何在最后的三分钟内,没有其他用户进行交易了?于是我们回访全网以太坊去一探究竟。

我们将当时最后交易发起后三分钟的区块全部调出。

第一列为当前区块中包含的区块数量,而第二列为矿工名(这个不重要),而我们圈中的部分为交易的reword,也就是交易的手续费。

然而我们能够发现,最开始的交易区块中包括交易数量92个,之后为103,在之后全网的交易就变的越来越少。在查看了所有相关的区块中的交易后发现,其三分钟内没有任何一个交易是于Fomo3D相关的。不仅如此,区块中的交易数量也是极少的。我们看最后的几个交易只有5、4、3、7等等个位数个交易。我们打开区块仔细查看相关交易:

我们来看后三个区块的内容。首先是包含三个交易的区块:

之后是包含四个交易的区块:

之后是最后一个区块:

此时我们应该发现了一个共同点,那就是这些区块中包含的交易巨大部分均是出自同一个合约。而打开这个合约,我们又发现了一个惊天的秘密。

我们发现这个合约的创建者就是我们游戏最后的受益人。

那么我们会进行联想,这两者之间到底有没有什么关系呢?

为什么在开奖前这个合约会持续不断的发起交易?然而这些交易均又共同的特点,那就是交易均失败,原因均是gas limit。

3 漏洞成因

这个时候我们不免会联想到以太坊的一个机制,那就是gas机制。

以太坊约14s左右会被挖出一个区块,一个区块中会打包交易,只有被打包的交易才会在链上永不可篡改。

所以为了奖励挖出区块的矿工,区块链上的每一笔交易都会消耗gas,这部分钱用于奖励矿工,而矿工会优先挑选gas消耗比较大的交易进行打包以便获得更大的利益,目前,一个区块的gas上限一般为8000000。

而对于每一笔交易来说,交易发起者也可以定义gas limit,如果交易消耗的gas总值超过gas limit,该交易就会失败,而大部分交易,会在交易失败时回滚。回滚之后你的gas就会白白丢失,而交易也不会被记录。

然而solidity中定义了特殊的指令assert()。这个指令允许交易失败,并且不进行交易回滚操作。所以作恶者完全可以利用这个函数来达到交易无法回滚,从而使交易被矿工打包的图中情况。

而这些占用了大量gas的交易就会将存量不高的区块占领。也就是为何我们后面的区块中只有几个交易的原因。

而在计时器倒计时的时候确实会有其他用户向合约中充钱。然而由于gas值提供的较少,从而没有恶意者的优先级高,然后交易无法被打包,也就导致了不断的交易失败。

而我们也能够看出来,为了提高自己的优先级,这个恶意者将自己的gas调到最大,也付出了一些gas的代价。交易手续费一度达到了2eth。然而对于回报来说,这些钱都是微不足道的。

对于以太坊的合约机制,有句话总结的很好:我们这个游戏的目的就是让用户顺序的去进行投注。然而以太坊的gas机制则是谁使用的gas多,谁就能优先获得被打包的权利。这也就类似于开后门的感觉,所以这两种机制相互矛盾,也就导致了漏洞的存在。

对于这个问题,我不建议大家去以身尝试,第一,这个倒计时是很难走到0的。第二,这种赌博心理完全是做了韭菜,很难从中获利的。

截止到发稿为止,真实世界的时间走了好几个小时了,然而这个游戏中的时间只增不减。从56分增加到58分。这也就是为何我们想要成为那个幸运儿是不太现实的原因。

三、参考资料

本稿为原创稿件,转载请标明出处。本文中所提及一切的有关虚拟货币的内容请勿随意尝试,一切后果均与作者无关。

源链接

Hacking more

...