Bitguai.com

区块链技术丨改变世界
做高价值的区块链资讯媒体

Fomo3D类游戏中漏洞的综合解决方案

摘要:Fomo3D及其追随者的失败与智能合约设计中的漏洞密切相关。这里我们从安全的角度详细分析了《Fomo3D》类游戏中的两个问题,并给出了一些实用的解决方案供您参考。

黑客行为导致Fomo3D类游戏的沦陷

Fomo3D已经进入第三轮。根据10月9日格林尼治时间2:00的数据,该池只生成了103.4482以太币。总数量少于前两轮的800个以太币和680个以太币,与之前相比,这是一个很大的衰退。

SECBIT实验室曾发表一篇文章,分析类似Fomo3D游戏的情况,供大家参考。

图1:Fomo3D播放器参与情况和基金统计数据

上图显示了Fomo3D玩家参与和基金的比例。红线表示参与者的数量,蓝线表示进入游戏合同资金的总和。顶峰在7月20日和21日。许多媒体都报道了这个时期的Fomo3D。许多人跟随其他人加入了游戏,参与者人数与基金达到了最高峰,超过18000人和40000人。高峰过后,Fomo3D急剧升级,在8月22日结束了第一轮,进入第二轮,而热度再也无法恢复。

然而,黑客并不仅仅是离开了这个。
图2:Fomo3D上的攻击统计 上图是对Fomo3D攻击的概述。一些黑客攻击了游戏的空投漏洞,。此外,在第一和第二轮接近尾声时,黑客们正在利用交易拦截攻击来获得最终的胜利。

Fomo3D以外——其他模仿者也是黑客的目标。

Fomo3D游戏的目的是让参与购买钥匙获得奖品,而且,参与者有机会获得空投奖励。奖励是激励参与者通过随机性和竞争使游戏更加有趣,吸引更多的参与者。

然而,事情并没有如预期的那样发展。由于合同代码的缺陷,黑客可以利用特殊的技术不断获得高可能性的空投奖励,而最终的奖品将被黑客窃取。一般的参与者几乎不能赢得奖品,希望尽早进入会使他们从追随者那里受益。然而,有两个基本机制未能发挥作用,游戏无法持续吸引更多资金。于是一个恶性循环出现了。

那么,黑客是如何利用这两个漏洞的呢?开发团队是否有出路?

空投错误

先来看看空投奖励。

所有进入游戏的1%的以太币将被定向到小池中。每次放置价值不低于0.1以太币的订单时,空投的可能性从0开始,增加0.1%。此外,空投量与订单有关,例如购买0.1–1以太币可赢得小池中25%的以太币。你付出的越多,回报率就越高。游戏界面将显示当前获胜的可能性和小池的数量。

Fomo3D空投实现有两个缺陷:

·合同随机数是可预测的
·确定调用方是否是合同地址的方式不可靠

空投依赖于airdrop()合同代码中由空投控制的智能合约内生成的随机数。
/**
    * @dev generates a random number between 0-99 and checks to see if thats
    * resulted in an airdrop win
    * @return do we have a winner?
    */
function airdrop()
    private 
    view 
    returns(bool)
{
    uint256 seed = uint256(keccak256(abi.encodePacked(
        
        (block.timestamp).add
        (block.difficulty).add
        ((uint256(keccak256(abi.encodePacked(block.coinbase)))) / (now)).add
        (block.gaslimit).add
        ((uint256(keccak256(abi.encodePacked(msg.sender)))) / (now)).add
        (block.number)
        
    )));
    if((seed - ((seed / 1000) * 1000)) < airDropTracker_)
        return(true);
    else
        return(false);
}
airdrop()中的随机数种子通过块信息和事务调用地址进行计算,很容易预测。

Fomo3D开发团队还采用了isHuman()方法阻止合同加入到Fomo3D游戏中,自动攻击并通过合同预测随机数。
/**
    * @dev prevents contracts from interacting with fomo3d 
    */
modifier isHuman() {
    address _addr = msg.sender;
    uint256 _codeLength;
    
    assembly {_codeLength := extcodesize(_addr)}
    require(_codeLength == 0, "sorry humans only");
    _;
}
这里我们可以看到另一个常见的错误。Extcodesize操作符用于获取目标地址的代码大小。部署合同的地址与特定的代码相关联,因此extcodesize大于0。许多人使用这种方法来确定目标地址是否是一个合约,Fomo3D依赖它来阻止调用某些函数的合约,但这是一个不可靠的方法调用函数,构造器中的函数可以绕过这个限制。在构建合约时,地址不链接到任何代码,而extcodesize为0。

通过结合这两个漏洞,黑客获得了构建攻击合约和预测随机数的权限,从而极大地增加了他们的获胜机会。

如何修补空投缺陷

那么我们应该怎么做来解决Fomo3D空投问题呢?

只有使用前面提到的两个漏洞,黑客才能成功地进行攻击。因此,我们只须实施其中一个补丁:

·防止智能合约中的随机数预测
·确定调用方是否为具有更安全方法的约定

补丁一:防止智能合约中的随机数预测

首先从随机数预测开始。

智能合约中的随机数很容易预测,因为任何人都可以访问随机种子。攻击者可以构造一个恶意合约,在完全相同的环境中执行随机数生成公式,以获得后续步骤的随机数。

智能合约的几乎每个变量都是公共的,并且生成公式需要每个节点之间的一致性。因此,要找到一种生成不可预测的随机数的简单方法几乎是不可能的。

不过,还有其他实际而复杂的解决办法。开发人员可以提交和显示,或者延迟几个块。此外,我们可以引入外部干预,如Oraclize和BTCRelay。

结合Fomo3D机制,SECBIT实验室介绍了一种使用当前或将来块的哈希值的方法。

以太坊智能合约可以调获得特定块的哈希值。接受的参数是除当前块以外的最近256个块的块高度之一。如果传递其他值,则返回0。

常见的不可靠的随机数计算将把前面的block.blockhash(block.number-1)的哈希作为随机种子进行读取。在合同中调用block.blockhash(block.number)将返回0。我们无法在合约中获得当前块哈希值,因为在矿工打包和执行事务之前,还没有计算其值。因此,可以肯定地说,当前的块哈希在将来是不可预测的。

我们可以保存地址和当前的块高度N,玩家可以在第一次购买键并得到一个id(如下面的_purchase()所示)时添加到数组中。
function _purchase(address user) internal {
    Purchase memory p = Purchase({
        user: user,
        commit: uint64(block.number),
        randomness: 0
    });
    uint id = purchases.push(p) - 1;
    emit KeysPurchased(id, user, packCount);
}
玩家可以在下面的255个块中使用他们的ID,并且可以随机生成高度为N的块哈希值,以确定一个玩家是否赢得了奖品(见下_airdrop()所示)。

function _airdrop(uint id) internal returns(bool) {
    Purchase storage p = purchases[id];
    require(p.randomness == 0);
    require(block.number - 256 < p.commit);
    require(uint64(block.number) != p.commit);
    require(p.user == msg.sender);
    bytes32 bhash = blockhash(p.commit);
    uint seed = uint(keccak256(abi.encodePacked(bhash, p.user, id)));
    p.randomness = seed;
    if((seed - ((seed / 1000) * 1000)) < airDropTracker_)
        return(true);
    else
        return(false);
}

当玩家加入游戏时生成的块哈希值在255个区块之后就不再可用了。因此,我们必须通知竞赛者在时间范围内核对奖品。当然,如果错过了,我们可以再给他一次机会。我们的技术界有更多的技术细节。

这种方法也适用于著名的区块链卡游戏“神性解除”,以控制玩家购买的稀有卡。当然,我们可以使用给定数目的块哈希值(例如5)在当前高度之后作为随机种子进行投放。

补丁二:确定调用方是否为具有更安全方法的约定

另一个问题是如何确定调用方是否为合同地址。我们可以用一种简单而有效的方法来解决这个问题。

modifier isHuman() {
    require(tx.origin == msg.sender, "sorry humans only");
    _;
}
以太坊开发的最佳安全做法建议不要使用tx.origin由于许多开发人员不知道tx.orign和msg.sender之间的差异。tx.orign代表一个事务的调用者,而msg.sender代表每个合同调用的调用方。

A -> B -> C

例如,用户a调用合同B和B调用合同c。msg.sender合同C中的发件人是B,而tx.origin是A.msg.sender发送方可以是合同,而tx.origin永远不会是合同。因此,上述方法可以有效地通过契约约束调用契约。

交易阻止错误

现在是检查最后奖品的时候了。

Fomo3D游戏有一个倒计时-在每一轮比赛结束前购买钥匙的最后一个人会在池里赢得近一半的以太币,因此许多玩家会在接近尾声时购买钥匙,希望能在最后一秒获得胜利。

一般人在结束时采用类似的策略: 检查倒计时, 提高价格, 买钥匙, 闭上眼睛祈祷胜利;不过, 这项策略极不可能给你带来奖品。

根据 SECBIT 实验室的分析, 前两个 Fomo3D 回合的获胜者采用了同样的攻击技术--在接近尾声的时候进行攻击交易。
黑客会在这是部署的攻击合同。当只剩最后一秒时, 最后一个买主是黑客本身, 那么合同将断言失败。

黑客正是使用这种方法触发了大量多变的神秘交易: 当黑客极有可能成为赢家时, 用高油价来绘制挖掘池来打包黑客的交易, 并占据了以下几块,导致其他交易购买密钥不能得到定期奖励。

在比赛接近尾声的时候,一般玩家只需要通过手工或者脚本就可以买到价格很高的钥匙。黑客方法比通常的策略更聪明。

如何解决交易阻塞

事实上,这个问题不仅仅是威胁到Fomo3D一样的游戏。所有需要玩家在一定时间范围内比赛的游戏也会受到威胁。游戏中总会有黑客使用之前描述过的方法,只要游戏奖高,奖励远大于成本。

补丁一:提高攻击成本

为了解决这个问题,SECBIT实验室建议游戏开发者不应该将游戏的胜负与倒计时联系起来,将攻击带来的回报和攻击的欲望最小化。

例如,我们可以修改规则,最后一个买家获得奖金的概率相对较低,比如5%。当时间结束而奖品因概率未透露时,合同将加总倒计时时间。因此,黑客不可能通过阻止来赢得胜利,而事务阻塞攻击对黑客来说则需要花费太多的精力来继续攻击。
function buyCore(...)
    private
{
    ...
    // check to see if end round needs to be ran
    if (_now > round_[_rID].end && round_[_rID].ended == false) 
    {
        // check to see whether or not this round should end
        if shouldRndEnd(lastCommitId) (
            // end the round (distributes pot) & start new round
            round_[_rID].ended = true;
            _eventData_ = endRound(_eventData_);
            ...
        ) else {
            ...
            updateTimer(_keys, _rID);
            ...
        }
    }
    ...
}
这里是一个代码示例。shouldRndEnd()控制结束的概率,以确定游戏结束的时间,这依赖于参考先前的代码控制空投的不可预测的随机数。

补丁二:禁止调用信息查询接口的合约

Fomo3D攻击的另一个原因是游戏合约的信息查询接口,任何地址都可以调用它。黑客现在可以不断地查询游戏,并选择不同的策略,以最小化成本和最大化的获胜机会。
modifier isHuman() {
    require(tx.origin == msg.sender, "sorry humans only");
    _;
}
function getCurrentRoundInfo()
    isHuman()
    public
    view
    returns(...)
{
    ...
}

因此,Fomo3D的另一个补丁是应用上述安全的isHuman()来获得getCurrentRoundInfo(),以防止通过合约进行自动攻击。

结论

由于安全和公平方面的问题,Fomo3D及其模仿者更有可能成为黑客的金矿,而不是吸引更多普通玩家。参与者的数量会一轮接一轮地减少,衰退还在继续。

SECBIT实验室建议,后来者应该从中学习,避免复制代码,只通过公关来吸引人。


原文:https://hackernoon.com/a-comprehensive-solution-to-bugs-in-fomo3d-like-games-ab3b054f3cc5
作者:SECBIT
翻译:雷茜


本文由比特怪小编整理编辑发布,文章地址:https://www.bitguai.com/block/youxi/25501.html,转载请注明出处!

免责声明:转载此文为传递更多市场信息,不代表比特怪的观点和立场,请自行参考。



商务合作(QQ):755847138
媒体合作:Market@bitguai.com
底部导航

本站除标明"本站原创"外所有信息均整理转载自互联网,版权归原作者所有。如有不妥,请联系我们修改或删除。

CopyRight 2017-2018 Bitguai.com All Rights Reserved丨苏ICP备18049263号-1 百度地图 谷歌地图 RSS订阅


比特怪成立于2017年,秉承做高价值的区块链技术和应用为核心的区块链资讯媒体。网站内容主要涵盖区块链技术、游戏、应用场景和区块链项目落地等。让区块链技术应用服务于各个行业。