SharkTeam:十大智能合约安全威胁之函数恶意初始化

语言: CN / TW / HK

问:我们常提到的智能合约漏洞真的是实际中威胁最大、发生最频繁的安全漏洞吗?

答:完全不是那样。例如“溢出”、“外部调用”等常提到的智能合约安全漏洞并不是最常发生,威胁最大的。

到底哪些安全威胁从发生频率和危害性上能称为Top10的呢?SharkTeam合约安全系列课程之【十大 智能合约 安全威胁】和您一起讨论和深入。第六课【详解函数恶意初始化】。

一、什么是初始化函数

初始化在计算机编程领域中指为数据对象或变量赋初值的做法,如何初始化则取决于所用的程序语言以及所要初始化的对象的存储类型等属性,用于进行初始化的程序结构则称为初始化器或初始化列表。

初始化函数的意思是,当你创建一个实例的时候,这个函数就会被调用。我们在部署合约后进行初始化合约时,一般不会采用传递构造函数参数进行初始化合约。

为什么不会采用构造函数方式constructor(uint256 _a)进行初始化,却采用上面所展示的方式进行初始化呢?

(1)不需要处理复杂的构造函数参数,可以更容易被Etherscan验证

(2)当正常工作流需要参数时,部署和配置不需要同时进行,这更加灵活点

(3)允许我们的智能合约升级

(4)可用于避免在传递超过 16 个变量时出现“Stack too deep, try removing local variables”错误

但是,采用这种方式进行初始化会有一些问题:

(1)权限未严格鉴别:未严格设置初始化函数调用者的权限,攻击者会自由利用权限漏洞进行恶意初始化。

(2)多次调用自定义初始化函数:自定义了不安全的初始化函数,没有对调用次数进行限制,攻击者利用该漏洞重复调用初始化函数,导致攻击者可以轻易的重新初始化资金池。

二、攻击事件分析

2.1 DODO

2021年3月9日消息,DODO的wCRES/USDT资金池遭受攻击,攻击者转走了价值约90万美元的wCRES和价值约113万美元的USDT。DODO是一个近期很热门的DeFi项目。攻击者利用资金池合约的初始化函数漏洞,从资金池盗取了133,548.93858472505wCRES和1,139,456.204397USDT。DODO官方发推表示,此次攻击只和DODO V2众筹池有关,作为预防措施,已经关闭DODO资金池创建入口。本次事件攻击流程如下:

(1)攻击者将FDO和FUSDT两种空气币转入wCRES/USDT资金池

(2)通过DODO闪电贷借出133548.93858472505个wCRES和1139456.204397个USDT

本次攻击的执行trace如下图所示,上面红框表示攻击者向资金池转入FDO和FUSDT的过程,下面的红框表示攻击者向资金池借贷的过程。

可以看到攻击者在调用flashLoan函数的过程中执行了init初始化函数,并且输入的baseTokenAddress参数为FDO, quoteTokenAddress参数为FUSDT。通过分析源码中的init函数可知,攻击者利用该函数将资金池重新进行了初始化,此时资金池被重新初始化成了FDO/FUSDT池。因此,攻击者可以规避闪电贷的贷款归还机制。

问题分析:DODO资金池的初始化函数init没有对调用者的权限进行严格的校验,也没有做一些防止重复调用的措施,这才导致攻击者可以轻易的重新初始化资金池。

2.2 Punk Protocol

2021年8月10日,去中心化年金协议 Punk Protocol遭到攻击,损失 890 万美元,后来团队又找回了 495 万美元(向黑客2提供了 100 万美元作为漏洞发现费,黑客2归还495万美金)。

在这次攻击事件中,黑客1有两笔攻击交易,一个是试探攻击,一个是正式攻击。黑客2也有两笔攻击交易,不过其中一笔是inputdata带有通知项目方的信息。

(1)黑客1试探攻击:0x7604c7dd6e9bcdba8bac277f1f8e7c1e4c6bb57afd4ddf6a16f629e8495a0281

(2)黑客1正式攻击:0xa76cd31bcd48869621e7f2698ac8754699026acd0655a6d33280224dabed4cfa

(3)黑客2攻击:0x597d11c05563611cb4ad4ed4c57ca53bbe3b7d3fefc37d1ef0724ad58904742b

(4)黑客2通知项目发攻击:0x4c8072a57869a908688795356777270a77f56ae47d8f1d869be0d25e807e03b1

接下来,以黑客1正式攻击交易为例,展开详细分析:

(1)首先,黑客的攻击执行了delegateCall,将攻击者的合约地址写入到compoundModel中initialize函的forge_参数。setForge(address) 函数在初始化函数中执行。这是一个修改Forge地址的功能

(2)其次,它执行withdrawToForge函数并将所有资金发送到攻击者的合约,然后在调用initialize函数发现forge参数已经被替换成攻击者合约的地址。链接到forge的所有CompoundModel都使用相同的代码,因此所有资产都转移到攻击者的合约中。目前,导致黑客入侵的代码已被项目方修补。添加了两个Modifiers(仅Creator,initializer),这样只有Contract Creator可以调用Initialize函数并控制它只被调用一次。

问题分析:本次攻击的根本原因在于 CompoundModel 合约中缺少对初始化函数的安全控制,可以被重复初始化。

2.3 Nomad

2022年8月2日,跨链协议 Nomad 遭受到黑客攻击,损失超过1.9亿美元。该事件有多笔交易,我们选择其中一笔交易进行分析。交易hash:0xb1fe26cc8892f58eb468f5208baaf38bac422b5752cca0b9c8a871855d63ae28

交易调用了process函数:

进一步分析后发现,在调用acceptableRoot函数时,输入的参数为0,即messages中并没有对应的消息,结果返回ture。

confirmAt本身的表示root被确认的时间,当root为0时,实际是没有被确认过的,confirmAt的值应该也为0,最后会返回false。正是这一点小失误造成了巨大的经济损失,对整个项目造成了近乎毁灭性的打击。

从合约中可以看出,合约的初始化函数initialize会将confirmAt的值初始化为1,初始化函数需要在合约部署后即刻调用,一般由合约的部署地址即owner账户来调用。我们查看了owner账户以及其所有的交易,发现是在初始化时将confirmAt[0]设置成了1。

(1)owner: 0xa5bd5c661f373256c0ccfbc628fd52de74f9bb55

(2)txHash: 0x53fd92771d2084a9bf39a6477015ef53b7f116c79d98a21be723d06d79024cad

该合约是升级后的合约,初始化函数是在合约升级后对合约进行初始化。对比于升级前的实现合约(0x7f58bb8311db968ab110889f2dfa04ab7e8e831b),初始化函数相同,但process函数是不同的,升级前的process函数如下:

问题分析:本次安全事件的根本原因是可升级合约在合约升级的过程中存在失误,容易被攻击者利用初始化函数漏洞,进行恶意攻击。

三、预防措施

我们应该采取哪些措施去避免函数恶意初始化?

(1)初始化函数应只能调用一次,而且需要进行调用者权限鉴别

(2)如果合约是使用初始化函数,而不是在构造函数中进行初始化,则应使用安全合约库中的初始化器来进行初始化。避免合约被恶意操纵,造成合约关键参数和逻辑的错误

(3)项目上线前,需联系专业的第三方专业审计团队进行审计

关于我们: SharkTeam 的愿景是全面保护Web3世界的安全。团队成员分布在北京、南京、苏州、硅谷,由来自世界各地的经验丰富的安全专业人士和高级研究人员组成,精通区块链和智能合约的底层理论,提供包括智能合约审计、链上分析、应急响应等服务。已与区块链生态系统各个领域的关键参与者,如OKC、polygon、Huobi Global、Polkadot、imToken、ChainIDE等建立长期合作关系。

Telegram:http://t.me/sharkteamorg

Twitter:http://twitter.com/sharkteamorg

更多区块链安全咨询与分析,点击下方链接查看

D查查|链上风险核查 http://m.chainaegis.com