Cover事件分析

2020年12月28日上午08:08:12 + UTC ,Cover Protocol的挖矿合约(Blacksmith)出现漏洞滥用现象。

根本原因

正如许多人解释说,这行代码导致在deposit时update池状态对矿工的状态无效。

自从经过审核之日起部署该合约以来,Cover Protocol Blacksmith合约中就存在该漏洞。

一名社区成员在UTC于12月27日下午6点通知Cover团队,他们的通过deposit获得了一些cover奖励。在调查了该问题之后,Cover团队尝试通过实施以下措施来缓解该问题:

  1. 在deposit操作之前添加update pool操作,以便在用户存款时更新池,这将缓解使用UI的用户的问题。

池更新的频率越高,矿工获得的额外奖励就越少。Cover团队分析认为,以上两项应将问题缓解该漏洞,让漏洞值得被黑客利用。实际上,团队没有预料到黑客攻击导致额外奖励被放大很多倍,造成了巨大损失。

错误的主要来源是矿工存款的数量与池预存款中存在的总lp Tokens之间的差。差异越大(例如1wei与1e18 wei),上述错误引起的额外奖励就越大。如果差异足够大,那么经过的时间就无关紧要,矿工仍然可以获得无限数量的COVER代币。

如果Grap Finance与合约发生了互动,则池中会剩下1 wei。它将奖励乘以1e18 +,从而铸造了40万兆个COVER。

这种情况发生在所有存入少于1e18 lpToken的所有池中,其中大多数是新池,并且Cover Protocol: Exploiter 1(0xf05ca010d0bd620cc7c8e96e00855dde2c2943df)是第一个采取行动的人。

Exploiter 1的时间表

2020年12月28日至04:09:27 + UTC

通过团队的多重签名交易向Blacksmith合约添加了一个新的Balancer池。

2020年12月28日至08:08:12 + UTC

攻击者执行了合约的第一笔存款,存入1,326,880个BPT令牌

source:https://etherscan.io/tx/0xd721b0ef2886f14b75548b70d2d1fd82bea085ca24f5de29b833a64cfd8f7a50

2020年12月28日至08:11:16 + UTC

然后,同一攻击者调用withdraw函数,提取了个703.64 COVER以及 1,326,878.99 个BPT

source:https://etherscan.io/tx/0xadf27f5dd052482d46fdf69a5208a27cc7352522c7c19bbde5aee18f6ea4373b

2020年12月28日至08:47:15 + UTC

可在此处找到被利用的COVER代币的首次出售:

source:https://etherscan.io/tx/0x66128a1685605b1798c852e14db0b0232a56e3bebf7f3f35b168642801754beb

在此期间,有多个帐户滥用该漏洞,并在市场上出售其COVER。

2020年12月28日至09:18:28 + UTC

攻击者继续铸造,并且攻击媒介仍然存在。

source:https://etherscan.io/tx/0xf81fb72ee096e0d7afe4b99a55b723110604fb26ec82846043cfc396e1fa79da

总共,Exploiter 1窃取了大约440万美元的用户资金,并将其转移到了该地址。Cover团队正在积极跟踪此地址以及其他参与该漏洞利用的地址。

第二波大的攻击:grap.finance的时间表

2020年12月28日至11:54:47 + UTC

grap.finance部署者将15,255.552810089260015362 BPT(DAI /基础池)存入了blacksmith合约。

source:https://etherscan.io/tx/0x77490baee41a9b35a6e87d49453c7329c7517c10ce6ce26b4c142692a2877e65

2020年12月28日至11:58:04 + UTC

grap.finance:部署者撤回了15,255.552810089260015361 BPT(DAI /基础池),在blacksmith合约中仅剩1 wei。

source:https://etherscan.io/tx/0x88ce99fc1cb695db82d83ce5fe587396744841d3a123687f95b18df6a3106818

2020年12月28日至11:58:56 + UTC

另一个用户从合约那里提取了他的大部分余额(1,007.599009946121991627 BPT)。现在,仅grap.finance就拥有合约上DAI /基础池的全部流动资金,正好为1 wei。

source:https://etherscan.io/tx/0xa27fb73caddb1cf24aa7a5afe84eed13db2f0a889a6ee0f3d5e6226a76c0fd9c

2020年12月28日至12:00:21 + UTC

Grap Finance:部署者根据blacksmith合约退回15,255.552810089260015361 BPT(DAI /基础池)。

source:https://etherscan.io/tx/0xbd1fcda7006ddd58b18cb3bfbd01ef2d1a979be596e1c73be1d7d65fd7eb8215

2020年12月28日12:02:04 + UTC

Grap Finance:部署者要求奖励,并且由于只有1 wei的余额加上存储/内存问题,导致铸造了40,796,131,214,802,500,000.212114436030863813 $ COVER。

source:https://etherscan.io/tx/0xca135d1c4268d6354a019b66946d4fbe4de6f7ddf0ff56389a5cc2ba695b035f

2020年12月28日至12:29:03 + UTC

Grap Finance:Deployer开始通过多次交易中的1inch.exchange出售尽可能多的代币。

source:https://etherscan.io/tx/0xaf94d9b537a13819e873b37160594af2b1cc70b420d0b160a02e341566866a6b

source:https://etherscan.io/tx/0x01b3517845ed9c6b7b40d57bd71ac1a89fec080c5b8988f764d8226ac5caa959

2020年12月28日至12:59:27 + UTC

grap.finance burn 掉了铸造的所有令牌

source:https://etherscan.io/tx/0xe6c068ca3605228b2435a414f2b372057340f77d3fe9f1d3967eb1ad128cb5d2

2020年12月28日下午01:41:01 + UTC

Grap Finance:部署者通过出售$ COVER将他们提取的4351(1 + 4350)ETH发送到cover部署者帐户,该帐户占漏洞利用总损失的34%(940万美元)

source:https://etherscan.io/tx/0x23cb9bdf14eed955a84da3f3cfcf296356c0f897dec0b99e85151a7f084a3051

source:https://etherscan.io/tx/0xc2fd5094c1e108f83222a86bd46b35fc0da35616385d681964b22003643f982e

对于漏洞的产生原因分析

Solidity中的Storage和Memory关键字类似于计算机的硬盘驱动器和计算机的RAM。与RAM相似,“固态存储器”是存储数据的临时位置,而“存储”在函数调用之间保存数据。Solidity Smart Contract在执行期间可以使用任何数量的内存,但是一旦执行停止,该内存将被完全擦除以进行下一次执行。另一方面,存储是持久性的,而智能合约的每次执行都可以访问先前存储在存储区域中的数据。

以太坊上的每笔交易都会花费我们一定量的gas。gas消耗越少,Solidity代码越好。与存储的gas消耗相比,内存的gas消耗不是很明显。因此,始终最好使用Memory进行中间计算并将最终结果存储在Storage中。

  1. 默认情况下,结构,数组的状态变量和局部变量始终存储在存储器中。

下图是创建了一个合约来演示’storage’关键字的示例:

当我们在上面的代码中检索数组编号的值时,请注意,数组的输出为[0,2]而不是 [1,2]。

第二个关于关键字memory的示例:

当我们在上面的代码中检索数组编号的值时,请注意,数组的输出为[1,2]。在这种情况下,更改myArray的值不会影响数组编号中的值。

因此,对于blacksmith合约的分析如下:

当前协议利用 pool.accRewardsPerToken 来计算第 130 行的 miner.amount.mul(pool.accRewardsPerToken).p(CAL_MULTIPLIER) ,由于 118 行的 pool 类型为 memory, 而 121 行的函数 updatePool()并未对其进行更新,导致最终计算出来的 rewardWriteoff 比预期的数额小。

在 DeFi 的世界里,崇尚「代码即法律」(Code is Law),但是defi安全问题层出不穷,这更应该引起我们的警惕。

--

--

Distributed blockchain research institution. Focusing on underlying technology research and practice. Support us: http://giveth.io/project/cyc

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
CYC

Distributed blockchain research institution. Focusing on underlying technology research and practice. Support us: http://giveth.io/project/cyc