神経ブリッジセキュリティインシデントの分析

Nerve Bridge事件とSynapse事件の類似点を掘り下げ、攻撃者の手口に光を当てる

神経ブリッジセキュリティインシデントの分析

0x.1 前言

2021年11月15日,我们的内部监控系统在BSC上捕获了可疑的闪电贷交易。 经过调查,我们发现这是针对Nerve Bridge的攻击,包括fUSDT和UST的MetaPool。

Figure 1: an example of attack transaction

截至发稿时,攻击者已耗尽Nerve质押池中fUSDT和UST的流动性,并获得了900 BNB的利润。

令人惊讶的是,我们发现漏洞代码是fork自Saddle.Finance,而Saddle.Finance已于2021年11月6日导致Synapse Bridge损失了8亿美元。 具体来说,漏洞的根本原因在于不同库中计算代币兑换金额的实现不一致。

然而,并没有公开可用的报告来分析此次安全事件。 因此,在本博客中,我们的目标是提供全面的分析,包括项目机制、漏洞和攻击。

0x2. 背景

0x2.1 什么是MetaPool?

基本上,Curve提供两种类型的稳定币兑换池,即标准稳定币兑换池(Standard StableSwap Pool)和MetaPool。前者是一个完全的AMM,用于在不同的稳定币之间创建跨市场交易 [1]。它是使用最广泛的池类型,例如Curve.3pool,它由DAI、USDC和USDT组成。然而,这种池无法隔离稳定币之间的风险,这可能导致LP提供者遭受巨大损失。

因此,提出了MetaPool来解决这个问题。 正如Curve所说 [2],“它允许一个单一代币与另一个(基础)池中的所有代币进行池化,而不会稀释其流动性”。它本质上是一个稳定币和标准稳定币池的LP代币之间的兑换池(后者由其他几个稳定币组成)。在我们的上下文中,我们将这两种类型的稳定币称为“池稳定币”和“底层稳定币”。

例如,此次事件的一个受害者就是fUSDT和Nerve.3pool(包括BUSD、USD和USDC)的LP代币的MetaPool,该池的结构本质上是[fUSDT, LP代币(BUSD, USD, USDC)]。因此,fUSDT是池稳定币,而BUSD、USD和USDC是底层稳定币。

Figure 2: Nerve.3pool

0x2.2 漏洞代码来源

Curve的MetaPool是用Vyper实现的。为了支持Solidity的开发,Saddle.Finance的开发团队用Solidity重写了代码。作为这次漏洞的开端,它已被Synapse和Nerve分别fork并采纳。11月6日,Synapse遭到攻击。

Figure 3: attack transactions targeting Synapse

MetaPool中约有820万美元的资金被耗尽,尽管由于攻击者“愚蠢”的失误,并没有实际损失资金 [3]

之后,Saddle.Finance采取了紧急措施,通过暂停所有MetaPool合约来确保其资金安全。然而,Nerve Bridge并未采取任何行动,这不可避免地导致了此次安全事件。

相关合约地址如下:

  • MetaSwap: 0xd0fBF0A224563D5fFc8A57e4fdA6Ae080EbCf3D3
  • SwapUtils: 0x02338Ee742ddCDe44488640F4edf1Aa947E670E7

0x3. 漏洞分析

在MetaPool中,有两个重要的函数,即swapswapUnderlying。具体来说,前者用于交换LP代币和池稳定币,而后者用于交换池稳定币和底层稳定币。

swap: _calculateSwap function
swapUnderlying: _calculateSwapUnderlying function

然而,这两个函数实现不一致。如图所示。红色矩形中的代码片段用于通过衡量LP代币的“虚拟价格”(随着更多费用收入而从基线值1增加)来调整LP代币的价值。而swap函数则忽略了虚拟价格的影响,这意味着LP代币的价值将被低估。换句话说,可以兑换出更多的LP代币。

因此,通过首先使用相应的LP代币收回底层稳定币的流动性,然后调用swapUnderlying函数来兑换池稳定币,就有可能获得更多的池稳定币。

0x4. 攻击分析

我们将以示例交易为例来说明攻击。

Figure 6: the five attack steps

图6显示攻击者采取了以下五个步骤发起攻击:

  • 步骤1:从Fortube通过闪电贷借入50,000 BUSD。
  • 步骤2:用50,000 BUSD从Ellipsis兑换50,351 fUSDT。
  • 步骤3:调用MetaSwap的swap函数,用50,351 fUSDT以较大的滑点兑换36,959 Nerve 3-LP。
  • 步骤4:调用Nerve.3pool的removeLiquidityOneCoin函数,使用LP代币(在上一步中收到)移除BUSD的流动性,即37,071 BUSD。
  • 步骤5:调用MetaSwap的swapUnderlying函数,用BUSD兑换fUSDT,收到51,494 fUSDT。

攻击者重复执行了以上五个步骤(约200多次交易),耗尽了MetaPool的流动性,最终获得了900 BNB。

有趣的是,攻击者采用了与Synapse事件中相同的手段,而这并不是一种最优化的实现目标的方式。另一种更有效率的攻击方式是,例如,应用优化的参数在一笔交易中耗尽流动性。 结果表明,攻击者可能并未完全理解此漏洞的根本原因。

参考

[1] https://curve.fi/files/stableswap-paper.pdf

[2] https://resources.curve.fi/lp/depositing/depositing-into-a-metapool/

[3] https://synapseprotocol.medium.com/11-06-2021-post-mortem-of-synapse-metapool-exploit-3003b4df4ef4

致谢:Hailin Wang, Lei Wu, Yajin Zhou @BlockSec

Twitter: https://twitter.com/BlockSecTeam

Sign up for the latest updates