注意安全風險

作者: Beosin

封面: Ronin

8 月 6 日,據區塊鏈安全審計公司 Beosin Alert 監測顯示,Ronin Bridge 專案出現異常提取跨鏈資產的行為。根據 Beosin 安全團隊分析,此異常行為的根本原因在於當專案方升級合約時,未正常初始化配置跨鏈交易確認所需的 operator 權重,導致合約中的 minimumVoteWeight 參數為零,從而使得任何人的簽名都能通過跨鏈驗證。

攻擊交易連結:

https://etherscan.io/tx/0x2619570088683e6cc3a38d93c3d98899e5783864e15525d5f5810c11189ba6cb

圖片

Beosin 目前正與專案方合作處理此事件,本篇長文,我們也將與大家分析本筆交易存在異常的點在於兩點:

首先,提取數量過高。在 Ronin Bridge 中,有跨鏈提取額度的限制,如果跨鏈提取的額度太大,則需要轉至人工確認,而本次交易的跨鏈資產 WETH 的限制額度為 4000。此筆交易提取了 3996 個。當然這不是漏洞,但足以讓人引起了注意。

其次,查看這筆提取交易發現,其跨鏈驗證者 Operator 只有一個,且對應的操作權重為 0,那就可以確認這筆交易是存在問題的,因為在 Ronin Bridge 項目中,用戶提取跨鏈資產需要得到多個 Operator 的簽名,且累計的權重必須達到指定閾值。

事件分析:

分析鏈上合約和資料發現,合約 Operator 是對應的 Manager 合約獨立管理,並且它是一個治理合約,專門用於管理 ronin bridge,查看其交易記錄,發現最近一筆交易是對 Ronin Bridge 合約進行升級,並且就在異常交易之前。所以漏洞的大致方面就基本明確,應該是專案方升級合約所導致的問題。

圖片

進一步深入,比較 Ronin Bridge 升級前後的程式碼發現,事件所使用的關鍵參數「_totalOperatorWeight」是本次升級新增的,並且需要在升級中呼叫 initializeV3 函數進行 V3 版本新增的「OperatorWeight」進行初始化。

圖片

但遺憾的是:在升級合約的交易中,並未呼叫 initializeV3 函數,而是錯誤地呼叫 initializeV4 函數進行了 V4 版本的初始化。

圖片

至此,該事件的漏洞原理已經明了,Ronin Bridge 專案方在合約升級時未正確對新增資料進行初始化,導致對應的關鍵資料「_totalOperatorWeight」始終為 0,使得任意用戶的提取請求都可以通過審核。

在本文發布前,項目方已經確認該問題,並發文說了本次攻擊行為是白帽所為,並已進行退款,並未造成過多的損失。這是一個好消息,但也揭露了合約升級這一大易出錯的點。

可升級合約


可升級合約是一種 solidity 智慧合約設計方案,使得已部署的合約能夠在未來進行升級或修改,而無需完全重新部署。這個概念的核心在於將合約的邏輯和資料分離,並利用「delegatecall」來實現對邏輯合約的呼叫。

圖片

儘管這種模式提供了靈活的升級能力,我們也必須高度重視其安全性。由於代理合約負責轉發所有的使用者請求,它實際上成為了合約系統的入口點。任何對代理合約的攻擊都可能影響到整個合約的安全性。因此,在設計和部署可升級合約時,確保代理模式的安全性至關重要。目前代理模式的合約需要注意以下幾點:

函數選擇器衝突

在以太坊虛擬機器(EVM)中,每個智慧合約函數都有一個唯一的標識符,稱為函數選擇器。這個選擇器是函數簽署的前 4 個位元組的雜湊值。函數選擇器用於確定合約中的特定函數,確保呼叫請求被正確路由到對應的函數實作。而在代理模式在呼叫函數時,會先檢查代理合約中的函數介面是否能匹配呼叫函數,如果不能匹配,才會利用 fallback 中的 delegatecall 呼叫邏輯合約。

因此,如果代理合約和邏輯合約中存在函數選擇器相同的函數,當代理合約接收到呼叫時,它直接呼叫代理合約中的函數,而不是邏輯合約,這可能導致預期之外的行為或安全漏洞。

儲存衝突

在以太坊虛擬機器(EVM)中,合約的狀態資料儲存在特定的儲存槽中,每個儲存槽的位址由其索引(從 0 開始)決定。合約中的每個狀態變數都對應一個儲存槽,資料在這些槽中持久化。

在代理模式中,儲存槽通常是由代理合約管理的,而邏輯合約則透過代理合約來存取這些槽。儲存衝突可能發生在邏輯合約中新增的狀態變數與代理合約中現有的狀態變數槽發生衝突時。這可能導致代理合約中的資料被覆蓋或不一致

合約初始化問題

在代理模式中,由於代理合約和邏輯合約的分離,而每次升級可能都涉及變數的變更和新增,因此在升級時必須確保在正確設定這些關鍵變數。這次的 ronin bridge 事件就是因為這個問題沒做好,導致被攻擊。

此外,需要確保初始化函數 initialize 只能被調用一次,以防止在初始化後被惡意攻擊者重複調用,從而修改關鍵變數。

ldelegatecall 呼叫機制

delegatecall 是一種低階呼叫機制,它允許一個合約在其上下文中執行另一個合約的程式碼。這意味著,呼叫合約的儲存、位址和訊息發送者保持不變,但執行的邏輯來自被呼叫的目標合約。雖 delegatecall 提供了強大的功能,但也需要謹慎使用。

如果目標合約位址不存在,delegatecall 的執行將失敗並傳回失敗碼,但這種失敗可能不會被立刻發現。結果,代理合約中的呼叫可能看起來像是成功,但實際操作並未生效,從而導致系統的不一致或錯誤。

代理合約權限管理

在代理模式中,權限管理是另一個關鍵的安全性問題。代理模式透過分離代理合約和邏輯合約的職責,使得合約可以在不改變資料儲存的情況下進行升級,但同時也引入了複雜的權限管理問題。正確地管理權限對於確保合約系統的安全性和穩定性至關重要。

免責聲明:作為區塊鏈資訊平台,本站所發布文章僅代表作者及來賓個人觀點,與 Web3Caff 立場無關。文章內的資訊僅供參考,均不構成任何投資建議及要約,並請您遵守所在國家或地區的相關法律法規。