通过短期贷款攻击DeFi生态系统以获得乐趣和利润(2020)

2021-01-17 16:49:57

本文将详细研究在两次利用漏洞在bZx中开设欠抵押头寸的交易中发生了什么,导致资产损失总计超过一百万美元。

这是一项技术分析。有关bZx开发人员的信息,请参阅其正式的验尸和Twitter帐户。

这与交易0xb5c8 ...有关,该交易发生在UTC 2020年2月15日(星期六)的雷东。

在那笔交易中,攻击者利用bZx / Fulcrum的漏洞采取了抵押不足的立场,从而为他们带来了大约370k美元的利润,并在bZx贷款池中损失了约62万美元的股本。

让我们看看为什么存在漏洞,以及它不是一个Oracle错误。我们将为每个索赔提供原始交易的链接。

要查看发生了什么,我们可以使用Oko或EthDecoder,两者都可以让我们看到在该事务期间进行的所有调用的树。当心:该交易相当复杂。

攻击者向bZx发送1300 ETH,为WBTC开立5倍空头头寸。

bZx通过路由到Uniswap的Kyber订单在内部将5637 ETH转换为51 WBTC(巨大价差)。

攻击者在Uniswap上将112 WBTC(借入B.)转换为6871 ETH(因为价格在C.处偏斜)。

攻击者最终获得71 ETH,然后进行一些混淆处理(见下文),然后向发起EOA的攻击者发送65 ETH。

不,他们没有获得71 ETH的“纯套利利润”。他们以5500 ETH抵押品和仅借入112 wBTC的复合头寸结束了交易。这大约相当于Compound的35万美元股权。

攻击者控制的地址和合同是新的,并且从未与bZx,Compound或其他任何东西交互。因此,显然他们到处都是零余额。

攻击者部署的所有合同以及用于调用交易的地址均由0x296e提供资金。该地址由攻击前不久的Tornado Cash(以太坊混音器)提供资金。因此我们无法进一步追踪资金(否则我们将需要进行概率/污点分析)。

在交易结束时,攻击者合同创建了另一个合同,向其发送65 ETH只是立即对其进行自我销毁,因此资金最终流向了交易产生的EOA。这是将ETH发送到tx.origin的一种非常人为的方式。我不确定这样做的目的是什么,但我最大的猜测可能是混淆,和/或一种试图避免运行中的机器人阻止运行中的攻击的方法通过使重播变得更加困难。

最重要的是,通过快速查看它,您会发现交易的原始帐户一无所有,然后借入并移动一堆现金,在同一笔交易过程中产生两个巨大的Uniswap订单(双向),然后结束达到65 ETH。那肯定看起来很腥。

Dydx Solo合约的operator()函数由第二个攻击者合约0x0d…调用:在此处调用。

这里发生的是,DyDx仅在您要完成的所有操作完成后才检查您是否有抵押。但是,如果您以原子方式进行所有操作,则不需要抵押!

请注意,整个漏洞利用都将发生在从DyDx启动的Call操作内部。攻击者将提取从DyDx借来的资金,进行利用,然后再将其放回去。最终,他们的帐户没有任何债务,因此没有抵押不足,DyDx也不会恢复通话。

这是获得漏洞利用所需的10k ETH的简单部分。

就像DyDx借用一样,这只是将借用的ETH转换为足够的WBTC的步骤,以便他们能够发起攻击。

该看涨期权打开一个Fulcrum头寸,以x5的杠杆将ETH与WBTC卖空。该头寸为1300 ETH(巨大)。

在内部,bZx使用Kyber来确定该头寸所涉及代币的中间价格(它对两个方向的价格进行平均:调用1,调用2)。它得到的价格都代表正确的市场价格。正如该推文还指出的那样,Uniswap不用作价格订阅源。

但是,当您打开这样的头寸时,它需要将乘以杠杆的1300 ETH转换为WBTC,这将成为您的抵押。

转换通过Kyber发送。 Kyber会查询每个储备金,但是除了Uniswap之外,似乎没有任何储备金有足够的流动性来单独履行该订单。因此,订单被路由到Uniswap。

对于如此巨大的数量,通过Uniswap会大大扭曲价格:bZx发送5637 ETH,接收51 WBTC。那是110 BTC / ETH,正常价格接近36 BTC / ETH!

这通常很好,因为该头寸被超额抵押了至少20%,因此您需要的滑点要比该滑点大,这会引起问题。但是在这种情况下,滑点造成了一笔损失,使贷款池陷入了损失。

但是,这似乎是有意设计的:代码还可以确保在完成所有操作后将主叫方帐户完全抵押,否则请恢复通话。

因此,如果由于滑点而造成巨大损失,则呼叫者将没有足够的抵押品并且呼叫将被还原,这是有道理的,并且其他合约(例如DyDx)也具有类似的设计(请参见上文)。

该代码应该强制该职位还足够抵押:

require((loanDataBytes.length == 0&& //仅Kyber sentAmounts [6] == sentAmounts [1])|| // newLoanAmount!OracleInterface(oracle).shouldLiquidate(loanOrder,loanPosition),"不健康的位置");

但是由于逻辑错误,该条件的第一部分为true,并且从不调用shouldLiquidate()(可以在跟踪中检入)。因此,当电话本应回拨时,却没有。

Lev Livnev对导致该错误的调用堆栈进行了更详细的介绍。

这是外部视图。从bZx的角度来看,攻击者将其1300 ETH转换为51 WBTC的抵押品(漏洞),还留下了360 ETH作为代管权益。您可以参考他们的官方验尸信息,以了解更多信息以及它如何影响将贷款存入池中的人们。

在上一步C.中,攻击者利用了bZx中的错误,导致其以3倍的虚高价格在Uniswap上进行了大量交易。

由于Uniswap的工作方式,这导致WBTC池价格大幅波动。然后,可以将这个扭曲的价格重新定为正常价格,以获取利润。

他们就是这样做的:他们通过在Uniswap上出售从Compound借来的112 WBTC来对Uniswap套利。由于Uniswap的供应全部失真,因此他们能够以6871 ETH的价格出售这112 WBTC。

这是61 BTC / ETH的价格:他们以市场价格的两倍出售其112 WBTC。

现在,他们有足够的ETH来偿还借来的DyDx醚,还有65 ETH的剩余余额,它们将被发送回发送交易的帐户。

同样,请注意,他们没有赚到65 ETH的利润。这只是面包屑,因为利润的最大部分在他们开立的复合头寸中。

在该交易之后,第一个攻击者智能合约最终以一个拥有超过30万美元净值的复合账户结束,该账户由以下组成:

但是,他们不能直接提取其ETH,否则他们的账户最终会抵押不足,因此他们需要在市场上购买WBTC,还清债务并提取ETH。

你猜怎么了?那正是他们一直在做的。在他们进行漏洞利用交易后大约两个小时,他们开始购买WBTC并偿还贷款。

这是他们偿还贷款的示例交易。此功能是其智能合约的一部分。

他们花了不到两天的时间才能完全偿还他们的职位。他们将所有资金发送到了现在拥有1193 ETH的EOA。

您可能已经注意到,bZx的资产损失和攻击者的收益并没有加在一起。

攻击者很可能没有充分利用自己的利益,因此攻击后他们完全失去了Uniswap的平衡。然后,许多机器人争先恐后地从中牟利。

该套利交易获利315 ETH(请参阅合同余额更改0xb958…)。

提款时,他们总是让他们的合同创建一个临时合同,该合同会立即自毁(参见上文)。他们指定了要提取的以太币数量。

首先他们失败了,因为他们没有通过10.1×10 18 Wei,而是通过了10.1×10 18×10 18(它们将Wei中的值乘以10 18的两倍),因此显然这是一个荒谬的高金额,并且没有用。

现在我们来看第二笔交易0x7628…,该笔交易发生在UTC 2020年2月18日(星期二)。 它产生了与第一个相同的效果,即在bZx上建立了不足抵押的头寸,但是它使用了完全不同的方法,并且更容易理解。 攻击者反复致电Kyber将900 ETH转换为155,994 sUSD(扭曲了Kyber sUSD的价格) 最终,攻击者在其攻击合同中获得2378 ETH。 他们不久后将其转移到其EOA。 在开始之前,建议您查看反编译的合同代码,您可以在此处看到要进行硬编码的调用顺序。 此步骤与第一个漏洞利用程序的第一步相当。 只有它使用bZx而不是DyDx。 同样,目标是借用足够的钱以能够利用该漏洞,并且,其余的攻击者活动再次发生在由bZx发起的攻击者合同的回调中。

Kyber使用“储备”来提供流动性。 对于ETH-sUSD对,有两个储备: 一种Synthetix,它实现了LiquidityConversionRates,可自动调整价格(概念上类似于Uniswap)。 一项交易必然会击中其中之一(取决于哪一个给出最佳价格)。 攻击者合约购买了两个储备中可用的大部分sUSD流动性,为此,他们连续进行了19次购买: 您会看到每笔交易的价格都变差了。 这就是攻击者通过吞噬所有可用的流动性来扭曲价格。 我们在Kyber中将价格从270 ETH / sUSD(正常汇率)提高到111 ETH / sUSD。 这是使用Synthetix Depot合同完成的,该合同具有大量可访问的流动性。

速率为157 ETH / sUSD。这是一个不好的汇率,但比Kyber现在返回的失真汇率要好得多(请参见上文)。

我强烈建议您阅读Sam Sun的有关oracle攻击的文章,该文章解释了它是如何工作的。

这个想法是bZx向Kyber查询当前的ETH / sUSD汇率,但是现在,攻击者扭曲了市场,它将获得错误的汇率!这使得攻击者可以借用比其通常多得多的sUSD借入更多的ETH,因为bZx被提供了错误的价格预言。

为此,他们只需调用iETH合约的roweTokenFromDeposit()函数即可发送1,099,841 sUSD(他们从Synthetix Depot购买了这些股票,并且在扭曲Kyber价格的同时还买了更多),并能够借入6796 ETH。

我们可以计算出bZx向他们发送了1.7mm $的货币,而仅收到了1.1mm $的sUSD。这是大约60万美元的股本损失。

如果您阅读有关oracle攻击的文章,您可能想知道该漏洞利用是如何进行的,该文章描述了一种非常类似的攻击,并且已修复。

bZx团队针对上一次攻击恢复了其更改,而是实施了价差检查,这样,如果价差超过某个特定阈值,则贷款将被拒绝。只要要查询的两个令牌在Kyber上具有至少一个不可操纵的储备,该解决方案就可以处理通用情况,当前所有白名单令牌都属于这种情况。

这意味着bZx将在两个方向上检查价格并查看价格差异,但是,这两个储备都是类似Uniswap的储备,并且都对其价格进行了操纵。

因此,是的,bZx应该检查是否有足够小的价差。但是,这两种储备的确有一个恒定的,很小的价差(取决于它们的费用)。因此检查通过了。

既然攻击者已经实现了以太坊的利润,他们可以偿还7500 ETH,因此交易可以正确终止,因为已经偿还了快速贷款。

还要注意,如果他们愿意的话,攻击者可能会将他们首先歪斜的价格拉回以获取更多利润,但是如果您查看这些数字,就会发现他们花费了900 ETH来歪斜价格,而相比之下,价值3518 ETH的sUSD他们从Synthetix仓库购买并利用bZx。因为他们花在扭曲价格上的钱只是他们神奇地乘以倍数后的金额的10%,所以他们不需要理会。

第二次攻击比第一次攻击简单得多,这的确是一个先知攻击。

两种攻击都利用了这样一个事实,即在单笔交易期间就有可能借入大量的流动性(“快速贷款”)。部分流动性最终导致在链上交易所进行大量的买卖(例如Kyber) ):此订单的价差很大,并且大大改变了市场价格。

在第一次攻击中,攻击者通过开立杠杆头寸使bZx做不好的交易,然后从套利中获利。

在第二个攻击中,攻击者首先扭曲价格,然后借用ETH触发对bZx的预言攻击(不进行内部Kyber交易)。 第二个攻击者的EOA于2年前由ShapeShift资助(而第一个攻击者不久前由Tornado Cash资助)。 就像第一次攻击一样,第二个攻击者很可能已经使用DyDx来获得所需的流动性。 相反,他们使用了bZx的类似功能。 两种事务中使用的攻击合同都非常不同:第一个具有许多带有参数的功能,有一条错误消息……第二个具有两个简单的功能。 因此,这将表明攻击者是不同的人。 但是我们永远不能确定。