0x.1 前言
2021 年 11 月 15 日,我們的內部監控系統在 BSC 上捕獲到可疑的閃電貸交易。 經調查,我們發現這是一場針對 Nerve Bridge 的攻擊,受影響的包括 fUSDT 和 UST 的 MetaPools。

截至撰寫本文時,攻擊者已耗盡了 Nerve 質押池中 fUSDT 和 UST 的流動性,並獲利 900 BNB。
令人驚訝的是,我們發現該漏洞代碼是從 Saddle.Finance 分叉而來的,而此代碼已於 2021 年 11 月 6 日導致 Synapse Bridge 損失了 8 億美元。 具體而言,該漏洞的根本原因在於不同程式庫在計算代幣兌換量時的實作不一致。
然而,目前並沒有任何公開報告來分析此安全事件。 因此,我們旨在透過本部落格提供全面的分析,包括專案機制、漏洞以及攻擊過程。
0x2. 背景
0x2.1 什麼是 MetaPool?
簡而言之,Curve 提供了兩種穩定幣交換池,即標準穩定幣互換池(Standard StableSwap Pool)和 MetaPool。前者是一個完整的自動造市商 (AMM),用於在不同穩定幣之間建立跨市場交易 [1]。它是最廣泛使用的池類型,例如由 DAI、USDC 和 USDT 組成的 Curve.3pool。然而,這種池無法隔離穩定幣之間的風險,這可能會給流動性提供者 (LP) 帶來巨大損失。
因此,MetaPool 被提出以解決此問題。
正如 Curve 所述 [2],「它允許單一代幣與另一個(基礎)池中的所有代幣進行聚合,而不會稀釋其流動性」。本質上,它是穩定幣與標準穩定幣互換池的 LP 代幣(由其他幾種穩定幣組成)之間的交換池。在本文中,我們將這兩種類型的穩定幣分別稱為 池穩定幣 (pool stablecoin) 和 底層穩定幣 (underlying stablecoin)。
例如,本次事件的受害者之一正是 Nerve 的 fUSDT 與 Nerve.3pool LP 代幣(包括 BUSD、USD 和 USDC)的 MetaPool,該池的結構本質上是 [fUSDT, (BUSD, USD, USDC) 的 LP 代幣]。因此,fUSDT 是池穩定幣,而 BUSD、USD 和 USDC 是底層穩定幣。

0x2.2 漏洞代碼來源
Curve 的 MetaPool 是使用 Vyper 實作的。為了支援 Solidity 開發,Saddle.Finance 的開發團隊使用 Solidity 重寫了程式碼。作為此漏洞的源頭,該程式碼隨後分別被 Synapse 和 Nerve 分叉並採用。11 月 6 日,Synapse 遭到攻擊。

大約 820 萬美元的資金從 MetaPool 中被抽走,但由於攻擊者犯了一個「愚蠢」的錯誤,實際上並沒有造成資金損失 [3]。
此後,Saddle.Finance 採取了緊急措施,透過暫停所有 MetaPool 合約來保障其資金安全。然而,Nerve Bridge 並未採取任何行動,這不可避免地導致了此次安全事件。
相關合約地址如下:
- MetaSwap: 0xd0fBF0A224563D5fFc8A57e4fdA6Ae080EbCf3D3
- SwapUtils: 0x02338Ee742ddCDe44488640F4edf1Aa947E670E7
0x3. 漏洞分析
在 MetaPool 中,有兩個重要函數:swap 和 swapUnderlying。前者用於交換 LP 代幣與池穩定幣,而後者用於交換池穩定幣與底層穩定幣。


然而,這兩個函數的實作並不一致。如上圖所示,紅色矩形中的程式碼片段透過測量 LP 代幣的「虛擬價格」(隨著費用增加,該價格會從 1 的基準值開始上漲)來調整 LP 代幣的價值。但 swap 函數忽略了虛擬價格的影響,這意味著 LP 代幣的價值會被低估。換句話說,可以兌換出更多的 LP 代幣。
因此,攻擊者有可能透過首先使用相應的 LP 代幣取回底層穩定幣的流動性,然後呼叫 swapUnderlying 函數來交換池穩定幣,從而獲取更多的池穩定幣。
0x4. 攻擊分析
我們以 範例交易 為例來說明此次攻擊。

圖 6 顯示攻擊者採取了以下五個步驟來發動攻擊:
- 步驟 1:從 Fortube 閃電貸借入 50,000 BUSD。
- 步驟 2:在 Ellipsis 上將 50,000 BUSD 交換為 50,351 fUSDT。
- 步驟 3:呼叫 MetaSwap 的
swap函數,將 50,351 fUSDT 以較大的滑點交換為 36,959 Nerve 3-LP。 - 步驟 4:使用(上一步收到的)LP 代幣呼叫 Nerve.3pool 的
removeLiquidityOneCoin函數,移除 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



