价值3400万美元的项目灾难,竟然是因为一个单词?

原创 2 收藏 评论
举报 2022-05-11


4月23号,原本是一个平静的周六,一个3400万美元的消息震动了整个NFT圈子。

我们的办公室小哥原本还在忙着为疫情囤菜,不得不放下手里的抢菜软件,第一时间学习了技术分析。

简单来说,就是Akutar这个项目的基础代码出现了问题,导致总共3400万美元的ETH被永久锁定。

这场NFT界史无前例的灾难,迅速吸引了各方关注,很多媒体也cover了这次事件。



事件始末

Akutar是一个由著名棒球运动员Micha Johnson发起的项目,发行一系列梦想成为宇航员的戴着头盔的黑人小男孩。这个项目采用了和Azuki一样的荷兰式拍卖,从官网精致的画风到用心的项目描述,发布之初就收到了大部分人的认可。

其实早在这次事件之前,@RedBeadnDAO成员meows.eth就在推特上提示了Akutar合同之中的潜在风险。

同时@hasan在github上发布了一个POC,并且推文说到,“大家可能觉得我是想抹黑这个项目,但是这个合同中确实存在一个令人担忧的漏洞,并会有被人利用的可能”。


(用户Hasen创建的POC页面)


Hasen试图积极与Akutar团队建立联系,但双方只是讨论了一些恶意攻击的问题,并没有完全解决合同中存在的危险。

甚至呢,Hasen还被说成了FUD(FUD通常指对价格产生负面影响的新闻观点或声明,通常是没有依据的)



于是,在4月23日AkuDreams 3.5以太坊的荷兰式拍卖如期上线,此时人们还没有意识到灾难正在来临。

拍卖过程非常成功,但是在最终提款的步骤时出现了问题,11539.5个以太币被卡在合约里面无法被取出。

由于区块链智能合约具有不可篡改的特性,这个问题是无法修复的。这就导致了这价值3400万美元的11539.5个以太币永永远远被锁死在合约里面。



Akutar团队也第一时间承认了错误,并进行了弥补。但是这笔资金不可能拿出来了,这让人既震惊又难过。

作为这场灾难的结果,对“无根据的FUD”的恐惧已经成为现实。我们应该反省一下,为什么像这样用心的、且被大家所认可的项目往往带来如此糟糕的体验。


代码分析

事情出来第一时间,大家就审查了Akutar的合同代码,这里借用@meows.eth的“审查摘要”说明一下主要漏洞。

总结一下就是:

漏洞一:processRefunds() 会卡住;

漏洞二:出价计数没有随着铸币量正确增加漏洞;

漏洞三:取款需要出价计数正确累加,这最终导致资金永远卡住。

我们先看看项目基本情况,这是ETHERSCAN上的页面,大家眼睁睁看着11539.5个以太币无能为力,有没有觉得狠狠得心疼?

合约地址:0xF42c318dbfBaab0EEE040279C6a2588Fa01a961d


我们把合约代码复制到REMIX,折叠一些不重要的,拉到最后。


这3个校验的目的是在合约层面限制项目方的提款权限,通过三层校检使项目的可信度更高。

为什么我说这个检验的初心是非常好的,他要求所有没中标的用户退款完成之后,才进行提款功能,refundProgress >= totalBids ,从逻辑上是没问题的,提高了项目的可信程度,保证了用户的权益。

但为什么又会出现代币锁死而无法提取的情况呢?

原因就在于第2个校验refundProgress在实现时存在错误,无法达到预期的目标。

这里,refundProgress指处理的退款数量,即退款账户的数量,退款处理函数 processRefunds代码如下:

我们看到refundProgress的比较对象是bidIndex,这里的bidIndex指的是投标账户的总数,在bid函数中,每当有一个账户投标,bitIndex就会加1,且不会重复,代码如下:


以上这些看起来都没有问题,然而...

真正的问题出现在下面这个语句里:

在claimProjectFunds函数中,使用的是refundProgress >= totalBids,进行退款校验比较。

这里的totalBids不是投标账户的总数,而是所有用户总投标的NFT总数,如果1个账户只能投标1个NFT,该比较在数量上不会有问题。

但是显然这是不可能的,实际上在bid函数中,1个用户可以投标多个NFT。只要用一个用户投标1个以上的NFT,那totalBids > bitIndex。

所以在所有人完成退款之后,实际情况是:refundProgress == bitIndex < totalBids

实际拍卖结果refundProgress的数量是3669,而totalBids是5495

但大家还记得之前的提款要求吗?是refundProgress >= totalBids

我想说到这里完全不懂代码的朋友也发现问题了,这第二项代码检验refundProgress是不可能通过的,导致claimProjectFunds函数也不可能执行。

提款这个行为自然永远不可能成功,这3400万美元也就永远地锁在了里面。

这个totalBids,原本应该是bitIndex ,就是这一个单词的错误,引起了一个3400万美元的灾难。


沉痛反思

这是一个比较少见的,在宣发层面非常成功,但是代码拉跨的项目。

Akutar作为一个优质项目,其实团队还是非常负责的,后续处理也非常积极,没有摆烂。OP上稀有的Akutar甚至一度价格超过18e,大有起死回生的意思。

但实在太可惜了,项目方本可以规避这次风险:

一、在项目初期寻找专业的代码审计进行风险规避;

二、不要将安全研究人员的担忧视为没有根据的FUD。

所有从事区块链开发的人其实都明白,如果在这个行业太过谦逊,只会让自己失去机会。所以不管技术有没有到位,直接上手项目才是最好的选择。

而这就是问题的症结所在。

这也是为什么我们看到一些备受瞩目的项目一遍又一遍犯下那些“不可思议”的灾难性错误。

这是血淋淋的典型案例。所有开发者和区块链从业人员都应该深入研究这个漏洞的二阶后果,在之后的项目开发时更加完善自身的合约代码。

让投资者可以在更完善的审计和安全环境下,专注于项目本身的价值。


本文系作者授权数英发表,内容为作者独立观点,不代表数英立场。
转载请在文章开头和结尾显眼处标注:作者、出处和链接。不按规范转载侵权必究。
本文系作者授权数英发表,内容为作者独立观点,不代表数英立场。
未经授权严禁转载,授权事宜请联系作者本人,侵权必究。
本内容为作者独立观点,不代表数英立场。
本文禁止转载,侵权必究。
本文系数英原创,未经允许不得转载。
授权事宜请至数英微信公众号(ID: digitaling) 后台授权,侵权必究。

    评论

    文明发言,无意义评论将很快被删除,异常行为可能被禁言
    DIGITALING
    登录后参与评论

    评论

    文明发言,无意义评论将很快被删除,异常行为可能被禁言
    800

    推荐评论

    暂无评论哦,快来评论一下吧!

    全部评论(0条)