前言

北京时间 9 月 24 日深夜 11 点 32 分,Fomo3D 第二轮最终大奖由地址 0x18a0 获得,奖金为 3,264.668 Ether。安比(SECBIT)实验室分析发现,此轮游戏获奖技巧与第一轮如出一辙,均为黑客“阻塞交易”攻击。与此前第一轮游戏相比,这轮游戏持续 33 天,无论是从入场资金、持续时长,亦或是最终大奖奖金额来说,相比上轮均大幅缩水。

回首两月之前,Fomo3D第一轮游戏入场资金一度超过 40,000 Ether,第一轮游戏最终大奖奖金额超过 10,000 Ether,游戏参与者众多,类似的资金盘游戏也如雨后春笋般相继冒出,一时间好不热闹。然而,自 9 月以来,随着市场整体的大萧条,类 Fomo3D 游戏也几近没落。以 Fomo3D 和 Last Winner 为代表的几款较活跃的游戏,热度明显下降。而大部分的同类合约由于无人开启下轮游戏,都处在搁置状态。

因而到如今,Fomo 3D第二轮大奖已经开出,黑客获奖几无悬念,游戏的热度已不可同日而语。

安比(SECBIT)实验室针对这一变化展开分析,分析结果表明,不久前连续揭露的游戏空投机制漏洞[1]和“阻塞交易”攻击事件[2],尽管在短时间内将该类游戏的关注度推上了新的高度,但本质上也是导致游戏没落的主要原因。

繁华 vs 凉凉?

曾经多火爆,如今就多冷清

游戏热度全景

7 月 20 日, 资金盘游戏 Fomo3D 在国内迅速传播开来,引起了广泛的关注。随后,一系列类似 Fomo3D 的游戏陆续上线。8 月 6 日,另一款类 Fomo3D 游戏 Last Winner 部署上线。在其团队的大力推广下, Last Winner 迅速火爆起来,甚至一度造成以太坊拥堵和 Gas 费用暴涨。但在游戏第一轮结束后,各个游戏的热度都开始持续下降。

热度都开始持续下降

图一:Fomo3D 玩家参与度与入场资金状况

上图展示了「Fomo3D 玩家参与度与入场资金状况」。红色代表调用合约参与游戏的人次,蓝色则代表进入游戏合约的资金量。图左侧出现数据曲线最高峰,对应时间分别是 7 月 20 日和 7 月 21 日。这两天恰好大量媒体疯狂报道 Fomo3D 这一现象级游戏。当时众多玩家跟风入场,游戏合约的参与次数和入场资金均达到了最高峰,入场资金量超过 40,000 Ether,而参与次数最高超过 18,000 次。而曲线的最高点则对应着 Fomo3D 游戏第一轮最高峰。

高峰过后,Fomo3D 游戏热度骤降,于 8 月 22 日前后结束第一轮,并随即进入第二轮,但游戏热度已然无法恢复。

游戏热度已然无法恢复

图二:Last Winner 玩家参与度与入场资金状况

类似地,上图展示了「Last Winner 玩家参与度与入场资金状况」。Last Winner 针对 Fomo3D 游戏做了部分修改,降低了游戏时间上限,因此每轮结束得更快。第一轮高峰出现在 8 月 8 日,此后有多个局部波峰,代表着不同轮次的开启,但显然调用次数和入场资金一轮不如一轮。

奖池资金

下图为 Fomo3D 合约中 Ether 余额的变化情况。Fomo3D 游戏在迅速火爆后,合约内 Ether 余额一度上升到峰值,第一轮游戏结束后,合约中的余额立即呈断崖式下跌,至今还处在缓慢减少的过程。

图三:Fomo3D 合约 Ether 余额变化状况

图三:Fomo3D 合约 Ether 余额变化状况

无独有偶,Last Winner 合约中的余额也曾在短时间内迅速增长,而后随着几轮游戏的迭代而逐步下降,至今所剩的余额更是不足 5,000 Ether。

图四:Last Winner 合约 Ether 余额变化状况

图四:Last Winner 合约 Ether 余额变化状况

最终大奖

8 月 17 日,安比(SECBIT)实验室与 Anchain.ai 团队联合揭露了黑客团队 BAPT-LW20 利用空投机制漏洞,攫取大量奖金的攻击事件。同一天,Last Winner 游戏第一轮结束,最终大奖的获得者也是 BAPT-LW20 团队。据统计,该黑客团队从 Last Winner 首轮游戏中获利累计达 12,948 Ether。

在 Last Winner 已完成的前 9 轮游戏中,黑客团伙持有的账户 0×5167 共夺得 4 次最终大奖。

最终大奖

类似地,Fomo3D 游戏首轮和第二轮大奖分别于 8 月 22 日和 9 月 24 日开出,奖金额分别为 10,469.66 和 3,264.66 Ether。除了奖金数额锐减之外,安比(SECBIT)实验室还发现大奖获得者均使用了相同的攻击技巧。

反思

机制漏洞是游戏没落的主因

除了市场萎靡以及玩家热情降低,安比(SECBIT)实验室认为“阻塞交易”攻击(针对最终大奖)[3]和空投漏洞攻击[4]打破了合约机制设计中的关键部分。这才是使游戏变冷的主要原因。

图五:Fomo3D 游戏合约被攻击状况

图五:Fomo3D 游戏合约被攻击状况

上图是「Fomo3D 游戏合约被攻击状况」,第一轮游戏高峰前后以及第二轮开始后,有黑客疯狂地利用空投漏洞进行攻击,攫取高额收益。而在第一轮临近结束,以及第二轮倒计时快结束之际,有黑客疯狂尝试“阻塞交易”攻击,目标在于夺取巨额的最终大奖。

北京时间 9 月 24 日深夜 11 点 32 分,Fomo3D 第二轮最终大奖由地址 0x18a0451Ea56Fd4FF58f59837e9EC30f346ffDCa5 获得,奖金达 3,264.668 Ether。安比(SECBIT)实验室发现 Fomo3D 第二轮大奖获得者 0x18a0 创建了攻击合约 0x705203fc06027379681AEf47c08fe679bc4A58e1,并发起大量攻击交易,连续堵塞 10 余个区块,时间长达 2 分钟以上。这一系列攻击,在上图的游戏被攻击监控数据中展露无疑。

图六:Last Winner 游戏合约被攻击状况

图六:Last Winner 游戏合约被攻击状况

上图是「Last Winner 游戏合约被攻击状况」,与 Fomo3D 类似,黑客攻击规模在游戏热度高时十分巨大。

“阻塞交易”攻击

Fomo3D 游戏巧妙地设计了一个环节,最后一个参与游戏的人将获得奖池中一半的 Ether 做为奖励。开发者想通过此设计保持大量的玩家陆续进场,任何人都有可能成为那个“幸运儿”。但是在黑客利用“阻塞交易”攻击[5]来加快游戏结束成为受益人后,这个吸引玩家的设计被打破了。因此后进场的普通玩家只能成为被割的韭菜,自然很难再吸引玩家参与。

“空投漏洞”攻击

除了最后的大奖,游戏中还设计了空投奖励机制,玩家有一定的概率获得空投奖励,这也是吸引玩家参与的一个重要原因。但是由于空投机制存在随机数漏洞,黑客可以通过一定的技巧持续高概率地拿走奖励,而普通玩家几乎不可能成功。这样空投奖励的公平性也同样受到了挑战。

对策与展望

dApp 还有很长的路要走。

这样一类红极一时的爆款游戏,必定会成为 dApp 游戏发展史上的一个重要里程碑。但是过快的萧条速度也给我们带来了很多的反思。对于类 Fomo3D 游戏,甚至是所有的 dApp 游戏,还有很长的路要走。要及时从黑客攻击的案例中吸取教训,并做好防御措施。

“空投漏洞”攻击是类 Fomo3D 游戏遭受攻击次数最多的一个破绽。黑客可低成本持续地攫取奖池中的资金。

一方面,Fomo3D 游戏的空投机制利用随机数来控制中奖概率,但是由于随机数的来源都是区块或者交易中特定的一些公开参数,如交易发起者地址、区块时间戳、区块难度等,所有以太坊智能合约都可以很容易的预测随机数[6]。因此在涉及到随机数的场景下,安比(SECBIT)实验室建议开发者应尤为慎重,或通过一定的手段来增加随机源的预测难度。如开发者可通过使用多次提交再披露、或延迟若干个区块开奖的方法来规避随机数被预测的风险。

另一方面,为了防止合约自动化攻击,Fomo3D 游戏合约对调用者是普通账户还是合约账户做了检测,但由于检测方式存在漏洞,黑客可以利用一些技巧绕开检测。因此项目方在对参与 dApp 游戏的玩家的身份做检测的时候,也应采用更加可靠的方式。例如,通过判断该笔交易最初的发起者(tx.origin)与当前合约的调用者(msg.sender)是否为同一地址。

前文提到的“阻塞交易”攻击,也是类 Fomo3D 游戏面临的另一重大挑战。

黑客通过高额手续费吸引矿工优先打包,并利用合约自动判断游戏进行状态,以之作为是否采取攻击的依据。黑客最终能够以较低成本堵塞区块,每个区块中仅打包很少的交易(降低他人交易被打包的可能性),使得游戏快速结束,并提高自己获得最终大奖的概率。

其实这一问题不止会威胁类 Fomo3D 游戏。所有类似机制,即需要玩家抢在某个时间范围内完成某种竞争操作的智能合约,都会受此威胁。要杜绝这一问题,安比(SECBIT)实验室建议游戏开发者,从游戏机制入手,切断游戏最终胜利(获得某个巨额大奖)和倒计时结束(最后一个交易被打包)之间的必然联系,从而使黑客的攻击获利概率和攻击意愿都降到最低。

类 Fomo3D 游戏中黑客利用了以太坊共识协议的特点,找到游戏机制的漏洞进而发起攻击。这也提醒我们,dApp 应用中的机制设计是一项极其复杂的工程,除了技术本身,还涉及到平台外部环境,博弈论等方面问题,这也是容易被黑客攻击的环节。因此,dApp 项目在机制设计过程中应尤为慎重。

正如安比实验室创始人郭宇所说:“新世界有生命,就有捕食者。有交易者,就有黑客。区块链上的应用在进化,攻击者也同样”。在技术和财富的交汇处,黑客永远保持着最灵敏的嗅觉。黑客的一次次攻击,给了我们很多的反思和改进的机会,这也是应用不断进化的催化剂。

参考文献

[1] Pwning Fomo3D Revealed: Iterative, Pre-Calculated Contract Creation For Airdrop Prizes!,

https://peckshield.com/2018/07/24/fomo3d/

[2] 智能合约史上最大规模攻击手法曝光,盘点黑客团伙作案细节

https://zhuanlan.zhihu.com/p/42318584

[3] Fomo3D 千万大奖获得者“特殊攻击技巧”最全揭露

https://zhuanlan.zhihu.com/p/42742004

[4] Péter Szilágyi 提出的空投漏洞利用 POC,

https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide/, 2018/07/23

[5] 区块律动:8万笔交易「封死」以太坊网络,只为抢夺Fomo3D大奖?

https://mp.weixin.qq.com/s/5nrgj8sIZ0SlXebG5sWVPw

[6] Predicting Random Numbers in Ethereum Smart Contracts

https://blog.positive.com/predicting-random-numbers-in-ethereum-smart-contracts-e5358c6b8620

*本文作者:安比实验室SECBIT,转载请注明来自FreeBuf.COM

前言

8 月 22 日,一个名不见经传的游戏God.Game发出通告,声称遭遇黑客攻击,游戏内的以太币被黑客全部转走。这一消息一时间传遍各大媒体以及讨论群,人们在质疑相关游戏安全性之余,也在揣测项目方动机,一时间难以分清到底是黑客攻击,还是游戏开发方留有后门跑路。

安比(SECBIT)实验室的小伙伴在得到消息后,迅速开展了追踪分析。

God.Game简单规则如下:GOD股份购买的所有数量的10%被征税并且作为被动的ETH收入分配给所有GOD所有者。

通过游戏官网规则,以及合约源码分析,不少人会认为God.Game是PoWH3D的直接仿品,安比(SECBIT)实验室仔细分析后发现,它其实是综合“借鉴”PoWH3D和Zethr的混合仿品。

PoWH3D是最近大热的Fomo3D游戏团队上一款作品。而Zethr则是在PoWH3D基础上进一步开发优化,新增不同玩法的另一款热门游戏。二者在玩法规则层面的细节差别,这里不做讨论。

God.Game游戏漏洞本身不复杂,不少安全公司也发出相关攻击手法的短篇快讯消息,部分不够详尽,安比(SECBIT)实验室通过本文全面分析该漏洞,重点分享漏洞追踪和定位的详细过程与大家讨论交流。

疑点一:攻击是无意还是刻意

一下子提走合约内所有以太币的人,是参与游戏过程中无意发现了漏洞,还是刻意进行的操作?

安比(SECBIT)实验室认为是后者,因为分析下来,该漏洞虽然隐藏不是很深,但实际触发需要一系列组合操作(类似玩游戏按上上下下左左右右开启作弊),正常游戏玩家的普通参与几乎不可能触发漏洞。安比(SECBIT)实验室分析攻击者的交易记录,发现其手段十分娴熟,目的性极强,明显是奔着该漏洞而来。

疑点二:攻击者的时机选择

攻击者为什么恰好选择在奖池金额243个以太币时发动攻击?

攻击发生时刻

上面是游戏合约账户余额趋势图,横坐标是区块高度,纵坐标是God.Game合约账户余额。此图可反映随着时间推进,入场资金情况。安比(SECBIT)实验室发现,God.Game合约部署上线后,合约资金量长期一直在 50 以太币以下,但在6179500区块高度以后,入场资金开始猛增到接近260个以太币(大量韭菜入场),随后略有衰退(一部分人选择提现退出),进入一段很长的调整停滞期。

无论是Fomo3D还是PoWH3D,这类庞氏游戏最早期入场资金优势都很大,可以较快速回本。后续只有在持续大规模地运营宣传下,才可能有大量新资金入场参与游戏。因此游戏第一时间内的入场资金,基本决定了游戏资金量的总体规模。

而攻击者正是在资金规模就快回升到前期高点时,果断出击,利用漏洞果断提走游戏内的所有以太币。于是形成了上图类似“高台跳水”的有趣情景。

这蹊跷的手法、这迷人的走势是不是有些眼熟?

疑点三:是漏洞还是后门

到底是无意引入的漏洞还是刻意暗留的后门?

前面提到,God.Game游戏代码重度参考了PoWH3D和Zethr。而与漏洞相关的关键函数名transferFromInternal()只在Zethr合约代码中有出现。

相关的关键函数

上图所示问题代码,“创新”地增加了一组关于转账双方是合约、还是普通账户的分支情况处理。面对这一串冗长的代码,只要清楚PoWH3D工作原理,就很容易能发现此处代码逻辑根本说不通,也无法在游戏实际规则中找到适配点。并且这种根据账户类型分别处理账本的逻辑在原版PoWH3D和Zethr中根本没有出现。

God.Game代码此处函数命名仿照了Zethr,但是具体变量名却是仿造Fomo3D,代码风格十分诡异。可以推断以下两种情况:代码作者有可能是因为没有理解PoWH3D游戏机制引入了漏洞;也有可能是故意增加代码混乱度来迷惑他人,以达到埋藏后门之目的。

埋藏后门

特别地,安比(SECBIT)实验室还发现同样是transferFromInternal()函数,God.Game代码中唯一与Zethr相近的地方,就是上图示例中针对ERC223代码做的回落处理,似乎是想通过引入此段代码(需要判断目标地址是否是合约)来为前面提到的不合逻辑代码打掩护。

异常:飙升的Token售价

下面让我们进入安比(SECBIT)实验室安全研究员sha3的第一视角,让TA为你拨开漫漫迷雾找出漏洞。

GoodLuck Have Fun.

我们首先进入游戏首页,发现单个God token的售价已经飙升至300ETH,而被God.Game抄袭的火爆原版游戏PoWH3D的单价才0.02ETH,面对不寻常的数值,敏感的安比(SECBIT)实验室小伙伴首先怀疑该游戏合约可能存在整数溢出漏洞。

为了考证我们的想法,我们想到了回溯God.Game过往的售价变化过程(感谢区块链技术,让我们无法消灭历史),寻找在何处触发了整数溢出漏洞。

通过遍历区块历史数据,发现在6182409高度时,buyPrice/sellPrice等数据飙升,于是我们便仔细分析该区块中和God.Game相关的交易。

分析发现,在6182409区块中,唯一和God.Game产生关联的交易是0x368688a944059fdd657e7842d8762b05250bd45f3a2a16cbae1b29727023b00f。

在该交易中,0x2368Beb4调用0x7f325EfC的reinvest()后,继而调用了0xca6378fc中的reinvest(),(通过简单的逆向分析,我们暂时认为0x7f325EfC是一个简单的代理合约,实现了God.Game游戏的基本接口)。

一番操作:定位到攻击源

我们不禁问道,为何调用一次reinvest()就可以将游戏中的各个数据全部暴增,我们怀疑创建这个合约的账户就是攻击者。

顺着这条线索,我们观察到这个合约的创建者,即0x2368Beb4,在6182409高度之前通过合约0x7f325EfC进行了另外几次操作。

具体操作历史如下:

区块高度 From To call
6182399 0x2368Beb4 0xca6378fc transfer(address,uint256)
6182399 0xca6378fc 0x7f325EfC tokenFallback(address,uint256,bytes)
6182403 0x2368Beb4 0x7f325EfC withdraw() 3ccfd60b
6182403 0x7f325EfC 0xca6378fc withdraw() 3ccfd60b
6182403 0xca6378fc 0x7f325EfC  
6182406 0x2368Beb4 0x7f325EfC transfer(address,uint256)
6182406 0x7f325EfC 0xca6378fc transfer(address,uint256)
6182409 0x2368Beb4 0x7f325EfC reinvest() fdb5a03e
6182409 0x7f325EfC 0xca6378fc reinvest() fdb5a03e
6182419 0x2368Beb4 0x7f325EfC sell(uint256)
6182419 0x7f325EfC 0xca6378fc sell(uint256)
6182439 0x2368Beb4 0x7f325EfC transfer(address,uint256)
6182439 0x7f325EfC 0xca6378fc transfer(address,uint256)
6182462 0x2368Beb4 0x7f325EfC transfer(address,uint256)
6182462 0x7f325EfC 0xca6378fc transfer(address,uint256)

再看看reinvest()函数调用时产生的event。

产生的event

我们看到了0000000000000000fffffffffffffffffffffffffffffffffffcf2ac578ec8d9这个极像溢出的值。

随即我们打开God.Game源代码,搜寻其中的蛛丝马迹。

我们将目标锁定到reinvest()函数。

function reinvest()    
onlyProfitsHolders()
public
{
    // fetch dividends
    uint256_dividends = myDividends(false);
    // retrieve ref. bonus later in the code
    // pay out the dividends virtually
    address_customerAddress = msg.sender;
   payoutsTo_[_customerAddress] += (int256) (_dividends * magnitude);
    // retrieve ref. bonus
    _dividends +=referralBalance_[_customerAddress];
   referralBalance_[_customerAddress] = 0;
    // dispatch a buy order with the virtualized "withdrawndividends"
    uint256 _tokens= purchaseTokens(_dividends, 0x0);
    // fire event
    emit onReinvestment(_customerAddress, _dividends, _tokens);
}

首先大概浏览一下reinvest()函数主体,仅有两个简单的加法,但是通常在减法溢出中会出现巨型整数,我们便开始探索reinvest()调用的函数。

首先引起我们注意的便是myDividends(false)函数,由于参数传入了false,我们就直接研究它最终调用的函数dividendsOf(_customerAddress)。

function dividendsOf(address_customerAddress)    
view
public
returns (uint256)
{
    return (uint256)((int256)(profitPerShare_ * tokenBalanceLedger_[_customerAddress]) - payoutsTo_[_customerAddress])/ magnitude;
}

Aha,看到减法了!

会不会就是在这里发生了溢出呢?

历史回放:精确找到溢出点

带着这样的疑问,我们回溯了0x368688a944059fdd657e7842d8762b05250bd45f3a2a16cbae1b29727023b00f这笔交易的trace,我们跟着静态分析结果,直接在trace中将PC指针定位到了dividendsOf函数中。

将PC指针定位

将PC指针定位

发现在0x0a8c指针处执行了sub指令,并且sub指令在栈上的两个参数分别为0和0x30d53a87137270000000000000000减法、除法执行完毕后栈顶变成了0xfffffffffffffffffffffffffffffffffffcf2ac578ec8d9,和event中的值完全相同。

果然,我们找到了发生溢出的地方,并且确定reinvest()函数使用了溢出后的值。

顺藤摸瓜:推理怎么构造溢出条件

那么问题来了,我们有什么办法能让这个减法溢出呢?

回想sub指令的参数:

1.第一个参数0对应profitPerShare_ *tokenBalanceLedger_[_customerAddress]

2.第二个参数0x30d53a87137270000000000000000对应payoutsTo_[_customerAddress]

思路来了,我们需要达成2个目标

1.使得profitPerShare_ *tokenBalanceLedger_[_customerAddress]的计算结果为0,

2.使得payoutsTo_[_customerAddress]中存储的值为正数。

首先我们看目标1,对于profitPerShare_变量,纵观全部代码,只有增加没有减,无法有效地将这个值变为0,那么只有tokenBalanceLedger_[_customerAddress]才能给我们修改的机会。

然后目光看向目标2,payoutsTo_[_customerAddress]必须设置为正值。

我们看到在transferFromInternal()、withdraw()函数均中对该值有操作,不由得想到了上文看到攻击者的4步组合拳,这4步具体发生的操作如下:

1.transfer (攻击者调用God.Game合约的transfer函数将token转移到到攻击者创建的合约)

2.withdraw (攻击者调用代理合约的withdraw函数,代理合约调用God.Game的withdraw函数讲ETH提入代理合约)

3.transfer (攻击者调用代理合约的transfer函数,将God.Game中代理合约的token转移到攻击者账户)

4.reinvest (攻击者调用代理合约的reinvest函数,代理合约调用God.Game的reinvest函数触发溢出)

谜底揭开:漂亮的组合拳

我们不妨看看在God.Game合约中发生了什么?

攻击者第1:从自己账户转移token到合约,触发transferFromInternal函数中的第一个else if分支:

else if (fromLength <= 0 &&toLength > 0) {    
    // human to contract
   contractAddresses[_toAddress] = true;
    contractPayout += (int) (_amountOfTokens);
    tokenSupply_ = SafeMath.sub(tokenSupply_, _amountOfTokens);
   payoutsTo_[_from] -= (int256) (profitPerShare_ * _amountOfTokens);
}

攻击者将自身的payoutsTo_[_customerAddress]被减为负数,紧接着修改`tokenBalanceLedger_[from/to]进行普通的token转账操作,给代理合约一些token以便拥有dividens。

攻击者第2:代理调用withdraw(),payoutsTo_[_customerAddress]值增加。

攻击者第3:将代理合约的token转移到攻击者账户,触发transferFromInternal中的if分支:

if (fromLength > 0 &&toLength <= 0) {    
    // contract to human
   contractAddresses[_from] = true;
    contractPayout -= (int) (_amountOfTokens);
    tokenSupply_ = SafeMath.add(tokenSupply_, _amountOfTokens);
   payoutsTo_[_toAddress] += (int256) (profitPerShare_ * _amountOfTokens);       
}

攻击者的payoutsTo_[_customerAddress]值增加,同时将代理合约中的token全部转移到攻击账户中。

可以看到,这3步组合让代理合约满足了减法溢出所需条件:

1.通过第1、第3两步将代理合约的tokenBalanceLedger_[_customerAddress]值为0;

2.通过第2步将payoutsTo_[_customerAddress]值置为正数。

攻击者第4,猛烈一击,调用reinvest()触发dividendsOf()的减法溢出,攻击者获取巨量的dividens,将divisions转换为token。

由于token数量过多,合约储备金额不够withdraw(),攻击者便将部分token转移到0xC30E89DB73798E4CB3b204Be0a4C735c453E5C74,通过0xC30E89DB73798E4CB3b204Be0a4C735c453E5C74进行sell操作卖出token换取ETH,合约储备金面对巨量增发的token,瞬间消耗殆尽,攻击者成功提取了几乎所有的ETH。

Good Game.

God.Game风波带来的思考

近来各类山寨版本智能合约游戏盛行,已经发生很多起安全事件,安比(SECBIT)实验室不久前已针对Fomo3D和Last Winner进行了一系列详细的漏洞披露和解析。而这次God.Game甚至上线不久就宣告被黑客攻击游戏结束,很多玩家的投入无法兑现,导致出现一些「游戏(投资)群」秒变「维权群」的闹剧。

安比(SECBIT)实验室提醒广大智能合约游戏爱好者,务必要擦亮双眼,提高安全意识,谨慎参与不明游戏(小心漏洞与后门),并且对于任何游戏都不要投入超出承受能力范围的资金。

游戏智能合约在一定程度上比一般Token合约更复杂,一些漏洞会隐藏得更深,触发条件更苛刻。很多游戏甚至会有更高层次的关于公平性、博弈机制的漏洞存在,需要从游戏模型设计、代码实现等各种角度进行安全评估。安比(SECBIT)实验室建议所有负责任的智能合约游戏开发商,都应该提高安全意识,加大安全投入。

参考文献

[1] God.Game合约地址,https://etherscan.io/address/0xca6378fcdf24ef34b4062dda9f1862ea59bafd4d

[2] PowH3D(P3D)合约地址,https://etherscan.io/address/0xb3775fb83f7d12a36e0475abdd1fca35c091efbe

[3] Zethr(ZTH)合约地址,https://etherscan.io/address/0xD48B633045af65fF636F3c6edd744748351E020D

[4] BCSEC:GodGame漏洞原理以及黑客攻击手法分析,https://bcsec.org/index/detail/tag/2/id/252

[5] 安比实验室:Fomo3D千万大奖获得者“特殊攻击技巧”最全揭露,https://mp.weixin.qq.com/s/MCuGJepXr_f18xrXZsImBQ

[6] 安比实验室:智能合约史上最大规模攻击手法曝光,盘点黑客团伙作案细节,https://mp.weixin.qq.com/s/YBG8YyPwh374HbGWcUKTdQ

以上数据均由安比(SECBIT)实验室提供,合作交流请联系[email protected]

关于安比(SECBIT)实验室

安比(SECBIT)实验室专注于区块链与智能合约安全问题,全方位监控智能合约安全漏洞、提供专业合约安全审计服务,在智能合约安全技术上开展全方位深入研究,致力于参与共建共识、可信、有序的区块链经济体。

*本文作者:安比实验室SECBIT,转载请注明来自FreeBuf.COM

前言

Fomo3D 游戏第一轮正式结束,最终大奖由地址 0xa169 获得,奖金额高达 10,469.66 以太币。不少人轻易得出结论:获奖者平平无奇。

安比(SECBIT)实验室昨日首个发现 Fomo3D 大奖获得者采取一些“特殊攻击技巧”,使得游戏临近结束时,矿工打包入区块内的交易数量骤减(涉及连续多个区块),从而起到加速游戏结束和提高胜率的作用。我们在 Last Winner 上轮游戏结束时也观察到了多个十分类似的异常区块和交易。

一组连续的异常区块和交易

连续的异常区块和交易

如上图所示,高度为 6191896 的区块中打包了 Fomo3D 游戏中奖者最后购买 key 的交易,该区块共包含 92 笔交易,交易数量正常。

而在该区块后的连续 11 个区块(6191898~6191908),每个区块包含交易数量都骤降,最少的一个区块(6191906)仅包含了 3 笔交易,十分异常。

让我们来观察一下这些“特殊”的区块。

“特殊”的区块

如上图所示,高度为 6191906 的区块仅包含了 3 笔交易,并且交易均发往同一合约(调用同一个神秘合约),三笔交易手续费之和超过 4 个以太币

而该神秘合约(0x18e1)创建者正是中奖者(0xa169)!

通过向 F2POOL 负责人确认我们得知,矿池挑选交易进入区块的策略为手续费(TxFee)高的优先。

这也解释了为何上文提到的连续 11 个区块,都仅打包了很少量的交易,却能获得超出正常区块的手续费。这些区块分别由 SparkPool、Nanopool、Ethermine、BitClubPool、MiningPoolHub 等矿池打包,显然挑选高手续费交易优先打包,是符合矿池利益的策略,是行业通行的方案 [1]。

黑客的“神秘合约”究竟做了什么?

我们观察到,这些异常区块内发往神秘合约(攻击合约)的异常交易,最终状态均为失败。

“神秘合约”

如上图所示,交易最终状态为失败,并且 Etherscan 有 Bad instruction 错误提示,最终导致耗光 Gas Limit (4200000),约为正常区块 Gas Limit 的一半,因此打包该交易的矿池也收到了高额的手续费。

以太坊区块 Gas Limit 这一概念,代表单个区块中最多允许的 Gas 总量上限,以此来决定单个区块中具体打包多少笔交易。区块 Gas Limit 由矿工间通过一定策略协商设定,目前区块常见值约为 8000000 [2]。

以太坊上每笔交易也包含一个 Gas Limit 概念,由交易发起者自行设定,代表该笔交易可最多消耗的 Gas 上限,实际 Gas 消耗以交易具体执行消耗为准。一个区块中所有交易花费的 Gas 不能超过区块的 Gas Limit [3]。

我们知道,以太坊智能合约还存在一个指令 assert(),用于断言。当 assert 的结果不满足条件时,则会耗光交易的 Gas。 Etherscan 针对这种情况的交易通常提示 Bad instruction,实际则为 EVM 执行过程中遇到了一个未定义的操作符 0xfe [4]。

获奖者(黑客)就是利用这个特性,实现少量交易就能占用整个区块的 Gas Limit。

黑客更犀利的操作

进一步,我们发现神秘合约会调用 Fomo3D 游戏的 getCurrentRoundInfo() 接口,用于获取当前轮次比赛信息,如剩余时间、最后一位购买者的信息(最有可能获奖者)等奖池详尽信息(如下图)。

更犀利的操作

“神秘合约”并未公开源码,结合逆向结果推测,获奖者(黑客)通过神秘合约,在合约内调用该接口查询游戏信息,重点关注剩余时间最后一位购买者地址。当游戏剩余时间达到一个阈值,并且最后一个购买者是自己时,则通过 assert() 让整个交易失败,并耗光所有 Gas;当剩余时间很长或最后一个购买者不是自己时,则不做任何操作,仅消耗很少的 Gas。

获奖者(黑客)就是利用这种方法,发起大量类似的可变神秘交易:在自己极有可能成为中奖者时,利用这些高额手续费的神秘交易,吸引矿池优先打包,占满后续区块,从而使得其他玩家购买 key 的交易无法被正常打包,最终加速游戏结束,并极大地提高自己的中奖概率。

其他一些技巧和重要细节

我们观察到 Fomo3D 获奖者(黑客)创建过多个类似神秘合约(攻击合约),并且前后曾使用很多不同地址向神秘合约发起交易,来分散大众注意力,降低自己的“特殊技巧”被发现的可能性。而每个攻击合约都有大量交易记录。上文提到的最终导致黑客获奖的攻击合约(0x18e1)有多达 5000 多笔交易,可见黑客暗地里做过不少尝试。

获奖者(黑客)控制的地址批量调用神秘合约(攻击合约)时,会给交易设置各种 Gas Limit,从 170000 到 4800000 不等,这同样是一个技巧。

Fomo3D 获奖者(黑客)0xa169 最后一次购买 key 后(区块 6191896),连续十余个区块,都无任何与购买 Fomo3D 游戏 key 相关的交易,这才最终导致游戏倒计时结束,获得最终大奖。

中奖区块的下一个区块(6191897),其实也包含大量异常交易,欢迎联系小安同学回答 :‑)。

在游戏结束前,大众一直假想黑客可能会与大矿池联合作弊,拒绝打包竞争者交易来获得大奖,或者设想黑客可能发出大量垃圾交易使以太坊网络拥堵而无法打包竞争者交易,从而获利。

这轮游戏的获奖者(黑客),并未联合多家矿池作恶,反而巧妙利用了所有矿池优先挑选交易打包策略的共性

获奖者(黑客)部署的神秘合约(攻击合约),简直是智能合约作为攻击武器的典范示例,精确制导,有的放矢。比起盲目地使用自动脚本在临近游戏结束时调高 Gas Price 发起参与游戏交易,黑客的攻击手法显然高明许多 。广播至各矿池的攻击交易,如导弹一般部署在内存池中,根据游戏状况,伺机而动。

我们在 Last Winner 上轮游戏结束时也观察到了许多十分类似的异常区块和交易,手法十分接近。

获奖者(黑客)创建的神秘合约,也正频繁与其他一些 Fomo3D 山寨游戏(如 Super Card)发生交易,企图使用同样的手法获得巨额大奖。

哪有什么简单的“黑天鹅”,有的只是聪明且努力的黑客。「幂律分布」和「游戏缪误」了解一下 [5]。

幸运的 F2POOL 矿池

另外值得一提的是,两款游戏(Fomo3D、Last Winner)中奖交易最终均由 F2POOL 矿池打包出块,并且中奖者的上一笔参与游戏的交易,也均由 F2POOL 打包。

安比(SECBIT)实验室与 F2POOL 矿池负责人仔细讨论过相关细节后,均初步认为这是巧合。F2POOL 矿池幸运地见证了两款热门智能合约游戏两笔巨额中奖交易的诞生。

智能合约游戏路往何方

我们之前还报道过 Last Winner 及其他 Fomo3D 山寨游戏中,黑客利用原版 Fomo3D 游戏中的空投漏洞,攫取大量奖励,以及 Fomo3D Quick 版本存在的问题。

在感慨黑客利用特殊技巧获得高额回报之余,我们也不由地替各类智能合约游戏的未来产生担忧。

Fomo3D 作为 2018 年最火爆的智能合约游戏,在玩法和技术上做了很多创新,是智能合约游戏历史上重要的一步。不可否认,Fomo3D 开发团队,有着过人的技术水平和强烈的去中心化情怀。

Fomo3D 游戏刚出之初,不少人都惊呼,这是真正公平的去中心化游戏。但回到现实,依旧有黑客能发现当中的各类缺陷,并悄悄利用,获得可观的回报。

技术的局限性、人性的贪婪、信息的不对称,都制约着一款真正安全、公平、透明的去中心化游戏的诞生。在 Fomo3D 出现之后,各类山寨游戏纷纷涌现,却没有在技术和创新上能走得更远,圈子氛围变得浮躁且嘈杂。作为区块链及智能合约爱好者,我们无比希望能尽快看到下一款安全、公平、优秀、有趣的智能合约游戏出现。

参考文献

[1]: https://ethereum.stackexchange.com/questions/15896/do-mining-pool-or-miners-decide-on-which-transaction-to-be-included-in-the-next, 2017/05/06

[2]: Accounts, Transactions, Gas, and Block Gas Limits in Ethereum, https://hudsonjameson.com/2017-06-27-accounts-transactions-gas-ethereum/, 2017/06/27

[3]: 科普 | 以太坊中的账户、交易、Gas 和区块 Gas Limit, https://ethfans.org/posts/479, 2017/07/03

[4]: Revert(), Assert(), and Require() in Solidity, https://medium.com/blockchannel/the-use-of-revert-assert-and-require-in-solidity-and-the-new-revert-opcode-in-the-evm-1a3a7990e06e, 2017/09/28

[5]: 黑天鹅, https://book.douban.com/subject/3025921/, 2008/05

*本文作者:安比实验室SECBIT,转载请注明来自FreeBuf.COM

Last Winner(类 Fomo3D)游戏大火,导致以太坊异常拥堵,Gas 费用暴涨。大量以太币资金入场。北京时间 2018 年 8 月 10 日凌晨 1:38,加州时间 9 日上午 10:38,安比(SECBIT)实验室收到合作伙伴美国硅谷 AnChain.ai 公司消息,基于 AI 的态势感知系统发出预警信息,发现部分游戏合约出现大量交易并且存在异常的资金流动情况。

安比(SECBIT)实验室的小伙伴赶紧根据最新线索,对相关合约和交易进行观察、跟踪、分析。安比(SECBIT)实验室由中国科学技术大学博士郭宇创建,从密码学、代码语义、形式化验证、博弈论、编译器等多种理论角度切入,在智能合约安全技术上开展全方位深入研究。AnChain.ai 由辛辛那提大学计算机博士方春生 Victor Fang 创建,方博士是硅谷上市网络安全公司 FireEye 史上第一位首席数据科学家,负责 AI 产品研发。AnChain.ai 专注安全威胁情报、区块链态势感知,凭借 AI 技术助力区块链生态安全。下文敏感地址只保留前 4 位。片尾有三个彩蛋,智能合约爱好者请不要错过。

hacker.png

悄然上线:莫名火爆的 Last Winner

Last Winner 是一款基于以太坊智能合约的 DApp 游戏,于 8 月 6 日上线,这款游戏一经推出,就“异常”火爆。

这款游戏合约地址为 0xDd9fd6b6F8f7ea932997992bbE67EabB3e316f3C。据 Etherscan 显示,短短六天时间内,该游戏合约就已产生 27 万余笔交易。甚至前段时间以太坊网络大拥堵也与 Last Winner 游戏密切相关。8 月 8 日 和 9 日,在 Last Winner 和 Fomo3D 超大规模交易量的共同作用下,以太坊未确认交易数量创年内新高,平均 Gas 费用一度飙升至正常 10 倍以上。

gasprice.png

该游戏第一轮奖池金额为 1.6 万多个以太币,而玩家总投资额更超过 10 万以太币,资金量巨大。目前游戏第一轮已结束,第二轮奖金池已迅速累积至 7000 多以太币。

疯狂的现象级游戏背后暗流涌动

疑团重重:前期大量参与者的资金来历不明

据知名媒体「区块律动」报道,Last Winner 由名为「蚁群传播」的资金盘传销组织推广运营,有着数量众多的会员和极强的推广拉下线能力 [1]。而据另一款火爆游戏 Fomo3D 开发团队称,Last Winner 是仿 Fomo3D 游戏,其背后运营团队准备了 20 万 ETH 来进行自动刷量交易。因此,Last Winner 游戏火爆的背后,可能是一场精心布局的传销游戏,初期利用机器人发起批量交易,伪造活跃假象,吸引新韭菜入场。

Last Winner 游戏合约存在大量非正常交易,并且伴随着大量合约的创建与自毁,与正常人类调用行为特征偏离很大,这引起了我们的高度警惕。

疯狂推广:只面向国人,合约源码却未公开

在各大论坛、媒体、以及微信群中,都可以见到 Last Winner 游戏的推广文章,而这些文章有着类似的描述,并且都附上推广邀请码。但 Last Winner 英文相关资料非常少。

promo-post.png

显然,这是一款针对中国人的游戏,有着诱人的推广(拉下线)奖励,因此在网络上广为传播。并且,这款游戏有适配安卓和 iPhone 手机的 App,简化了使用操作,降低了参与门槛。

但是,十分可疑的是,作为一款基于智能合约的区块链游戏,Last Winner 居然没有公开合约源代码!这是一个非常危险的信号。为何这样一个游戏能这么火爆,吸引这么多人参加?

我们直觉上感到这款游戏到处透露着诡异的气息。

安全性存疑:实则是 Fomo3D 山寨版

Last Winner 官方宣传语写道:

Last Winner(LW)是首款完全去中心化的类 Fomo3D 游戏 DApp,完全基于以太坊智能合约原生开发。只要下载安装 App 就可参与游戏。

类 Fomo3D 游戏,且未公开源代码,这不得不让人产生怀疑。要知道,短短时间内原创开发一个好玩又安全的 DApp 游戏难度非常大。

安比(SECBIT)实验室迅速使用内部工具逆向分析了 Last Winner 的合约代码(字节码)。果不其然,这款游戏合约代码函数名称与 Fomo3D 高度相似,疑似直接拷贝(抄袭)了 Fomo3D 的源码,但却又新增了 10 余个可疑未知函数

尽管 Fomo3D 在 Etherscan 公开了源代码,但这并不代表它开源给任何人随意使用。

安比(SECBIT)实验室之前报道过:在 Fomo3D 爆红之后,各类山寨版 Fomo3D 层出不穷。之前这些山寨版游戏往往复制 Fomo3D 官网和合约源码,并可疑地在一些地方进行修改。而 Last Winner 在此基础上更进一步,推出移动客户端,并疯狂推广,却不公开智能合约源代码。

智能合约游戏或 DApp 的亮点之一就是公开透明。Last Winner 游戏则完全违背了这一点,动机十分可疑,参与该类游戏的风险极高!

当时严峻的形势是:一方面有多个地址疑似疯狂发起攻击交易,另一方面项目方游戏合约未公开源码,高度可疑却吸引了巨量资金。我们感觉到态势十分紧急,于是迅速开展分工合作。AnChain.ai 中美团队日夜交替分析和监控异常交易,收集证据,定位攻击来源与攻击规模。

安比(SECBIT)实验室的小伙伴们则兵分两路,分别开展对不透明游戏合约黑客攻击手法的逆向分析。

前情回顾:类 Fomo3D 游戏空投机制存漏洞

Fomo3D 游戏参与形式是用以太币购买游戏道具,除了最后一个购买的人可以获得巨额大奖外,平时参与者还有机会获得“空投”奖励。

这里有主奖池和副奖池的概念,最终的巨额大奖和空投奖励分别从从主奖池和副奖池中获取。

所有进入游戏的以太币,有 1% 数量会进到副奖池。每一次购买道具都会有概率获得空投。空投的概率从 0% 开始,每增加一笔不小于 0.1 ETH 销售订单,空投概率会增加 0.1%。同时空投奖金额与购买金额也挂钩,如果购买 0.1 ~ 1 ETH,就有概率赢得 25% 副奖池奖金,购买更多则比例越大。

一进入游戏界面,就会看到鲜明提示,通知当前中奖概率和奖池金额。这一设计,原本是想增加游戏趣味性,并起到吸引资金入场、延长游戏时间的作用。但实际情况却并非如此。

通过观察 LastWinner 游戏合约以及部分地址的异常交易行为,我们心中有了初步答案

让我们把时间退回到 20 多天前,早在 7 月 24 日,安比(SECBIT)实验室和派盾(PeckShield)科技分别同时预警:Fomo3D 游戏的智能合约存在随机数漏洞可被利用,Fomo3D 及所有抄袭源码的山寨合约均存在该安全漏洞 [2]。原本设计上随机性较大的空投游戏可通过特殊手段操纵,大大提高中奖概率。

经安比(SECBIT)实验室字节码智能扫描工具逆向分析,Last Winner 游戏空投奖励代码与 Fomo3D 基本一致,相似度达 91%,可能存在同样漏洞。随着游戏火爆进行,机敏的黑客肯定也闻风而动。

不能说的秘密:黑客制造秘密武器攫取高额收益

在区块链态势感知系统所展现出来的数据面前,我们不由地倒吸一口凉气。

图中的这些可疑地址,如同“病毒”一般紧紧缠绕在 Last Winner 合约四周,肆意吞噬着 Last Winner 内的资金。

我们观察到,图中紧靠 Last Winner 的这些地址,有着类似的行为模式

如:

1.不停地往某合约地址上发起交易,同时附带 0.1 个以太币

2.不少交易状态为失败

3.成功的交易又会涉及大量“内部交易”

4.“内部交易”调用逻辑十分复杂,并伴随大量合约的创建和自毁

安比(SECBIT)实验室迅速得出初步结论:这些不明合约就是黑客用来攻击 Last Winner 的秘密武器,黑客正是通过这些合约,持续吸走 Last Winner 游戏内的以太币。

案发现场:大量类似交易,超高回报率

上面态势感知图中,占地面积最大的嫌疑地址引起了我们的注意:0xae58,于是从这个地址展开了追踪。

8 月 9 号当天,0xae58 地址内以太币余额就以超过 300 个,而当时他正在大量往地址 0×5483 上发起交易,每笔交易转账金额都是 0.1 Ether。显然,黑客正通过 0×5483 智能合约向 LW 发起攻击。

same-txn-in.png让我们观察下面这条状态显示为成功的交易。表面上看是 0xae58 向攻击合约 0×5483 转了 0.1 Ether,实际却涉及了一大堆地址间的相互转账,最终随着 0x7c77 合约 自毁,0.189 个 Ether 转移回 0xae58 的账户中。 

pwn-tx-example.png

这十分神奇,攻击者投入 0.1 个以太币,最终收获 0.189 个,瞬间回报率高达 89%,简直暴利。

我们很快发现,除了 0xae58 地址外,还有四个地址也持续不断地向 0×5483 合约发起类似交易,持续获得高额回报。

而失败的交易,仅消耗 27712 燃料(Gas),成本损耗十分低

研究目标立刻锁定为攻击合约 0×5483。由于无法获得源码,安比(SECBIT)实验室立刻使用内部工具展开逆向分析。

暴利:数据面前我们再次震惊

8 月 13 日,当我们沉浸在研究黑客的攻击合约各种细节优化和精巧设计之时,黑客攻击数据全景分析新鲜出炉。

其中,攻击获利最大的是以 0x820d 地址为首的团队。他们累计获利超过 5000 个以太币。AnChain.ai 团队和安比(SECBIT)实验室将该黑客团伙精确定位,并将其命名为 BAPT-LW20 (Blockchain APT – Last Winner)。

BAPT-LW20 团队在短短 6 天时间内,共发送将近 5 万笔交易,从中攫取 5194 个 Ether,获利价值将近 1200 万人民币。 

由下图每小时发起的攻击交易数量趋势图(下图),我们可以看出攻击的高峰期发生在 8 月 8 日 ~ 10 日,每小时平均攫取将近 100 以太币,将近 22 万人民币。这正好也是 LW 游戏最火爆的时间段。随着游戏进入后期,入场资金急剧下降,收益降低,黑客也不得不也降低了攻击频率。

再看看黑客每小时攫取以太币数量趋势图(下图)。惨淡的漫漫熊市里,黑客却在狂赚以太币。 

lw-hourly-stolen.png

下图是“Last Winner 中黑客的交易量占比和攫取 ETH 占比”,可见黑客发送的交易量只占总交易量的 9.877%,但是去攫取了Last Winner奖金池中49%的奖金。黑客的娴熟攻击技能,为他们带来了普通玩家难以企及的好运,而普通玩家在这场游戏里面几乎很难获得空投奖励。 

lw-attack-txn-and-share.png

火线追凶:BAPT-LW20 团队攻击 LW 始末

安比(SECBIT)实验室尝试追踪复原 BAPT-LW20 团队攻击时间线。

下图是 BAPT-LW20 团队某账户余额变动情况。

fomo-vs-lw.png

0x820d 是 BAPT-LW20 团队所有攻击合约的部署者,也是攻击的实施者之一,可认为是 BAPT-LW20 团队的队长。0x820d 地址最早活跃于 7 月 20 日,账户中的初始以太币均由 0x73B6 地址转入。而 0x73B6 也是同一天开始活跃的新地址,它的初始以太币来自总部位于美国旧金山的 Kraken 交易所。

0x820d 在收到来自 0x73B6 的 10 个以太币后,随即部署了它的第一个合约。可能有些地方不太理想,他并没有继续使用该合约。三分钟后,0x820d 部署下了第二个合约,攻击对象是 Fomo3D。在一组准备工作设置、若干次失败的调用以及两次虽然成功但却没有收益的尝试过后,0x820d 应该是发现了攻击合约里的 bug 和优化空间。

在接下来的 14 个小时内,他依次部署了 8 个合约进行攻击测试,无奈都不成功。终于在第 9 个合约 0xBad7 中首次完成攻击,以 0.1 ETH 的投入换回了 0.125 ETH。

0xBad7 是 0x820d 团队首个可以正常工作的攻击合约,他们在 7 月 21 日至 7 月 23 日三天时间内总计调用该合约 11551 次,小有斩获。

7 月 23 日,0x820d 又部署了新的合约,将攻击对象转移为另一款 Fomo3D 山寨游戏老鼠会 RatScam (0x5167350d082c9ec48ed6fd4c694dea7361269705),0x820d 团队在一天时间内使用了 2299 次攻击合约。

一天后,0x820d 又找到了新目标,一个名为 FoMoGame(0x86D179c28cCeb120Cd3f64930Cf1820a88B77D60) 的山寨游戏,部署新合约(0xb599)进行攻击。这款游戏知名度不高,入场资金并不多,黑客调用了 126 次之后就放弃。

接下来的三天内,0x820d 前后部署了 10 个新合约进行优化与攻击测试。

终于,在 7 月 26 日上线了他们的新版攻击合约(0×5483)。该合约总共发生过 23835 笔交易,最近一次活跃时间在 8 月 10 号(7 天前)。这款攻击合约,可由攻击者 自定义受害游戏合约地址。因此 0x820d 在接下来的几天内,持续混合攻击 Fomo3D 原版、RatScam、FoMoGame 等游戏,并持续观察其他山寨游戏的动态,等待时机。同时,继续部署若干个新合约进行调优测试。

终于,8 月 6 日 Last Winner 游戏上线,24 小时后 0x820d 团队就使用准备好的 0×5483 合约,针对 Last Winner 发起第一次攻击,并在接下来的 4 天内集中力量,疯狂利用空投漏洞展开攻势。

8 月 10 日,0x820d 调用 0×5483 攻击合约 withdraw 接口,提走了里面的余额,攻击疑似暂停。

原来,他们早已经部署了新版合约攻击合约 0x9C10,又发起了超过 30000 笔交易,至今仍在活跃攻击。

不仅仅是空投:BAPT-LW20 黑客团队拿走 LW 最终大奖

北京时间 8 月 17 日上午,Last Winner 游戏第一轮最终结束,最终大奖由 0×5167 地址获得,奖金额总计 7,754 以太币。

real-winner.png

而这个地址正是 BAPT-LW20 黑客团队的五个地址之一

如下图,14 小时前,黑客还在利用攻击合约获取空投奖励。随后,他改变了方案,直接用自身地址购买道具参与游戏,不断尝试夺取最终大奖。在此之后,又继续调用合约攻击 LW 游戏。

attack-and-play.png

安比(SECBIT)实验室猜测黑客潜伏很久,早已做好充分的准备,长时间利用脚本监控 LW 游戏状态,最终才能在众人放松警惕之时获得大奖。

BAPT-LW20 黑客团队利用空投漏洞获利超 5,194 Ether,同时又夺取最终大奖 7,754 Ether,累计获利 12,948 Ether。

同行相杀:Zethr 团队两天时间就成功利用漏洞

这场超大规模的类 Fomo3D 智能合约游戏被攻击事件,攻击者使用的秘密武器也正是智能合约。

据安比(SECBIT)实验室调查分析,0x20C9 地址最先成功利用原版 Fomo3D 空投漏洞并获取奖励。我们将他定位,并将其命名为 BAPT-LW10。

0x20C9 于 7 月 8 日 10 点 07 分创建了攻击合约 0xe7ce,在接下来的十分钟内,前后调用了三次,最终在第四次时成功获得奖励,投入 0.1 以太币,收回 0.19 个,回报率高达 90%(见下图)。

first-pwn-by-etherguy.png

此后,0x20C9 继续部署多个攻击合约,进行调试优化。最终,在 7 月 23 日部署了最终版本 0x39ac 攻击合约,接下来的时间前后调用过 90 余次,而攻击对象涉及 Fomo3D 原版、Last Winner 以及其他山寨版 Fomo3D。

据我们观察,0x20C9 是最早研究并成功利用空投漏洞的黑客。研究过程中,安比(SECBIT)实验室发现 0x20C9 与另一款游戏 Zethr 密切关联。

最终我们在 Zethr 游戏合约代码中发现了他的身影。他是热门游戏 Zethr 的八位核心开发者之一,代号为 Etherguy

zethr-etherguy.png

显然,作为 DApp 游戏开发同行,Etherguy 以及他所在的 Zethr 团队很早就研究了 Fomo3D 项目代码。Fomo3D 合约 7 月 6 日部署上主网,Etherguy 两天后就发现并成功利用了漏洞。从调用规模来看,Etherguy (BAPT-LW10) 应该主要还是出于研究目的,并没有太多获利。

让其他黑客获利最多的正是 Last Winner 游戏。

游戏细节:Last Winner 为何让黑客如此疯狂

从最初 Fomo3D 上线后不久,空投漏洞就已被人发现并成功利用。随着游戏的广泛传播,以及该漏洞被逐渐披露,空投漏洞的攻击手段也在这一过程中不断升级进化,最终部分黑客团队完成了精巧的攻击方案,可低成本、高效率获得奖励,并可大规模工程化地攻击任意任何同类游戏合约,疯狂收割以太币。

据安比(SECBIT)实验室分析,除 LW 游戏以外,不少黑客团队都曾尝试攻击其他类 Fomo3D 游戏合约。但获利都远小于 BAPT-LW20 团队在 LW 游戏中所得。

我们试图从 LW 游戏本身寻找答案。

LW 游戏是 Fomo3D 山寨版,本身没有太多创新,但入场资金完全集中在游戏开始后的第 2 天至第 5 天内。巨量入场资金,会让游戏空投奖池迅速累积,因此这段时间也是黑客攻击的黄金时机。

更要命的是,Last Winner 团队修改了空投游戏参数,使进入副奖池(空投奖池)的以太币比例由 1% 调整到 10%,相当于空投奖励金高了 10 倍!

一方面,游戏运营团队可能是利用高额空投奖励吸引用户疯狂加入;另一方面,他们可能并不知道空投漏洞的严重性,而提高奖励比例则会让该问题进一步放大。

Last Winner 游戏简直成为了黑客的提款机!

特别地,前面提到 Last Winner 游戏第一轮入场资金已达 10 万以太币,这也就是说,单单这一款游戏就有超过 1 万个以太币都持续暴露在被攻击的风险下,成为黑客的囊中之物。要知道,这款游戏第一轮最终奖池也才 1.6 万余以太币。本来空投奖励都是很小的金额,但黑客持续利用空投漏洞,积少成多,终成 Last Winner 最大赢家。

我们追踪到有多个团队对 Fomo3D 及山寨合约开展大规模自动化攻击,企图攫取利益。

而 BAPT-LW20 团队在游戏开始后 24 小时左右就加入了战局,并迅速扩大作战规模,最终占得先机,获取巨额收益。

安比(SECBIT)实验室追踪到还有其他黑客团队向 Last Winner 合约开展攻击。部分黑客 8 月 11 号以后才入局,虽规模也很大,但终究因为错过黄金时机而获利较少。

攻击合约:设计复杂又精巧

攻击合约 0×5483,创建于 7 月 26 日,创建者为 0x820D,同时也是持续调用攻击合约的五个地址之一。

起初,攻击合约的创建时间让我们感到疑惑,前面提到 LW 游戏合约于 8 月 6 日才部署上主网。难道 0x820D 可以未卜先知,或者他与项目方有什么不可告人的秘密?

带着这个疑问,我们尝试从合约 0×5483 的代码(字节码)中寻找答案。

经过逆向发现,该合约有七个公开函数,其中一个疑似函数名是 withdraw(uint256),用于将攻击合约中积累的以太币转走。

安比(SECBIT)实验室在字节码中发现了上面提到的五个地址。原来这两个函数都会跳转到同一个内部函数,检查交易发起人是否是这五人地址之一。如果是,则可继续执行,如果不是,则提前让交易失败。

这也解释了为什么偏偏是这五个地址一直在调用攻击合约。因为他们是一个团队,合约特地为他们而设计,而其他人根本无法正常调用。

初步猜测,攻击合约这么设计是为了分散权限和资金,降低出问题或被发现的风险。

pwn-calldata.png

上图正是一笔攻击交易的传入参数。第一部分是调用函数哈希 ID,后面跟着三个参数。注意看第一个参数,攻击者传入的明显是一个地址。显然,这个地址正是 LW 游戏合约地址。

原来如此,攻击目标对象可以作为参数传入。“黑客真机智!”,我们不由地感慨。之前的一个困惑被解开,早在 LW 游戏上线前就已部署好的攻击合约 0×5483,其实是一个通用型武器

继续研究,接下来合约的复杂程度出乎我们意料。我们沿着生成的控制流程图(CFG)追踪合约函数调用过程,程序指令以及分支情况非常之多(下图是一小部分截图),让人难以完全跟上。

cfg.png安比(SECBIT)实验室使用动态追踪调试技术,结合逆向分析结果与攻击交易内部记录,搞清楚了黑客所使用的手法。 

internal-txns.png

其他攻击交易也都是类似的过程,黑客调用攻击合约,攻击合约再调用提前创建好的合约,进而创建新的合约,以新合约的身份参与 LW 游戏,买游戏道具,然后几乎必定获得空投奖励。

这一过程中不断新建的合约,就是态势感知系统中预警的大量异常合约创建与自毁。

追踪攻击合约调用历史,发现攻击者在部署完攻击合约后,就立即多次调用特定函数,每次新建 10 个新合约。而函数总共恰好调用了 100 次,因此新建了 1000 个新合约(记住这个细节)。

在攻击交易中,攻击合约最先调用的就是这预先创建好的 1000 个合约之一,似乎是特地从中挑选出来一个地址。

智能合约:一切皆可预测

攻击函数控制流程图(CFG)中一个相隔很远的循环引起了我们的注意。

我们恍然大悟。攻击函数所做的就是不断循环地在 1000 个合约中,挑选“合适”的地址来完成下一步新建合约操作。所谓“合适”的地址,就是指能确保每次参与游戏都能获得空投奖励。

前面提到,以太坊智能合约中可以很容易的预测随机数,因为随机数的来源都是区块或者交易中特定的一些公开参数,如交易发起者地址、区块时间戳、区块难度等等。因此空投游戏利用随机数来控制中奖概率是不可行的。

而这里,黑客利用了另一个以太坊的特征,一个地址(账户)创建一个合约,合约地址是可按照特定规则计算得到的,任何人都可以根据已知信息进行推算。

因此,黑客循环利用自己控制的 1000 个合约地址,推算各地址下一次新建的合约地址,而该地址恰恰是空投游戏中奖数字的随机源。攻击合约通过一系列预演推算来筛选出“合适”的地址来完成攻击操作(技术细节后文会详细讨论)。

这才是黑客能够以超高概率获得空投的真正原因!

技术流:攻击手法细节披露

类 Fomo3D 游戏空投漏洞的根本原因在于,以太坊智能合约环境中难以生成无法预测的随机数。而 Fomo3D 开发者在其合约中增加了「判断调用者是普通人类还是合约」的逻辑来尝试规避,但此逻辑实现存在漏洞。黑客利用攻击合约提前预测随机数,并通过在构造函数内调用游戏合约的方法来伪装成普通人类(非合约)地址,从而大大增加自身中奖概率。

时间再次回到 7 月 23 日,以太坊基金会开发团队负责人之一 Péter Szilágyi 在 Reddit 上首次公开爆出这个漏洞并给出 1.0 版本的 POC 方案(详细可以参考引文 [3])。这主要是利用了这些特点:

空投游戏用来控制中奖概率的随机源,可被提前获得

用户能否获得空投奖励以及奖励金额,可在另外一个合约中提前计算出来,从而决定后面的操纵逻辑

Fomo3D 空投机制中尝试限制只有非合约地址(普通人类)才能参加游戏以防止上述情况的发生。但该判断方法存在漏洞,在构造合约的过程中(即合约构造方法里)参与游戏即可绕过该限制。

因此攻击者可以部署一个智能合约,并在合约的构造方法中计算出自己是否能够获益,如果能则投入以太币参加游戏空投获利,否则就不参加(参见下图)。

pwn-v1.png

Péter 提出的这个方案只是一个最简单的原型,因为每次部署合约都要消耗不少 Gas,而且工作效率很低且收益率并不高,采用该方案攻击,发起上千笔交易,都不一定能够真正获得空投奖励。

看到这里你可能会有疑问,上文的攻击者似乎手法更高明,而且实际成功攻击的发生时间要早得多

Zethr 开发者 Etherguy 早在 7 月 8 日就已使用更高明的手法成功获利,解决了上文 1.0 方案中的部分问题,我们姑且称之为 2.0 版本

这个思路是通过合约循环创建子合约(参见下图),直到子合约满足空投条件可以获利为止。这样做的好处是,在 Gas 充足的情况下,每次调用合约几乎一定可以获得收益,提高了工作效率。然而这种方案和 1.0 版本的攻击成本接近,并没有从本质上提高收益率。

pwn-v2.png

而这次事件的最大获利者 BAPT-LW20 团队,就是在 2.0 版本的思路上进行了进一步优化降低了投入成本,提高了收益率。 3.0 版本 则疯狂创建代理合约,通过利用计算下一步新建合约地址的技巧提前预判,筛选出符合条件的代理合约再创建出新的子合约,在子合约的构造函数中再完成上述攻击(见下图)。而且攻击目标地址可配置,可多人同时协作攻击。当游戏奖池金额不足以覆盖攻击成本时,发出的攻击交易会自动提前失败,仅消耗很低的 Gas 费用。

在分析各类攻击合约过程中,我们还见到另外一种更高明的做法:主攻击合约有着良好的设计模型,支持核心算法动态替换与升级。原理上则是利用了 delegatecall 进行操作。安比(SECBIT)实验室会持续关注这批黑客的动向。

彩蛋一:空投与挖矿

我们知道在 PoW 挖矿的时候,矿工通常需要进行如下计算:

BlockHash = Hash(Header+Nonce)
Check(BlockHash < Diff)

当 BlockHash 结果小于当前难度值的时候,代表找到了一个合法的 Nonce。

在 Fomo3D 的空投奖励里有着类似挖矿的机制:

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);
}

用户唯一可以操纵的就是 msg.sender 字段,我们是否可以将 msg.sender 作为 Nonce 来挖矿呢?

答案显然是可以的,智能合约的地址是根据 发起者账户 + nonce 决定的,于是有了第 1 代方法:

                       创建合约
用户(地址+nonce0) --------------------> 新合约(尝试攻击)
用户(地址+nonce1) --------------------> 新合约(尝试攻击)
用户(地址+nonce2) --------------------> 新合约(尝试攻击)
用户(地址+nonce3) --------------------> 新合约(尝试攻击)

但是这种方式需要用户持续部署合约,消耗的矿工费代价非常大,且成功率极低,每次都是以 1/1000 的中奖概率在尝试。

由于第 1 代惊人的失败率,显然无法利用,于是有了第 2 代攻击方法:

这种方法的主要思想是,合约创建的新合约地址由 合约地址+nonce 确定:

      部署合约    |------------------| hash(caddr, nonce)
用户 ----------> |循环创建合约,      | -------> 新合约(尝试攻击)
                 |直到攻击成功或到达终 | -------> 新合约(尝试攻击)
                 |止条件才停止,可能需 | -------> 新合约(尝试攻击)
                 |要部署大量合约。    | -------> 新合约(尝试攻击)
                 |----------------- |

这种方式类似于挖矿,固定区块头部,不断修改 nonce 来试探能否成功获得奖励,但是问题在于,如果在循环第 1000 次才发现合法的 nonce,那么意味着之前部署的 999 个合约都属于浪费 Gas 的操作。

那么如何更高效地寻找合法的 nonce 呢?

我们回想比特币挖矿,一个挖矿任务中,不仅有 nonce,还有 extraNonce [4]。

bitcoin.png

在比特币区块的 Coinbase 字段中,有一个自由修改的区域,修改这个区域会导致 MerkleRoot 被修改,从而实现 Header 被修改,具有 nonce 的效果,因此被称作 extraNonce。 

为什么需要引入 extraNonce 呢?原因在于 nonce 为 32 bit 数字,搜索范围只有 2^32,矿机几乎一瞬间就遍历完了,通过不断修改 extraNonce 来扩大本地搜索范围,我们甚至可以不修改 nonce 只修改 extraNonce 来挖矿。

也许黑客也想到了这一点,他们通过提前部署 1000 个代理合约来实现有 1000 个 extraNonce 的效果。 至此,攻击方法升级到了第 3 代:

                    部署合约
用户 --------------------------------------> 管理合约 C
      调用合约C,预先创建代理合约(extraNonce)
用户 --------------------------------------> 1000个代理合约
      调用合约C,循环尝试可以成功攻击的代理合约
用户 --------------------------------------> 部署合约(尝试攻击)

显而易见,这种攻击方式同时实现了 2 个效果:

提升了攻击成功率

减少了攻击合约部署数量,大大减少了 Gas 消耗。 

彩蛋二:黑客预先创建的合约数量与中奖概率

前文提到黑客预先部署 1000 个代理合约,这个数字有什么讲究呢?

if((seed - ((seed / 1000) * 1000)) < airDropTracker_)

seed 经由一系列以太坊链上环境以及多次 Hash 运算得出。Hash 结果对 1000 取余数,可以得到 0~999 的伪随机数。

我们假设哈希输出结果是均匀的,并且哈希是抗碰撞的,那么平均每次中奖的概率为 1/1000。

模拟结果:

probability-sim.png公式运算结果: 

probability-calculate.png

尽管更多合约能够提供更高的中奖概率,但是需要考虑到 Gas 消耗与 Gas Limit 等因素,不宜过大。

安比(SECBIT)实验室认为黑客选择部署 1000 个合约,是根据概率代码 1/1000 想当然做出的判断。

彩蛋三:黑客可能利用了空投概率计算的另一个 bug

黑客仍然需要更高效地攫取利润,他们甚至“发现”了 Fomo3D 空投规则在这种攻击方式下暴露出的缺陷。

攻击合约需要在最开始获取空投奖励信息,作为后续操作的依据。因此,攻击合约会先依次调用游戏合约的两个查询接口,分别是 0xd87574e0 airDropPot_() 和 0x11a09ae7 airDropTracker_()

Fomo3D 空投奖励的 airDropTracker_ 计算方式为如下方法:

if (_eth >= 100000000000000000)
  {
    airDropTracker_++;
    if (airdrop() == true)
      {...}
  }

Fomo3D 判断是否能中空投奖励使用了如下方法:

if((seed - ((seed / 1000) * 1000)) < airDropTracker_)
  return(true);

根据我们分析,0x820d 后期更新的攻击合约直接去除了对 airDropTracker_ 的判断,但这样做其实有利有弊

如果你了解细节,猜出了黑客的用意,或者知晓这种做法的优缺点,欢迎添加小安同学微信(secbit_xiaoanbi),她会把你加入到「SECBIT 智能合约安全技术讨论」的群里。

尾声:下一个是谁?

8 月 14 日,BAPT-LW20 黑客团队的 0x820d 再次部署了两个新版攻击合约,这一次他们将炮筒又对准了另一款一天前新部署的不知名合约。

望着大屏幕上 AnChain.ai 态势感知态势感知平台不断闪烁的红点,安比(SECBIT)实验室和 AnChain.ai 都很清楚,区块链世界里的战役还远远没有结束。

2009年,中本聪创造了一个虚拟的去中心化新世界。这仿佛是一片流着奶和蜜糖的应许之地,人们欢呼雀跃,蜂拥而至。但与所有的生态系统一样,新世界有生命,就有捕食者。有交易者,就有黑客。区块链上的应用在进化,攻击者也同样,我们给大家展示的是区块链世界不为人知的另一面,暗流涌动。意料之外,也在意料之中。

BAPT-LW20 & BAPT-F3D 大事件时间表:

2018/07/06 Fomo3D 游戏合约上线

2018/07/08 Zethr 核心开发者 Etherguy 发现并利用空投漏洞

2018/07/20 Fomo3D 游戏在国内走红

2018/07/20 BAPT-LW20 黑客团队地址开始活跃

2018/07/21 BAPT-LW20 团队第一次成功利用 Fomo3D 空投漏洞

2018/07/23 BAPT-LW20 团队攻击山寨游戏⽼鼠会 RatScam

2018/07/23 Péter 在 Reddit 爆料 Fomo3D 空投漏洞

2018/07/24 安比(SECBIT)实验室发布 Fomo3D 及山寨版游戏空投漏洞预警

2018/07/24 BAPT-LW20 黑客团队攻击 FoMoGame

2018/07/26 BAPT-LW20 黑客团队部署新版攻击合约 0×5483

2018/08/06 类 Fomo3D 游戏 Last Winner 上线

2018/08/07 LW 游戏开始火爆

2018/08/07 BAPT-LW20 黑客团队开始攻击 LW 游戏

2018/08/09 以太坊未确认交易数创年内新高

2018/08/10 凌晨 AnChain.ai 态势感知系统发出预警

2018/08/10 安比(SECBIT)实验室与 AnChain.ai 联手开展调查

2018/08/10 BAPT-LW20 黑客团队转移旧合约中资金,使用新版合约继续开展攻击

2018/08/11 完成 BAPT-LW20 攻击时间线复原

2018/08/12 完成 BAPT-LW20 攻击手法复原

2018/08/13 针对更多攻击源分析

2018/08/13 完成 BAPT-F3D 和 BAPT-LW20 攻击数据全景分析,黑客获利超 5000 Ether

2018/08/14 BAPT-LW20 黑客团队再次部署新版攻击合约,开始攻击一个不知名合约

2018/08/17 BAPT-LW20 黑客团队夺取 LW 最终大奖 7754 Ether

2018/08/17 安比(SECBIT)实验室完成 BAPT-LW20 攻击事件报告

参考文献

[1] 区块律动:8万笔交易「封死」以太坊网络,只为抢夺Fomo3D大奖?https://mp.weixin.qq.com/s/5nrgj8sIZ0SlXebG5sWVPw

[2] Pwning Fomo3D Revealed: Iterative, Pre-Calculated Contract Creation For Airdrop Prizes!, https://peckshield.com/2018/07/24/fomo3d/

[3] Péter Szilágyi 提出的空投漏洞利用 POC,https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide/, 2018/07/23

[4] AsicBoost – A Speedup for Bitcoin Mining, https://arxiv.org/pdf/1604.00575.pdf, 2016/03/31

以上数据均由安比(SECBIT)实验室和 AnChain.ai 提供。

*本文作者:安比(SECBIT)实验室 & AnChain.ai,转载请注明来自FreeBuf.COM