Web3安全預警丨LST大熱風險:Prisma Finance攻擊事件分析


2024年3月28日,Prisma Finance遭受攻擊,約損失1,100萬美元。攻擊發生後,項目緊急暫停並要求用戶立即取消授權。 Prisma Finance是一個非託管、去中心化的項目,用戶可以透過託管wstETH鑄造mkUSD穩定幣。攻擊涉及合約MigrateTroveZap,使攻擊者能夠操作其他借款人的質押物。攻擊透過閃電貸服務實施,調用漏洞函數,致使借款人的質押物數量減少,債務基本不變,並盜取了質押物。攻擊涉及多個地址,主要受影響的是藉款人A。整個攻擊過程透過閃電貸服務實現。

2024年3月28日,Prisma Finance遭遇攻擊,目前累計虧損約1,100萬美元。攻擊發生後,Prisma Finance緊急暫停了項目,並通知用戶立即取消委託授權(https://twitter.com/PrismaFi/status)/1773371030129524957)。

攻擊簡述

Prisma Finance 是一個非託管、去中心化的,以託管以太坊LST(流動性質押代幣)鑄造穩定幣的專案。例如用戶可以透過託管wstETH 來鑄造mkUSD,這個過程可以創造一個寶藏,寶藏可以理解為是一個記錄指定借款人的借貸人的借貸情況的寶庫,這個寶庫有一個寶藏管理員,用於管理寶庫的借貸物以及借貸幣(鑄造的穩定幣)。

本次的漏洞合約是MigrateTroveZap 合約,該合約的主要功能是使用者的質押物從一個trove 管理者遷移到另一個trove 管理者。因此創建trove 的借款人可能會授權MigrateTroveZap 合約對其trove 進行操作。然而MigrateTroveZap契約中的onFlashloan函數缺乏輸入驗證,從而允許攻擊者透過MigrateTroveZap契約操作其他借款人的質押品,將其他借款人的質押品數量變少,但債務基本不變,從而盜取其他借款人的質押品。

攻擊中涉及的關鍵地址

本次攻擊涉及多筆交易,我們僅以下面這比交易為例來對攻擊進行分析。

攻擊交易:https://etherscan.io/tx/0xe15fa959627871845f2f5bbfbd7529e6d2aff20ab14ece743f11641700bd7188

攻擊EOA:

0x7e39e3b3ff7adef2613d5cc49558eab74b9a4202

攻擊者(契約):

0xd996073019c74b2fb94ead236e32032405bc027c

受害者(Prisma Finance TroveManager):

0x1cc79f3f47bfc060b6f761fcd1afc6d399a968b6

漏洞合約(Prisma Finance MigrateTroveZap):

0xcc7218100da61441905e0c327749972e3cbee9ee

一個被利用的借款人,簡稱借款人A:

0xcbfdffd7a2819a47fcd07dfa8bcb8a5deacc9ea8

穩定幣mkUSD:

0x4591dbff62656e7859afe5e45f6f47d3669fbb28

質押物wstETH:

0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0

借款人營運合約:

0x72c590349535ad52e6953744cb2a36b409542719

攻擊流程分析

01、攻擊準備

攻擊者安置借款人情況,尋找借款人率最高的借款人,在這比攻擊交易中,找到的借款人為BorrowerA。

攻擊者在TroveManager 中取得BorrowerA 的collionToken(wstETH)和DebtToken(mkUSD)的數量,分別為824,599,953,913,164,625,273、598,174,188,9064,625,273、598,174,188,9064,625,273,598,174,188,9064,6274273,598,174,188,9064,188,0649,096 155,大概機率大約是5 70% ,遠超過了最小惡魔率MCR 110%。這給後續攻擊創造了條件。

02、攻擊實施

實施攻擊階段主要是攻擊者透過mkUSD 的閃電貸服務,呼叫到漏洞標誌MigrateTroveZap 的onFlashLoan 函數,onFlashLoan 函數對攻擊者指定的借款人的trove 進行置換,所謂置換即是先關閉這個trove,再給借款人開啟一個新的寶庫。這個功能原本是用來將質押物遷移到不同的寶庫管理器。然而這個功能存在漏洞,一是它沒有驗證新開的寶庫是否和之前的寶藏同樣具有數量的質押物,二是透過mkUSD的閃電貸服務,攻擊者可以操控別人的寶庫。具體的攻擊步驟如下:

1、 利用mkUSD 的閃電貸服務調用到漏洞質押MigrateTroveZap 的onFlashLoan 函數,並在此基礎上建立好的受害者BorrowerA 的地址以及準備新開的trove 的質押品數量。攻擊者呼叫mkUSD 的flashLoan 函數進行閃電貸,借出mkUSD 給MigrateTroveZap(MigrateTroveZap 同步用於自動將同樣的質押物遷移到不同的trove manager),並在資料參數中指定了後續操作所需佔用的BorrowerA 的位址、TroveManager 的位址、建立trove 時主機的wstETH 的數量,具體確定的參數如下:

receive: MigrateTroveZaptoken: mkUSDamount: 598,174,188,906,400,741,697,930 (該數量匹配是BorrowerA在TroveManager中藉的mkUSD的數量)data(為了查看,以32字節劃分為博日32位): 000000006d 9a47fcd07dfa8bcb8a5deacc9ea8000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b60000000007060000fcc 61fcd1afc6d399a968b6000000000000000000000000000000000000000000000000011c3794b4c52ff0000000000000000000000000000000000000000000000000 00 000a6a488d5ae4bc84b6 //192125967324963177654000000000000000000000000e87c6f39881d5551cf463000000e87c6f39881d5551cf463007020010020202020002020202020202020 000 089ee26fcdff6b109f81abc6876600ec427f7907f

mkUSD.flashLoan:

function flashLoan( IERC3156FlashBorrower 接收者,地址令牌,uint256 金額,位元組calldata 資料) external returns (bool) { require(token == address(this), “ERC20FlashMint: 錯誤的令牌”); require(amount);

在flashLoan函數中,首先會產生MigrateTroveZap鑄造598,174,188,906,400,741,697,930這麼多mkUSD,然後調用MigrateTroveZap的回呼函數onFlashLoan,問題就出在了這個回調函數中。

2.在MigrateTroveZap 的onFlashLoan 函數中對BorrowerA 的trove 進行更換,及時MigrateTroveZap 能操作BorrowerA 的trove,是因為BorrowerA 對MigrateTroveZap 進行了委託授權。到底BorrowerA 的trove 質押率比較高,但是質押率比較高的trove被關閉掉,取而代之的是一個債務一樣,但是質押品的餘額的寶庫,這個操作能夠成功,因為更換寶庫時並沒有校驗新舊寶庫的質押品數量是否一致,最終留下的質押品數量會被在MigrateTroveZap 中。

詳細過程如下:

在MigrateTroveZap 的onFlashLoan 函數中,指定帳戶的質押物從troveManagerFrom 遷移到troveManagerTo,在本次呼叫中,從攻擊者確定的資料參數中解析出來,這兩個參數都指定給TroveManager 的位址。

遷移TroveZap.onFlashLoan:

function onFlashLoan( 地址, 地址, uint256 金額, uint256 費用, bytes calldata data) external returns (bytes32) { require(msg.sender == address(debtToken), “tro!DebtTokenken”); , uint256 maxFeePercentage, uint256 coll, 地址upperHint, 地址lowerHint ) = abi.decode(data, (地址, 地址, 地址, uint256, uint256, 地址, 地址)); uint256 toMint = 金額+ 費用;帳戶); borrowerOps.openTrove(troveManagerTo, account, maxFeePercentage, coll, toMint, upperHint, lowerHint); 回傳_RETURN_VALUE;}

攻擊者指定的帳戶是BorrowerA,因此首先會調用BorrowerOperations 的closeTrove 函數將BorrowerA 的trove 關閉掉,關閉之前會通過修飾符callerOrDeleated 檢查BorrowerA 是否授權調用發起者(這裡是MigrateTroveZap)代理它進行關閉它進行關閉trove 的操作。由於BorrowerA 確實授權了MigrateTroveZap,因此判斷通過,並且此時並不是恢復模式,因此BorrowerOperations 最終調用TroveManager 的closeTrove 函數關閉trove,在此過程中,將BorrowerA 的質押品wstETH(數量為824,599,953,913,1647599,953,913,1647599,953,913,16476399,953,913,16476399,953,913,160003)。銷毀MigrateTroveZap 的mkUSD,數量為597,974,188,906,400,741,697,930。

BorrowerOperations.closeTrove:

function closeTrove(ITroveManager troveManager, 地址帳戶) external callerOrDeleerated(account) { IERC20 collat​​​​alToken;

uint256 價格; bool isRecoveryMode; uint256 總定價質押品; uint256 總債務; (collat​​​​eralToken,價格,totalPricedCollat​​​​eral,totalDebt,isRecoveryMode)= _getgetlatlateral,Rplatveal(Sicp. BorrowerOps: 復原模式期間不允許進行操作」);

(uint256 coll,uint256 債務)= troveManager.applyPendingRewards(帳戶);

uint256 newTCR = _getNewTCRFromTroveChange(totalPricedCollat​​​​eral,totalDebt,coll *價格,假,債務,假); _requireNewTCRisAboveCCR(newTCR);

troveManager.closeTrove(帳戶,msg.sender,coll,債務);

發出TroveUpdated(account, 0, 0, 0, BorrowerOperation.closeTrove);

// 銷毀使用者餘額中已償還的債務和Gas Pool 中的Gas 補償DebtToken.burnWithGasCompensation(msg.sender, Debt – DEBT_GAS_COMPENSATION);}

緊接著BorrowerOperations的openTrove函數被調用,此時確定的參數_collat​​​​eralAmount為攻擊者在數據參數中指定的192,125,967,324,963,177,654,而_debtAmount為攻擊者向mkUSDD簽名閃電的數量加貸閃電的數量。 ,數值為598,712,545,676,416,502,365,458。經過計算,現在還不是一種回收模式,因此最終用來計算質押率的債務的數量compositeDebt為_debtAmount+債務借貸費+債務gas補償,具體數值為598,912,545,682,977,901,520,496,根據_collat​​deal計算出來的ICR (個人質押率)約133%,大於MCR (最小質押率) 110%,符合創造trove的條件。附帶MigrateTroveZap給TroveManager轉移_collat​​​​eralAmount數量的wstETH,MigrateTroveZap取得給其鑄造的_debtAmount數量mkUSD。這番操作其實攻擊者利用MigrateTroveZap 契約,將借款人本來有很多質押品的寶庫,換成質押品的寶庫少了,而攻擊者想要盜取的,就是這一部分質押品的差額。

BorrowerOperations.openTrove:

function openTrove( ITroveManager troveManager, address account, uint256 _maxFeePercentage, uint256 _collat​​​​eralAmount, uint256 _debtAmount, address _upperHint, address _lowerHint) 0ternalconscaled (Ead​​帶”); IERC20質押代幣; LocalVariables_openTrove 內存變數; bool isRecoveryMode; ( collat​​​​alToken, vars.price, vars.totalPricedCollat​​​​eral, vars.ColtalDebt, isRecoveryMode ) = _getlatveal, vars.Collat​​​​eralt,isRecoveryMode ) = _getlatveal,

_requireValidMaxFeePercentage(_maxFeePercentage);

vars.netDebt = _debtAmount;

if (!isRecoveryMode) { vars.netDebt = vars.netDebt + _triggerBorrowingFee(troveManager, account, _maxFeePercentage, _debtAmount); _requireAtLeastMinNetDebt(vars.netDebt);

// ICR是基於Compound債務,即請求的債務金額+債務借貸費用+債務gas comp。 vars.compositeDebt = _getCompositeDebt(vars.netDebt); vars.ICR = PrismaMath._computeCR(_collat​​eralAmount, vars.compositeDebt, vars.price);DevareralAmount, vars.compositeDebt, vars.price);DevareralAmount, vars.compositeDebt, vars.price);Devars.R = PvarrismM._R = Pvaribute.L = Pvarrist.R = PvarrismM._R = Pvaribute.L = PvarrismM.R = PvarrismM._R = PvarrismA.NICR = P ;

if (isRecoveryMode) { _requireICRisAboveCCR(vars.ICR); } else { _requireICRisAboveMCR(vars.ICR, troveManager.MCR()); uint256 newTCR = _getNewTCRFromerChanlatge( s.altoaltoalal,altoal,alerpooler​​r. vars .price, true, vars.compositeDebt, true ); // bools: coll 增加,債務增加_requireNewTCRisAboveCCR(newTCR); }

// 建立trove (vars.stake, vars.arrayIndex) = troveManager.openTrove( account, _collat​​​​eralAmount, vars.compositeDebt, vars.NICR, _upperHint, _lowerHint, isRecoveryModere );

// 將質押品移至Trove Manager collat​​​​alToken.safeTransferFrom(msg.sender, address(troveManager), _collat​​halAmount);

// 向呼叫者鑄造DebtAmount 並為Gas Pool 提供Gas 補償DebtToken.mintWithGasCompensation(msg.sender, _debtAmount);

發出TroveUpdated(account, vars.compositeDebt, _collat​​halAmount, vars.stake, BorrowerOperation.openTrove);}

3.回呼結束稍後,回到mkUSD 的flashLoan 函數。此時MigrateTroveZap 持有的mkUSD 的數量恰好比需要的mkUSD 的數量多一些,透過銷毀MigrateTroveZap 的mkUSD 成功初始化了這個閃電貸的債務。至此,MigrateTroveZap實際上額外獲得了約632 個wstETH。接下來攻擊者就開始想辦法把這632 個wstETH 給套到自己手上。

03、收割贓物款

收割盜款的步驟主要是攻擊者將#攻擊實施#步驟中盜取的留在MigrateTroveZap合約中的wstETH提取出來。攻擊者依然是利用MigrateTroveZap合約的漏洞。這次攻擊者先自己開了一個質押率較低的trove,然後透過mkUSD 的閃電貸服務進入到漏洞標誌MigrateTroveZap 的onFlashLoan 函數,對自己的trove 進行更換。

近期於#攻擊實施#階段對借款人A的寶庫的交易所,不同地點任職,攻擊者對自己寶庫的交易所,是要從低質押率的寶庫更換為高質押率的寶庫,而佔用的質押物正是MigrateTroveZap 中多出來的wstETH。之所以能使用MigrateTroveZap 的wstETH,是因為攻擊者也授權了MigrateTroveZap 本身的trove 來管理,那麼在更換trove 時,質押物都在TroveManager 和MigrateTroveZap 之間流轉。在開出新的寶藏時,如果使用的惡魔物品數量增加,就會直接遷移MigrateTroveZap中的惡魔物品數量。

最終攻擊者的寶藏在交易所之後擁有了更多的質押物,攻擊者就可以自己發起關閉寶藏的調用,將質押物解救出來,從而將被盜的質押物收入囊中。

詳細過程如下:

攻擊者從Balancer中閃電貸出1個wstETH,從而調用到攻擊者的receiveFlashLoan函數。在receiveFlashLoan函數中,攻擊者首先授權BorrowerOperations使用其wstETH,然後再取得MigrateTroveZap代理權。接著攻擊者呼叫BorrowerOperations的openTrove函數為自己建立一個寶庫,使用閃電貸出的1個wstETH進行質押,借2000個mkUSD,此時的質押率大約是188%。這是攻擊者為了後續操作做的一個準備好了。確定的參數詳細資訊如下:

troveManager:0x1cc79f3f47bfc060b6f761fcd1afc6d399a968b6帳號:0xd996073019c74b2fb94ead236e32032405bc027c74b2fb94ead236e32032405bc027c74b2fb94ead236e32032405bc027c_maxF1374ead236e32032405bc027c_maxF1375,03,000 1,000,000,000,000,000,000_debt金額: 2,000,000,000,000,000,000,000 _upperHint: 0xe87c6f39881d5514667 dff6b109f81abc6876600ec427f7907f

攻擊者再次呼叫mkUSD 合約的flashLoan 函數進行閃電貸,這次是藉2000 個mkUSD 給MigrateTroveZap,跟#攻擊實施#階段類似,給MigrateTroveZap 鑄造了2000 個mkUSD,並觸發了MiglashTroveZap 的回調函數。這次攻擊者確定的資料參數如下,攻擊者指定的想要遷移的函式庫是攻擊者上一個步驟所建立的函式庫。

為了方便查看,以32位元組劃分:000000000000000000000000d996073019c74b2fb94ead236e32032405bc027c00000000000000000008 399a968b60000000000000000000000001cc79f3f47bfc060b6f761fcd1afc6d399a968b600000000000005 7 94b4c52ff00000000000000000000000000000000000000000000225737b283504ff0c3 //633473986588201448000060 39 881d5bf51cf46d3dc7e1c1731c2f790a00000000000000000000000089ee26fcdff6b109f81abc6876600ec427f7907f

inflashloan裡,同樣同樣先先先,trovemanager將1個wsteth轉給遷移轉給轉給,447,619,幾乎幾乎是所有的wstETH,當然其中包括在#攻擊實施#階段套取的約632個wstETH。此時_debtAmount具體值為2,001,800,000,000,000,000,000。質押率大概1195%。 trove創建成功,MigrateTroveZap將約633個wstETH轉給TroveManager,並為MigrateTroveZap鑄造了約2001個mkUSD。回調稍後,回到mkUSD 的flashLoan 函數。 MigrateTroveZap 初始化閃電結束負債,持有的mkUSD 被燒掉。到了這一步,攻擊者自己創造的寶藏依然存在,不同的是,經過前面在mkUSD 中的閃電貸,攻擊者將自己創建的寶藏中質押品的數量提升到了約633 個wstETH。攻擊者呼叫BorrowerOperations 的closeTrove 函數,關閉trove,從TroveManager 處取得約633 個wstETH,並燒掉對應的mkUSD 債務。攻擊者在Balancer 中藉出的1 個wstETH。

至此,收割贓物結束,攻擊者收益約632個wstETH。

資金流向追蹤

本次攻擊共涉及三個EOA,如下:

利用者1:0x7E39E3B3ff7Adef2613d5Cc49558EAB74B9a4202 利用者2:0x7Fe83f45e0f53651b3ED9650d2a2C67D8855e385 利用者使用者315Efd5cf8

Exploiter 2 和Exploiter 3 共收益約200 個ETH,另外的約3200 個ETH 被稱為白帽的Exploiter 1 獲取(https://etherscan.io/tx/0xc2825fd6dd05e8ec9f271d63efdebd06e78296afc0813c65788790567916d2 09)。目前Prisma Finance專案方現在聲稱是白帽的剝削者1 溝通資金退還詐騙。

開發者1向專案方生長以下條件:

Prisma 團隊需要進行一次線上新聞發布會; 團隊所有成員都必須現場露面並出示身份證明; 向Prisma的所有用戶、投資者和利用者1表示歉意和感謝; 具體介紹本次事故裡面的問題所在:智能合約審計方是誰,以及Prisma 未來提高安全性的計劃;Prisma 需要承認Exploiter 1 在本次事件中沒有任何責任,Exploiter 1 純粹是在幫助Prisma 修復問題; Prisma 需要在12 小時內修改事後總結所有具有指控性的措詞。

透過ZAN 的KYT 服務,我們可以看到,利用者1 將資金轉到了三個不同的地址上,最終這些資金一部分流入了Tornado Cash 中。詳細資金流轉請查看連結:https://zan.top/ kyt/控制器/交易?實體=0x7e39e3b3ff7adef2613d5cc49558eab74b9a4202&ecosystem=以太坊

Web3安全預警丨LST大熱背後的風險:Prisma Finance攻擊事件分析

轉入Tornado Cash資金流動狀況

利用者2的資金流動:https://zan.top/kyt/controller/transaction?entity=0x7fe83f45e0f53651b3ed9650d2a2c67d8855e385&ecosystem=ethereum

Exploiter 3 的資金流動:https://zan.top/kyt/controller/transaction/?entity=0x7C9FC6E2B908e858F30c5c71a20273315Efd5cf8&ecosystem=ethereum

安全建議

透過分析本次攻擊事件,我們有以下建議:

一是專案方需要謹慎行事,專案中是否需要委託授權的邏輯,如果需要,那麼一定要對相應操作進行嚴格的權限校驗以及校驗輸入,防止攻擊者利用該授權,形成不合法的參數,篡改項目中的關鍵參數。二是委託授權應該有時間限制,長期的授權,容易被使用者忽視,但卻被攻擊者利用。項目設定暫停機制。建議專案方在設計專案邏輯時,建立完善的暫停機制,以發現突發的意外事件,及時停損。

資訊來源:0x資訊編譯自網際網路。版權歸作者ZAN Team所有,未經許可,不得轉載


Total
0
Shares
Related Posts