本期大赛规则与相关代码见白帽黑客挑战赛 赛制与规则说明。本比赛最大的原则:凡是规则不禁止的,都特么是提倡的!!!
大赛涉及合约共有四个:GamerVerifier
、CommonWalletLibrary
、oobserver
和TimeDelayedVault
。
GamerVerifier
该合约存有本次大赛所有组参赛选手的比赛账号地址,我们可通过在etherscan上查询这些地址的交易信息,得知所有选手的分组情况以及他们组比赛合约地址。
addGamer
函数允许选手向该合约中添加非比赛账号,不过需要破解magic的值。
CommonWalletLibrary
该合约是所有参赛组的钱包库合约,若该合约被摧毁,那么所有参赛组的合约都不能正常运行,直接游戏结束。所以要尽快找到库合约地址,并成功占领owner。
addToReserve
函数主要用于向库合约打钱,更新lastUpdated,使得owner在12小时内不能摧毁合约,让游戏可以继续运行下去。initializeVault
(画圈圈!!重要!重要!重要!),第一个调用该合约的地址将抢占owner,拥有游戏主导权。basicWithdraw
用于用户一匹一匹向外搬砖,每30min可取出0.001eth。resolve
用于owner摧毁库合约。
TimeDelayedVault
该合约是各个小组比赛合约,每个小组一个地址,所以抢占该合约是此次比赛首要任务。在其他队没有意识到抢占比赛合约之前,可自己通过一些手段查找到他们的比赛合约,及时抢占,他们的生死掌握在你们的手上。
addToReserve
与库合约类似,主要用于对其他组合约添加ether,使得他们没办法通过摧毁合约取出大量ether。withdrawFund
用于本组用户从合约中搬砖。通过此方法取出的ether数量相当少,且每半小时才能取一次,且消耗gas相对于取出的ether太多,所以通过该方法取出的eth往往得不偿失。不过从积分计算规则可看出,通过漏洞取出第一笔钱的时间越短,积分越高。所以在抢占owner之后30分钟内要全组投票通过自己部署的Attacker合约地址,用于reentry攻击。addAuthorizedAccount
用于全组成员投票Attacker地址,必须全队全票通过才算成功,所以考验团队凝聚力的时刻到了,每个参赛选手都必须时刻关注自己小组情况。initilizeVault
用于摧毁比赛合约,取出合约中所有的ether。一定要趁早向合约中打入大量ether,以最快的时间摧毁该合约。否则等其他队意识到这个问题,就只能眼巴巴地看着他们给你的合约续命。
在比赛开始前几个小时,队长xd发给我们一份已知的solidity漏洞的攻击与防御方法(Solidity 安全:已知攻击方法和常见防御模式综合列表)。
比赛规则及合约代码发布时间为北京时间20:00,队长xd在对规则及积分公式进行解读后,列出几条纲要:
- 尽量破解获取更多的ether
- 第一次成功破解时间尽量小
- 各成员的调用合约产生的消耗ETH/GAS尽量少,如果有找到漏洞的一定要及时爆出来呀···
- 重生次数一定要少
因老董在最后一节课时讲到过reentry漏洞,所以很容易就能发现withdrawFund
函数存在此漏洞,不过当时并没有太在意第一次破解速度,依旧在理解规则及代码。。。
既然要从合约中大量取ether,所以需要向合约中冲入大量ether。于是我创建了一个其他账号,并部署了一个自毁合约,向比赛合约强制打入10ether。(使用此方式充钱不需要使用gamers中的地址,且不会更新lastUpdate)。此时,我们队比赛合约拥有11ether。
不过在阅读TimeDelayedVault
合约时发现address(this).call(bytes4(sha3("initializeVault()")));
和已定义的函数名initilizeVault
似乎有一点小小的差别,居然少写一个a
!!!于是我们分析出该合约在部署时并没有对owner进行赋值,所以我赶紧使用我的比赛地址调用此函数,成为我们组的owner。这时zc通过查看我们组比赛合约创建者的交易,发现还创建了其他10个合约,我们当时一致认为,这不就是其他组的合约地址吗??赶紧进去查看了一下,发现owner都没有被占用!!
于是,我和队长xd突然有了一个羞羞的念头,把那些合约全部抢占!(当时还不知道这是助教挖的一个坑,差点被埋进去。。。)
有了存和取大量ether的方法,通过公式,我们下一步需要最快破解一小部分ether到我们账户,所以就只能使用reentry攻击。我和zc编写了Attacker合约后,将地址发送给组员,等待他们投票。因我们组有一人已经下线,所以我们决定第二天再使用此攻击。
想到其他组合约已被我们“占领”,就这样带着愉悦的心情,进入了美好的梦境,准备第二天起床收菜。
早上起床后,第一件事情就是尝试炸掉“其他组”的合约,在调用resolve
之后,惊奇的发现,那1ether竟然转到了其他账户!于是我们又继续炸掉了几个那所谓的其他组的合约,发现结果还是一样。当时我们不得不怀疑此方法的可行性。所以当务之急是利用reentry从合约中将ether一点一点向外搬。与之同时,zc已将oobserver
合约部署及设置完成,并且负责监视我们的oobserver
是否被别组修改。
当时所有组员都已上线,所以投票速度很快,不过过程相当曲折。只要有一个组员投错,就得重新投票。我们队也在此花费了不少gas,对最后积分或多或少有点影响。使用reentry攻击合约成功后,距离比赛开始已过了18小时。。。
这一晚我们干了一系列大动作:炸合约、刷币、攻击库合约、重置合约等。
我们目标需要破解更多的ether,所以我们需要向合约中打入大量ether。metamask手动撸币效率太慢,且有10ether上限。若达到上限,需将币转出。所以sz在吃着火锅唱着歌之后,写了一个自动刷币转发币的脚本。于是,我们备用地址中ether数量嘿嘿嘿~
当晚没有找到太好的漏洞能一下破解出大量的ether,所以我们决定还是尝试采用摧毁合约的方式,转出合约中所有的币。因为此时我们已经意识到那些地址并不是其他组的合约地址,因为那上面的交易信息就没有更新。感觉自己从身体到灵魂都被助教调戏。我们越在坑里挣扎,助教就越兴奋。
咳咳!当时我们已经撸了接近10000币,由于担心还是出现之前的问题,我只向合约中打入了2000个ether。深夜,在组长带领组员的讨论下,我们还是决定摧毁比赛合约。结果一出来,大晚上的睡意全无(当时已凌晨3点)。然后我们联系了助教,决定重置合约(在现在看来是过于在乎那刷出的几万ether,想将最后积分刷上天,没有考虑到其他组员续命的情况……)。在助教重新部署新合约之后,我迅速占领了新合约。后来我们反应过来应该用其他队员的地址抢占owner,否则我们的新合约地址太容易被发现。
由于我们还想将刷出的几万币打入该合约,炸掉后再取到参赛地址上。担心库合约owner随时爆炸,于是我们向该合约打入了3ether,使得他们不能在我们新合约炸毁前炸(之前消耗的gas相对于这3ether简直九牛一毛,现在看来多此一举)。
接着,我们伴着刷币的轰鸣声进入了睡眠,等待第二天继续收菜。。。(现在已凌晨4点过)
伴随微信“叮叮”的声音,想着今天的事不会太多,就没有理会,一觉睡到11点。起床后得知,sz已经刷到20000多ether,差不多吧水管撸爆。
今天的任务本来是破解magic,但起床后发现我们的新合约已经被别人发现,并续了命。而且12小时后游戏早已结束,想通过销毁合约获得ether已成为不可能。刷出的币已经不能用于炸毁合约。通过助教得知,我们通过昨晚炸出的2000多个币,现在优势很大。所以我们现在只需要向其他组合约续命就能确保我们的优势。
因zj在此前已通过gamers中的参赛选手地址,已经获得其他小组的合约地址。我依次搜索,并向里面打入了0.01000001ether,成功更新他们的lastUpdate
,使得他们不能炸毁合约获得大量ether。
不过已有小组和我们一样,重生了合约,这些合约地址暂时还没有找到。从助教那听说他们已经开始屯币,一旦炸掉,我们的优势全无。
现在时间已经到了下午5点,距离比赛结束只剩3个小时。
不过因为一些场外原因,我们得知了他们合约新建的大概时间点。哈哈哈,关键时候还是助教管用。。。
得知他们几乎要到比赛结束才能炸毁合约后,我和sz决定在ether网络上scan早上7点到8点之间部署的合约。因为我们比赛的合约有很大的特点,及助教在新建时会向合约中打入1ether,所以我直接进入etherscan,找到相应时间段的交易,人工寻找某地址向某合约打入1ether那笔交易(也是因为时间区间短,不然还是得靠sz的code)。在寻找了半个小时后,果然找到一笔交易,在经过abi调用该地址的相应方法后,验证此地址及那只潜水队的地址。于是迅速打入致命的0.01000001ether。
游戏结束!
本来觉得挺轻松的一天,其实还尝试其他破解方法。比如是否可以修改lastUpdate
以及破解助教们存有所有小组地址的google docs,最终都没能成功。
最终我们组一共取出2011ether
,但目前各小组积分还未公布,暂时还不知道排名情况 ^_^
-
组长每阶段都会分配每人工作,且每个组员都能及时得到响应。感谢小姐姐为我们分配了一个优秀的团队!
-
语音群聊+修仙作战。这两天几乎都是处于和组员连线,且两天睡眠不足10小时。
-
大胆的尝试。由于之前被助教调戏,不敢resolve我们的合约。但在经过全组成员讨论之后,还是决定大胆尝试。队长的建议必不可少!
-
刷币脚本six six six!!!