硬核解析 Uniswap 与 Lendf.Me 攻击全过程

加密谷Live view 82 2020-4-20 18:32
share to
Scan QR code with WeChat

UTC时间2020年4月18日上午12:58:19发生了一起针对Uniswap imBTC流动池的重入漏洞攻击。大约24小时后,UTC时间2020年4月19日上午12:58:43 Lendf.Me上也发生了类似的黑客攻击事件。从技术上讲,这两起事件背后的主要逻辑是,兼容ERC777的“transferFrom()”的实现有一个回调机制,这可使得攻击者可以在余额真正更新之前(即在_move()内部),彻底地劫持交易并执行其他非法操作(通过“_callTokensToSend()”)。

具体来说,攻击者利用该漏洞耗尽了Uniswap流动性池中的ETH-imBTC(约有1278个ETH),而Lendf.Me攻击事件中,黑客利用该漏洞伪造了其在Lendf.Me中imBTC抵押品金额内部记录,这样他就可以从所有可用的Lendf.Me流动性池中借到10多种资产(资产总值为25,236,849.44美元)。

根本原因分析

我们通过研究源代码发现,“transferFrom()”操作的“from”地址注册成为“implementer”(通过标准化ERC1820界面)时调用“tokensToSend()”函数的内在逻辑就是事件涉及的漏洞所在。为了清晰展示,我们在“tokensToSend()”函数被调用时对部分代码进行截图如下所示。就如在1054行显示的那样,ERC1820的“getInterfaceImplementer()”被用来收回已注册的implementer。该函数有两个参数:“from”和“TOKENS_SENDER_INTERFACE_HASH”,第一个参数其实是攻击者参数(比如该参数可以向Lendf.ME提供imBTC),而第二个参数是一个常数,即“keccak256("ERC777TokensSender")”。在1056行的代码中,“implementer”中定义的“tokensToSend()”函数被调用了,这使得攻击者可以通过填入其他恶意执行代码来黑进相应交易。

硬核解析 Uniswap 与 Lendf.Me 攻击全过程

正如OpenZeppelin在2019年4月的帖子和去年7月发布的概念验证(proof-of-concept)漏洞所描述的那样,攻击者可以通过“InterfaceImplementer()”与定义上文提到的“tokensToSend()”的智能合约设置钩子(hook)。

在“tokensToSend()”函数中,攻击者可以填入其他的逻辑代码,比如多次出售相同规模的Token。

硬核解析 Uniswap 与 Lendf.Me 攻击全过程

Uniswap攻击事件

由于前面已经介绍了Uniswap攻击事件背后的理论,所以我们在这篇博客中不再赘述。本文中我们研究了一个特定的恶意交易:(hash: 0x9cb1d93d6859883361e8c2f9941f13d6156a1e8daa0ebe801b5d0b5a612723c1)。

显然内嵌的“tokenToEthSwapInput()”又被调用了一次,这意味着攻击者又进行了一次将imBTC兑换成ETH的交易(当兑换比率被攻击者操纵至对其有利的水平时)。

硬核解析 Uniswap 与 Lendf.Me 攻击全过程

Lendf.Me攻击事件

针对于Lendf.ME的黑客事件作用机制有所不同,但是本质上是一样的。

如果我们研究下以下恶意交易:(hash: 0xae7d664bdfcc54220df4f18d339005c6faf6e62c9ca79c56387bc0389274363b),

由于在存入函数中,Lendf.Me中的“supply()”函数被嵌入一个额外的提现(withdraw())函数钩子(hook),这导致攻击者可以在不实际存入资产的情况下,伪造其imBTC抵押品余额的内部记录。

硬核解析 Uniswap 与 Lendf.Me 攻击全过程

攻击者确实首先向Lendf.Me提供了确定数量的imBTC(即289.999999999 imBTC)。然而在第二个“supply()”中,攻击者仅实际提供了0.000000001个imBTC,并且其后来通过“withdraw()”的钩子又取出了290 imBTC(攻击者黑入了“dotransferIn()”中“imBTC:transferFrom()”的调用——1583行)。从而使得290imBTC在钩子函数“withdraw()”中从攻击者的余额中成功扣除。然而,当代码又回执至“supply”时,余额又被重置为290imBTC(1599行)。这就是攻击者如何在lendf.Me中操纵imBTC抵押品余额数量的内在逻辑。通过多次上述操作,攻击者将其在Lendf.Me中的imBTC内部记录刷到了足够从多个流动池(总资产价值为25,236,849.44美元)中借出可用的10多种资产。

硬核解析 Uniswap 与 Lendf.Me 攻击全过程

缓解措施

作为阻止这种重入攻击的缓解机制,Checks-Effects-Interactions确实能够在这两个事件中起到帮助。如果Lendf.Me“supply()”在存储用户的代币余额更新后调用“doTransferIn()”,攻击者就不可能通过调用“withdraw()”重置余额记录。

从另一角度分析,ERC777原生地支持钩子(hook)机制的运行,因此我们需要防止其所有交易类型的重入攻击。比如,如果“supply()”和“withdraw()”都在函数起始部分设有互斥锁,那么攻击者就不可能使用“supply()”中的“withdraw()”。最后但同样重要的是,我们需要重新回顾一下在ERC20与 ERC777之间进行选择时,选择ERC777正是考虑到其能更好的保护用户资产(因为每次交易操作时,不需要用户将所有的Token都授权给平台)。

写在最后

Lendf.Me黑客事件对于当前的DeFi社区而言确实是一个打击。在下面的图片中我们收集了在该事件中各项资产的损失额数据。

硬核解析 Uniswap 与 Lendf.Me 攻击全过程

btcfans公众号

Scan QR code with WeChat

Disclaimer:

Previous: Uniswap 如何成为 DEX 的最强王者? Next: Uniswap 近一年的市场表现如何?

Related