全面揭露 bZx 闪电贷事件

全面揭露 bZx 闪电贷事件

全面揭露 bZx 闪电贷事件


​作者 | PeckShield


本文我们将介绍关于bZx攻击者的交易行为,该攻击者的交易行为最近占据了推文与媒体的头条。人们对该攻击者的性质存在很多误解。我们强调这不是针对Oracle的攻击。相反,这是一个聪明的套利行为。该攻击者利用了bZx智能合约执行中的一个漏洞,允许本应锁定的bZx资金流向Uniswap,并进一步将流出资金吸收到一个Compound头寸中。我将通过深入的利润分析来揭露该攻击者的行为。


全面揭露 bZx 闪电贷事件

bZx Hack I


全面揭露 bZx 闪电贷事件

Bzx事件的五个套利步骤


套利的五个步骤


罪魁祸首:

0xb5c8bd9430b6cc87a0e2fe110ece6bf527fa4f170a4bc8cd032f768fc5219838发生在2020年2月15日以太坊区块高度9484688期间。如上图所示,该攻击可以分成5个不同的步骤:Flashloan Borrow, Hoard, Margin Pump, Dump, Flashloan Repay。接下来,本文将逐一拆分每个步骤。

第一步:Flashloan Borrow。该步利用闪电贷的特性从dYdX中借出10,000 个ETH。这一部分不做细讲。

全面揭露 bZx 闪电贷事件


这步骤完成后,我们注意到攻击者具有以下细分资产。但是还没有产生收益。

全面揭露 bZx 闪电贷事件


第二步:Hoard。攻击者将5, 500个ETH存入Compound作为抵押品,并借出112个WBTC。该步是正常Compound操作,而这些WBTC为第4步做准备。

全面揭露 bZx 闪电贷事件


完成此步骤后,我们看到攻击者控制的资产发生了以下变化。但很显然,还没有任何收益。


全面揭露 bZx 闪电贷事件


第三步:Margin Pump。该攻击者利用bZx的保证金交易功能做空ETH,这有利于WBTC(如sETHWBTCx5)。该攻击者还存入1,300个ETH,发起bZx保证金交易。保证金交易利用KyberSwap将借入的5637.623762个ETH换成51.345576个WBTC。值得注意的是,做空ETH是借入的5倍。该互换实际上将1个WBTC的转化率提高到109.8个WETH,大约是正常转化率的三倍。

具体来说,为了完成这一交易,bZx将订单转发给KyberSwap, KyberSwap随后会查询其外汇储备并找到最佳汇率。这一步骤实质上使Uniswap中的WBTC价格上涨了三倍。

全面揭露 bZx 闪电贷事件


需要注意的是,内置的完整性检查应该阻止该步骤的行为,因为这个检查会验证头寸在交换之后不会变成默认值。然而,当攻击发生时,这个检查并没有生效,稍后我们将在之智能合约 bug部分检查详细信息。

在这一步之后,我们注意到攻击者控制的资产发生了以下变化。然而,这一步之后仍然没有产生收益。

全面揭露 bZx 闪电贷事件


第四步:Dump。随着Uniswap中WBTC的价格飙升,攻击者以Uniswap中的价格将借来的112 WBTC抛售。

全面揭露 bZx 闪电贷事件


该步骤将获得6871.4127388702245个ETH的回报,总的转化率为1WBTC=61.4WETH。

全面揭露 bZx 闪电贷事件


第五步:Flashloan Repay。利用112个WBTC转换的6871.4127388702245个ETH,攻击者将归还10,000个ETH的dYdX闪电贷。

在此步骤后,我们重新计算资产明细。结果表明,攻击者获得了71个ETH的套利,再加上两个头寸,一个是Compound(+5,500WETH/-112WBTC),另一个是bZx(-4,337WETH/+51WBTC)。当bZx头寸处于默认状态时,Compound头寸是非常有利可图的。显然,在攻击之后,攻击者马上开始安排支付Compound债务,归还112WBTC换回5,500weth抵押品。对于bZx头寸,因为它已经处于默认状态,所以攻击者没有表现出进一步的兴趣。

全面揭露 bZx 闪电贷事件


考虑到1WBTC=38.5WETH(或1WETH=0.025BTC)的平均市场价格,攻击者可以用4,300个ETH兑换112个WBTC。其结果是,加上之前的71.4个ETH,攻击者大约获利1,271个ETH,大约获利355,880美元(当时ETH价格约为280美金)。

bZx智能合约中的bug


最神奇的是Uniswap中的WBTC/ETH价格是如何被操纵至高达61.4的利润。如步骤3所述,WBTC/ETH的价格甚至在正常市场价格仅为38左右时,就被推高到了109.8。然而,如此巨大的价格下滑应该会导致bZx头寸未完全抵押。但为什么会允许抵押品不足,这自然会导致在bZx智能合约执行中发现隐藏的错误。

特别是,margin pump从marginTradeFromDeposit()这项功能开始。

全面揭露 bZx 闪电贷事件


如上如所示,marginTradeFromDeposit()利用840行设置为true的第四个参数调用_borrowTokenAndUse()。

全面揭露 bZx 闪电贷事件


在_borrowTokenAndUse()中,当amountIsADeposit为true时,在第1,348行调用_getBorrowAmountAndRate()。返回的borrowAmount将会存入sentAmounts[1]。

全面揭露 bZx 闪电贷事件


同样地,在_borrowTokenAndUse()中,在amountIsADeposit为true时,第1,355行中的sentAmounts[1]的值会填充sentAmounts[6](稍后我们将看到这一点)。随后,在第1,370行中调用_borrowTokenAndUseFinal()。

全面揭露 bZx 闪电贷事件


在第1414行,_borrowTokenAndUseFinal()通过IBZx接口调用takeOrderFromiToken(),以便交易流进入bZxContract。

全面揭露 bZx 闪电贷事件


有趣的部分来了。在第145至153行,由一个require()来检查头寸是否健康。不幸的是,在loadDataBytes.length == 0 && sentAmounts[6] == sentAmounts[1]例子中,完整性检查bZxOracle::shoudLiquidate()将被跳过。这正是漏洞触发避免完整性检查的条件。

全面揭露 bZx 闪电贷事件


如果我们研究一下bZxOracle::shouldLiquidate(,第514行getCurrentMarginAmount() <= loanOrder.maintenanceMarginAmount可以通过捕捉margin pump步骤来完成检查,从而防止这种攻击。


全面揭露 bZx 闪电贷事件

bZx Hack II


2月18日,就在官方公布黑客报告的几小时后,bZx团队再次暂停了该协议。

全面揭露 bZx 闪电贷事件

利用Oracle操作的五个步骤

PeckShield的研究人员使用内部实时异常检测引擎检测到异常的交易模式,并立即对其进行研究。第二次攻击与第一次攻击不同,它实际上是Oracle攻击。在Oracle的操作中,它实际上采取了一种相反的方式,允许假定锁定的bZx资金直接流向攻击者控制的帐户,而不需要进一步将资金归还到Compound头寸中。具体来说,Oracle操纵实质上提高了受影响数字货币(如sUSD)的价格,并使其在bZx贷款系统中极具价值。然后,攻击者可以简单地将之前购买或储存的sUSD作为抵押品存入,而借入WETH以获取利润(而不是出售)。在上图中,我们详细展示了这一行为背后的五个步骤:Flashloan Borrow、Hoard、Pump、Collateralized Borrow、Flashloan payments。注意第四步是抵押借款,而不是抛售。接下来,我们将检查每个特定的步骤。

获利的五个步骤


该漏洞发生于2020年2月18日18:13:58时,以太坊区块高度为9,504,627。罪魁祸首可以在etherscan上找到。如上述所言,这个攻击过程可以分为以下5个步骤:

第一步:Flashloan Borrow。

这一步主要利用了bZx闪电贷的特点借入7,500个ETH。值得注意的是,这个步骤与第一次攻击中的第一步相同。不同之处在于第二次攻击使用bZx而不是dYdX来进行闪电贷。因此,我们不在此处过多讨论该细节。

全面揭露 bZx 闪电贷事件


该步之后,我们注意到攻击者具有以下资产细分。但是还没有产生收益。

全面揭露 bZx 闪电贷事件


第二步:Pump。攻击者通过Kyber分两批将900个ETH交换为sUSD。第一批在KyberSwap中卖出540个ETH,在对储备金进行内部咨询之后,将掉期订单传递至KyberUniswap储备(0x31e085afd48a1d6e51cc193153d625e8f0514c7f)中,并得到92,419个sUSD回报。第二批价格是ETH的18倍,同样在Kyber中,在对储备金进行内部咨询之后,将掉期订单转至Kyber-sUSD储备(0x4cb01bd05e4652cbb9f312ae604f4549d2bf2c99),并得到63,584个sUSD。这两批抛售有效地推动了sUSD的价格上涨到0.00899ETH(或1ETH=111sUSD)。与ETH/sUSD的平均市场价格相比,被操纵的价格大约高出平均市场价格的2.5倍。

通过以上操作后,攻击者获得92,419+63,584=156,003个sUSD。

全面揭露 bZx 闪电贷事件


此步骤后,我们注意到攻击者控制的资产发生以下改变。但是仍没有产生收益。

全面揭露 bZx 闪电贷事件


第三步:Pump。接下来,攻击者转向Synthetic Depot合同以市场价格获得更多的sUSD。值得注意的是Synthetic Depot合同允许以公平价格的sUSD存入ETH。我们的分析显示,攻击者发送了6,000个ETH并回购了943,837个sUSD(由于没有足够的sUSD,有2,482个ETH被退回)。请注意,此步骤通常在Pump步骤之前启动。无论出于何种原因,在这次的黑客攻击中,情况并非如此(订购不会影响最终结果,因为Synthetic Depot价格不受KyberSwap的影响)。

全面揭露 bZx 闪电贷事件


完成此步骤后,攻击者将拥有100万个sUSD,约占sUSD总供应量的20%!我们还注意到攻击者的资产发生了以下变化,但仍未产生任何收益。

全面揭露 bZx 闪电贷事件


第四步:Collateralized Borrow。正如之前所讨论的那样,到目前为止,攻击者已经显著地推高了sUSD/ETH价格,并拥有超过100万个sUSD可供使用。请注意,在第一次攻击中,攻击者采取的方法是在一个有利可图的Compound头寸中利用上涨的价格。考虑到sUSD的流动性可能较低,攻击者这次采取的方法是先将收集到的100万个 sUSD抵押回bZx,然后向bZx借款6796个 ETH。由于bZx依靠Kyber作为价格来源,在sUSD/ETH价格飙升的情况下,拥有的1百万个 sUSD可以借入6,796 个ETH。按照正常的1ETH=111 sUSD的兑换率,同样数量的sUSD代币只能购买4,000 个ETH,这说明这笔贷款已经资不抵债,担保不足。

全面揭露 bZx 闪电贷事件

全面揭露 bZx 闪电贷事件


第五步:Flashloan Repay。借入的6,796个 ETH(加上剩余的7,500—900—6,000—2,482=3,082 ETH),攻击者可以将7,500 个ETH归还给bZx,获利2,378 个ETH的利润。

全面揭露 bZx 闪电贷事件


简单回顾一下,在这次交易中,攻击者获得2378 个ETH的利润,并留下bZx头寸(+1,099,841 sUSD/-6,796 ETH)。显然,攻击者没有进一步的兴趣,并带走了2,378个 ETH,约合

665,840美元(假设ETH的价格为280美元)。


全面揭露 bZx 闪电贷事件

PeckShield 作者

Zoe Zhou 翻译


内容仅供参考 不作为投资建议 风险自担


分享到:


相關文章: