2024年5月15日,Sonne Finance在樂觀鏈上遭受了2千萬美元的攻擊。攻擊者利用質押鑄造漏洞,透過創造和操作市場的方式,導致匯率異常,最終盜取大量資金。在攻擊後,一名用戶透過轉移VELO代幣到soVELO合約中,改變了totalSupply和totalCash的值,阻止了攻擊繼續進行。攻擊者將資金轉移到多個位址進行洗錢,呼籲Defi專案方加強精確度損失風險管理,並建議特權使用者執行關鍵操作以防止類似攻擊發生。
2024年5月15日,Sonne Finance在樂觀鏈上遭受攻擊,損失高達2千萬美元。
攻擊發生後,X上@tonyke_bot用戶推推表示,其用約100美元保護了Sonne Finance的代幣質押幣(也稱為市場,類似Compound中的cToken)中剩餘的約650萬美元。
(https://twitter.com/tonyke_bot/status/1790547461611860182)
Sonne Finance計畫方發現攻擊後,迅速暫停了所有市場的樂觀情緒,並表示基礎市場是安全的。
(https://twitter.com/SonneFinance/status/1790535383005966554)
攻擊簡述
Sonne Finance是Optimism上的一個分叉了Compound V2的去中心化的網路協議,提供個人、機構和協議訪問金融服務。 Sonne Finance協議將用戶的代幣資產聚合起來,形成了網路流動性礦池,為用戶提供了一個類似銀行的借貸業務。與Compound一樣,協議參與者可以將其持有的代幣質押到Sonne Finance的借貸流動性礦池中,同時獲得憑證soToken(與cToken一樣)。而soToken是一種生息資產憑證,隨著區塊的推進會產生一定的收益,同時也能獲得SONNE 代幣激勵。而參與者持有手中的soToken 可以從Sonne 借貸資產礦池中藉出其他代幣,例如參與者可以一定質押數量USDC 獲得如此USDC 的憑證,並通過網絡出WETH 用於經一步的流通。 Sonne Finance 協議中的質押借貸可以是多對多的資產關係,在質押借貸的過程中,協議會自動計算參與者地址的健康度(Health Factor),當健康度低於1時,該地址的讚助品將受到重慶支持,而重慶人也可以獲得一定的重慶獎勵。
用戶存入的底層代幣與鑄造的soToken的數量關係,主要與一個稱為exchangeRate的變數有關,該變數粗略可以用來表示每個soToken價值多少底層代幣。 exchangeRate的計算公式如下:
公式中,totalCash 是指soToken 持有的底層代幣的數量,totalBorrows 是指市場中被借出的底層代幣的數量,totalReserves 是指某總儲備金數量(其中包含轉帳人支付的利息),totalSupply是指鑄造的soToken的數量。
在回贖時,用戶可以指定想要贖回的標的代幣的數量redeemAmount,來計算需要重置的soToken 的數量redeemTokens,計算方式大概為“redeemTokens = redeemAmount / exchangeRat ”,注意這裡並沒有對準確性損失做處理。
這次攻擊事件的本質是市場(soToken)被創建出來時,攻擊者進行了第一筆質押鑄造的操作,以少量底層代幣鑄造了很少的soToken,導致soToken的“totalSupply”數值太小。者繼而利用了Solidity承諾精度損失這個漏洞,再配置直接往soToken承諾承諾底層代幣(不會鑄造soToken,意味著「totalSupply」不變,「totalCash」變大),而不是質押+鑄造的方式存入底層代幣。這樣的操作使得合約中「totalCash」變數變大,但「totalSupply」保持不變,導致兌換率變大。最終攻擊者在贖回底層代幣時,需要調用的soToken 劫持時鑄造的soToken,攻擊者利用餘額的soToken去其他的soToken(如soWETH、soUSDC)中藉出底層代幣WETH、USDC,最終盈利高達2000萬美元。
攻擊中涉及的關鍵地址
攻擊準備交易:
https://optimistic.etherscan.io/tx/0x45c0ccfd3ca1b4a937feebcb0f5a166c409c9e403070808835d41da40732db96
攻擊盈利交易:
https://optimistic.etherscan.io/tx/0x9312ae377d7ebdf3c7c3a86f80514878deb5df51aad38b6191d55db53e42b7f0
攻擊EOA相關地址:
0x5d0d99e9886581ff8fcb01f35804317f5ed80bbb
0xae4a7cde7c99fb98b0d5fa414aa40f0300531f43
攻擊者(合約)相關地址:
0xa78aefd483ce3919c0ad55c8a2e5c97cbac1caf8
0x02fa2625825917e9b1f8346a465de1bbc150c5b9
底層代幣(VELO代幣V2):
0x9560e827af36c94d2ac33a39bce1fe78631088db
漏洞合約(soVELO,類似Compound的cToken):
0xe3b81318b1b6776f0877c3770afddff97b9f5fe5
X上@tonyke_bot用戶交易:
https://optimistic.etherscan.io/tx/0x816f9e289d8b9dee9a94086c200c0470c6456603c967f82ab559a5931fd181c2
攻擊流程分析
前情提要
Sonne Finance專案方最近通過了一項將VELO 市場添加到Sonne Finance 的提議(https://twitter.com/SonneFinance/status/1786871066075206044),並透過多簽錢包安排了五筆在兩天之後執行的交易( https://optimistic.etherscan.io/tx/0x18ebeb958b50579ce76528ed812025949dfcff8c2673eb0c8bc78b12ba6377b7),這五筆交易是為了創建VEVELO市場(soLO市場創建之後,用戶可以存入VELO代幣,來鑄造soVELO代幣,soVELO代幣又可以用於網路其他soToken。
攻擊準備
攻擊準備階段主要是攻擊者在兩天鎖定時間結束後,根據Sonne Finance項目方提議中的信息,創建VELO市場(soVELO合約),設置關鍵的配置,並通過發起人VELO代幣進soVELO合約來鑄造soVELO代幣,同時也將自己持有的VELO代幣以直接發送給soVELO合約的方式,來增加兌換率,為後續攻擊增益做準備。
具體步驟如下:
攻擊者在兩天鎖定時間結束後,首先將提議中安排的前四筆交易的操作配額到追加交易中(交易0x45c0cc),用於創建VELO市場(所以VELO合約),並設置好關鍵的配置。 VELO 市場初始化時,exchangeRate 被設定為「 200,000,000,000,000,000,000,000,000 」。
攻擊者調用soVELO合約的「mint」函數存入VELO代幣,並鑄造soVELO代幣,攻擊者指定「mintAmount」為「400,000,001」(VELO代幣的數量)。從函數「exchangeRateStoredInternal」可以看出,由於此時soVELO代幣的「_totalSuppl」為0,因此exchangeRate即為第1步驟設定的值。根據公式「mintTokens =actualMintAmount / exchangeRate」,此時計算出的應該鑄造的soVELO代幣的數量為2簡單來說,這一步攻擊者向soVELO合約存入數值為「400,000,001」的VELO代幣,攻擊者獲得數值為2的soVELO代幣。
soVELO.mint:
攻擊者以直接給soVELO 合約發送VELO 代幣的方式,給soVELO 合約發送的數值為「2,552,964,259,704,265,837,526」的VELO 代幣,此時soVELO 合約持有的VELO 代幣增加,但由於沒有新代幣的VELO鑄造,因此totalSupply保持不變,這意味著此時根據exchangeRate計算公式計算出的exchangeRate會變大。
攻擊者將持有的soVELO代幣轉移多次,最終轉移給了另一個攻擊EOA 0xae4a。
攻擊收益
攻擊提升階段是攻擊者執行提議的第五筆交易,並透過閃電貸借出VELO代幣直接發送給soVELO合約,以增加進一步的匯率。然後攻擊者利用自己帳戶的數值為2的soVELO代幣,其他的soToken(例如soWETH,soUSDC等)合約中藉增長了WETH、USDC等去底層代幣,這些部分成為了攻擊者獲利。緊接著攻擊者去soVELO合約中贖回自己的底層代幣,由於匯率變大,以及計算贖回需要模擬的soVELO代幣時的精度損失問題,最終使得攻擊者只需使用數值為1的soVELO代幣就贖回了交易所存入的幾乎所有的VELO代幣,理解為攻擊者利用多得的數值為1的soVELO代幣,透過從其他soToken回購了WETH、USDC等底層代幣。攻擊者利用同樣的手段多次重複攻擊,最終實現巨大利益。
具體步驟如下:
攻擊者執行題案中的第五筆交易,設定提案中規定的抗體多肽。
攻擊者從VolatileV2 AMM – USDC/VELO 礦池子中閃電貸出數值為「 35,469,150,965,253,049,864,450,449 」的VELO 代幣,這會觸發攻擊者的hook 函數。在hook 函數中,攻擊者繼續執行攻擊操作。
攻擊者將自己持有的VELO 代幣發送給soVELO 合約,以進一步增加兌換率。目前soVELO 合約中一共用數值為「35,471,703,929,512,754,530,287,976」的VELO 代幣(攻擊者三次轉入的VELO 代幣和)。
攻擊者創建新的合約0xa16388a6210545b27f669d5189648c1722300b8b,在構造函數中,將持有的2個soVELO代幣轉給出新創建的合約0xa163(以下稱為攻擊者0xa163)。
攻擊者0xa163以持有的soVELO代幣,從soWETH借出數值為「265,842,857,910,985,546,929」的WETH。
攻擊者0xa163 呼叫soVELO 的「redeemUnderlying」函數,指定贖回VELO 代幣的數值為「35,471,603,929,512,754,530,287,976 」(幾乎是所有攻擊者轉入或「VE贖回令牌=贖回金額/交易所率」來計算贖回所需要整理的所以VELO代幣的數量。
從「exchangeRateStoredInternal」函數可以看出,由於此時_totalSupply 是2 不是0,因此需要計算exchangeRate 的值,通過公式「exchangeRate = (totalCash+totalBorrows -totalReserves)/totalSupply」計算出,目前的exchangeRate 為「17,735,851,964,756,377,265,143,988 ,00 0,000,000,000,000,000” ,這個值顯然大於設定的初始兌換率「200,000,000,000,000,000,000,000,00」。
根據新的交易所率計算出的「redeemTokens」的值為「1.99」,由於Solidity 後續最終取整的特性,「redeemTokens」的值為1。也就意味著攻擊者0xa163 數值使用為1 的所以VELO代幣,贖回了阿聯酋存入的幾乎所有的VELO代幣。同時攻擊者0xa163也了解了從soWETH中藉出的數值為「265,842,857,910,985,546,929」的WETH。
所以VELO.redeem底層:
所以VELO.exchangeRateStored內部:
攻擊者0xa163 將藉到的WETH 和贖回的VELO 代幣全部轉給了上層攻擊者,然後自毀。
攻擊者呼叫soWETH 的「liquidateBorrow」函數,用於取消前面新建立的合約0xa163 比特幣的部分資產,目的是拿回鎖定住的數值為1 的soVELO 代幣。目前攻擊者只持有數值1 的soVELO代幣。
攻擊者再次調用soVELO 的「mint」函數,再一次質押鑄造soVELO 代幣,目的是湊夠分數為2 的soVELO 代幣,然後執行上述第3-8 步,賺取其他的uneylying 代幣。
攻擊者執行數次第9步的操作,還掉閃電貸,獲利離場。
$100 如何撬動$650 萬美元
攻擊發生後,X上@tonyke_bot用戶在交易0x0a284cd中,透過託管人將1144個VELO代幣到soVELO合約中,鑄造了0.00000011個soVELO。這樣操作的話能夠阻止攻擊者進一步攻擊,是因為剛才交易改變了所以VELO中totalSupply的大小和持有VELO代幣的數量totalCash,而totalSupply的增長是為了計算匯率產生的影響大totalCash產生的影響,因此匯率變小,從而導致攻擊者進行攻擊時,無法再利用精度損失如此VELO,導致攻擊無法再次進行。
資金追蹤
攻擊者攫取非法收益後不久便將資金進行了轉移,大部分資金轉移到了以下4個地址,有的是為了換個地址繼續攻擊,有的是為了洗錢:
0x4ab93fc50b82d4dc457db85888dfdae28d29b98d
攻擊者將198 WETH 轉入該地址,然後該地址採用了相同的攻擊手法,在以下交易中獲得非法收益:
攻擊結束後,該地址將上述非法所得轉給了0x5d0d99e9886581ff8fcb01f35804317f5ed80bbb。
0x5d0d99e9886581ff8fcb01f35804317f5ed80bbb
攻擊者將724277 USDC、2353 VELO 轉入該地址,將USDC 兌換成以太幣。隨後立即將部分資金轉入Stargate 跨鏈橋,剩餘大部分非法資金滯留在該地址:
0xbd18100a168321701955e348f03d0df4f517c13b
攻擊者將33 WETH 轉入該地址,並採用剝離鏈的方式嘗試洗錢,洗錢倉庫如下:
0xbd18100a168321701955e348f03d0df4f517c13b -> 0x7e97b74252b6df53caf386fb4c54d459cb6928 -b6df53caf386de854d459cb6928 -> 095321b c2b4 -> 0x9f09ec563222fe52712dc413d0b7b66cb5c7c795。
0x4fac0651bcc837bf889f6a7d79c1908419fe1770
攻擊者將563 WETH 轉入了該位址,並轉給了0x1915F77A116dcE7E9b8F4C4E43CDF81e2aCf9C68,目前沒有進一步行為。
攻擊者這次提高反洗錢的手段相對來說比較專業,利用了多元趨勢。因此對於我們Web3參與者來說,在安全方面要持續不斷地我們的反洗錢能力,透過KYT、AML等相關區塊鏈交易安全產品來提升Defi專案的安全性。
安全建議
精度損失需要重視。精度損失導致的安全問題層出不窮,尤其是在Defi專案中,精度損失往往會導致嚴重的資金損失。建議專案方和安全審計人員仔細審查專案中存在精確度損失的程式碼,並做好測試,盡量彌補漏洞。
建議類似於Compound中cToken這種市場的創建和首次質押鑄造操作由特權使用者來執行,避免被攻擊者操作,從而操作匯率。
當合約中存在關鍵變數依賴「 this.balance 」或「 token.balanceOf() 」的值時,需要慎重考慮該關鍵變數改變的條件,例如是否允許直接透過給予合約轉幣或代幣的方式改變該變數的值,還是只能透過呼叫某個特定函數來改變該變數的值。
本文由ZAN Team 的Cara(X 帳號@Cara6289)和XiG(X 帳號@SHXiGi) 共同撰寫。
資訊來源:0x資訊編譯自網際網路。版權歸作者ZAN Team所有,未經許可,不得轉載