就在上週一,CertiK 因發現重大安全漏洞 「倉鼠滾輪」,獲得了 SUI 50 萬美元漏洞賞金。下文中將從技術層面披露此關鍵漏洞的細節,闡明該漏洞的根本原因和潛在影響。
(前情提要:Sui公布「第二版」代幣釋放計畫,附註:不保證內容、更改不會通知)
(背景補充:Sui基金會鎖倉代幣竟在質押?KOL痛罵內幕「挖提賣」,造成SUI瘋狂通膨)
此前,CertiK 團隊於 Sui 區塊鏈發現了一系列拒絕服務漏洞。在這些漏洞中,一種新型且具有嚴重影響力的漏洞格外引人注目。該漏洞可導致 Sui 網路節點無法處理新的交易,效果等同於整個網路完全關閉。
就在上週一,CertiK 因發現該重大安全漏洞,獲得了 SUI 50 萬美元漏洞賞金。美國業內權威媒體 CoinDesk 對該事件進行了報導,隨後各大媒體也緊隨其報導發布了相關新聞。
該安全漏洞被形象地稱為 「倉鼠滾輪」:其獨特的攻擊方式與目前已知的攻擊不同,攻擊者只需提交一個大約 100 位元組的載荷,就能觸發 Sui 驗證節點中的一個無限迴圈,使其不能響應新的交易。
此外,攻擊帶來的損害在網路重啟後仍能持續,並且能在 Sui 網路中自動傳播,讓所有節點如倉鼠在輪上無休止地奔跑一樣無法處理新的交易。因此我們將這種獨特的攻擊型別稱為 「倉鼠滾輪」 攻擊。
發現該漏洞後,CertiK 通過 Sui 的漏洞賞金計劃向 Sui 進行了報告。Sui 也第一時間進行了有效回應,確認了該漏洞的嚴重性,並在主網啟動前積極採取了相應措施對問題進行了修復。除了修復此特定的漏洞外,Sui 還實施了預防性的緩解措施,以減少該漏洞可能造成的潛在損害。
為了感謝 CertiK 團隊負責地披露,Sui 向 CertiK 團隊頒發了 50 萬美元獎金。
下文中將從技術層面披露此關鍵漏洞的細節,闡明該漏洞的根本原因和潛在影響。
延伸閱讀:SUI 遭爆3月秘密修復「十億美元安全漏洞」,可讓駭客閃電貸攻擊..
漏洞詳解
驗證器在 Sui 中的關鍵作用
如 Sui 和 Aptos 這樣基於 Move 語言的區塊鏈,其防止惡意載荷攻擊的保障機製主要是靜態驗證技術。通過靜態驗證技術,Sui 可在合約發布或升級之前檢查使用者提交的載荷有效性。驗證器提供了一系列檢查器用來確保結構和語義的正確性,只有當通過檢查驗證後,合約才會進入 Move 虛擬機器被執行。
Move 鏈上的惡意載荷威脅
Sui 鏈在原始 Move 虛擬機器之上提供了一套新的儲存模型與介面,因此 Sui 有一個定製版的 Move 虛擬機器。為了支援新的儲存原語,Sui 進一步針對不可信載荷的安全驗證引入了一系列額外的、定製的檢查手段,如物件安全及全域性儲存訪問等功能。這些定製檢查手段契合了 Sui 的獨特功能,因此我們將這些定製檢查稱為 Sui 驗證器。
Sui 對載荷的檢查順序
如上圖所示,驗證器中的大多數檢查會針對 CompiledModule(表示使用者提供的合約載荷執行)進行結構層面的安全驗證。例如,通過 「重複檢查器」 確保執行時載荷中沒有重複的條目;通過 「限制檢查器」 確保執行時載荷中每個欄位的長度都在允許的條目上限之內。
除了結構層面的檢查之外,驗證器的靜態檢查仍需要更複雜的分析手段,以確保不可信載荷在語義層面的強健性。
延伸閱讀:Beosin:Move VM先前毀滅級漏洞,可讓Sui、Aptos「崩潰、甚至硬分叉」
瞭解 Move 的抽象直譯器:線性和迭代分析
由 Move 提供的抽象直譯器,是一個專門為通過抽象解釋在位元組碼上執行複雜安全分析而設計的框架。這種機製使得驗證過程更加精細和準確,每個驗證者都被允許定義他們獨特的抽象狀態從而進行分析。
在開始執行時,抽象直譯器從編譯的模組中構建控製流圖(CFG)。這些 CFG 中的每個基本塊都會維護一組狀態,即 「前序狀態」 和 「後序狀態」。「前序狀態」 提供了一個基本塊執行前的程式狀態快照,而 「後序狀態」 則提供了基本塊執行後的程式狀態描述。
當抽象直譯器在控製流圖中沒有遇到回跳(或迴圈)時,它則遵循一個簡單的線性執行原則:每個基本塊都被依次分析,並根據塊中每個指令的語義計算出前序狀態和後序狀態。其結果就是一個程式在執行過程中每個基本塊級別狀態的精準快照,幫助驗證程式的安全屬性。
然而,當控製流中存在迴圈時,這個過程則變得更加複雜。迴圈的出現意味著控製流圖中包含一條回跳的邊,回跳邊的源頭對應著當前基本塊的後序狀態,而回跳邊的目標基本塊(迴圈頂級)是一個之前已經分析過的基本塊的前序狀態,因此抽象直譯器需要對回跳相關的兩個基本塊的狀態進行仔細合併。
如果發現合併後狀態與迴圈頂級基本塊現有的前序狀態不同,抽象直譯器就會更新迴圈頂級基本塊的狀態,並從這個基本塊開始重新啟動分析。這個迭代分析過程將一直持續到迴圈預狀態穩定。換句話說,這個過程不斷重複,直到迴圈頂級基本塊的前序狀態在迭代之間不再變化。達到一個固定點,則表明迴圈分析已經完成。
Sui IDLeak 驗證器:定製的抽象解釋分析
與原來的 Move 設計不同,Sui 的區塊鏈平臺引入了一個獨特的以 「目標」 為中心的全域性儲存模型。這個模型的一個顯著特點是:任何具有 key 屬性(作為索引上鏈儲存)的資料結構必須以 ID 型別作為該結構的第一個欄位。ID 欄位不可改變,且不能轉移到其他目標上,因為每個物件必須有一個全域性唯一的 ID。為了確保這些特性,Sui 在抽象直譯器上建立了一套自定義分析邏輯。
IDLeak 驗證器,也被稱為 id_leak_verifier,與抽象直譯器協同工作進行分析。它有著自己獨特的 AbstractDomain,被稱為 AbstractState。每個 AbstractState 由多個區域性變數對應的 AbstractValue 組成。通過 AbstractValue 來監督每個區域性變數的狀態,以此來追蹤一個 ID 變數是否是全新的。
在結構體打包的過程中,IDLeak 驗證器隻允許將一個全新的 ID 打包到一個結構體中。通過抽象解釋分析,IDLeak 驗證器可以詳儘地跟蹤本地資料流狀態,以確保沒有現有的 ID 被轉移到其他結構體物件。
Sui IDLeak 驗證器狀態維護不一致問題
IDLeak 驗證器通過實現 AbstractState::join 函式與 Move 抽象直譯器整合。這個函式在狀態管理,特別是在合併和更新狀態值方面中起著不可或缺的作用。
詳細檢查這些函式以瞭解它們的操作:
在 AbstractState::join 中,該函式將另一個 AbstractState 作為輸入,並試圖將其本地狀態與當前物件的本地狀態合併。對於輸入狀態中的每個區域性變數,它將該變數的值與它在區域性狀態中的當前值進行比較(如果沒有找到,預設值為 AbstractValue::Other)。如果這兩個值不相等,它將設定一個 「changed」 的標誌,作為最終狀態合併結果是否變化的依據,並通過呼叫 AbstractValue::join 來更新本地狀態中的本地變數值。
在 AbstractValue::join 中,該函式將其值與另一個 AbstractValue 進行比較。如果它們相等,它將返回傳入的值。如果不相等,則返回 AbstractValue::Other。
然而,這個狀態維護邏輯包含一個隱藏的不一致性問題。儘管 AbstractState::join 會基於新舊值的不同而返回一個表示合併狀態發生變化(JoinResult::Changed)的結果,但合併更新後的狀態值仍然可能是不變的。
這種不一致的問題是由操作順序導致的:在 AbstractState::join 中對改變狀態的判定發生在狀態更新(AbstractValue::join)之前,這種判定並不反應真正的狀態更新結果。
此外,在 AbstractValue::join 中,AbstractValue::Other 對合併的結果起著決定性作用。例如,如果舊值是 AbstractValue::Other,而新值是 AbstractValue::Fresh,則更新的狀態值仍然是 AbstractValue::Other,即便新舊值不同,更新後狀態本身沒有變化。
這就引入了一個不一致:即合併基本塊狀態的結果被判定為 「改變」,但合併後的狀態值本身並沒有發生變化。在抽象解釋分析的過程中,出現這種不一致問題有可能產生嚴重的後果。我們回顧抽象直譯器在控製流圖(CFG)中出現迴圈時的行為:
當遇到一個迴圈時,抽象直譯器採用一種迭代的分析方法來合併回跳目標基本塊和當前基本塊的狀態。如果合併後的狀態發生變化,抽象直譯器則會從跳轉目標開始重新分析。
然而,如果抽象解釋分析的合併操作錯誤地將狀態合併結果標記為 「變化」,而實際上狀態內部變數的值沒有發生變化,就會導致無休止的重新分析,產生無限迴圈。
延伸閱讀:科普 | 以太坊EIP-4337「帳戶抽象(AA)」是什麼?往後錢包無註記詞!
進一步利用不一致在 Sui IDLeak 驗證器中觸發無限迴圈
利用這種不一致性,攻擊者可以構造一個惡意的控製流圖,誘使 IDLeak 驗證器進入一個無限迴圈。這個精心構造的控製流圖由三個基本塊組成:BB1 和 BB2,BB3。值得注意的是,我們有意引入了一條從 BB3 到 BB2 的回跳邊來構造一個迴圈。
這個過程從 BB2 開始,其中一個特定區域性變數的 AbstractValue 被設定為::Other。在執行 BB2 之後,流程轉移到 BB3,在那裡同一變數被設定為::Fresh。在 BB3 的結尾處,有一條回跳邊,跳轉到 BB2。
在抽象解釋分析這個例子的過程中,前文提到的不一致性起到了關鍵作用。當回跳邊被處理時,抽象直譯器試圖將 BB3 的後序狀態(變數為 「::Fresh」)與 BB2 的前序狀態(變數為 「::Other」)連線起來。AbstractState::join 函式注意到了這個新舊值不同的差異並設定了 「變化」 的標誌,以此表示需要對 BB2 的進行重新分析。
然而,AbstractValue::join 中 「::Other」 的主導行為意味著 AbstractValue 合併後,BB2 狀態變數的實際值仍然是 「::Other」,狀態合併的結果並沒有發生變化。
因此這個迴圈過程一旦開始,即當驗證器繼續重新分析 BB2 以及它的所有後繼基本塊節點(本例中為 BB3),它就會無限期地持續下去。無限迴圈消耗了所有可用的 CPU 週期,使其無法處理響應新的交易,這種情況在驗證器重新啟動後仍然存在。
通過利用這個漏洞,驗證節點如倉鼠在輪上無休止地奔跑一樣無限迴圈,無法處理新的交易。因此我們將這種獨特的攻擊型別稱為 「倉鼠滾輪」 攻擊。
「倉鼠滾輪」 攻擊可以有效地使 Sui 驗證器陷入停頓,進而導致整個 Sui 網路癱瘓。
理解了漏洞成因與觸發過程之後,我們通過使用以下 Move 位元組碼模擬構建了一個具體例子,成功地在真實環境中的模擬中觸發了該漏洞:
這個例子通過精心構造的位元組碼,展示瞭如何在真實環境中觸發漏洞。具體來說,攻擊者可以在 IDLeak 驗證器中觸發一個無限迴圈,利用僅僅約 100 位元組的載荷即可消耗 Sui 節點的所有 CPU 週期,有效阻止新交易處理,並導致 Sui 網路拒絕服務。
延伸閱讀:Sui台灣社群盛大同聚!Mysten Labs「核心談話」聚焦Web3生態前景
「倉鼠滾輪」 攻擊在 Sui 網路中的持續性危害
Sui 的漏洞賞金計劃對漏洞等級的評定有著嚴格的規定,主要依據對整個網路的危害程度進行評定。滿足 「嚴重(critical)」 評級的漏洞必須使整個網路關停並有效阻礙新交易確認,同時需要硬分叉來修復問題;如果漏洞隻能使部分網路節點拒絕服務,至多被評定為 「中危(medium)」 或 「高危(high)」 漏洞。
CertiK Skyfall 團隊發現的 「倉鼠滾輪」 漏洞可以使整個 Sui 網路關停,同時需要官方發布新版本進行升級修復。基於對該漏洞的危害程度,Sui 最終被將其評定為 「嚴重」 等級。為了進一步理解 「倉鼠滾輪」 攻擊造成的嚴重性影響原因,我們有必要了解 Sui 後端系統的複雜架構,特別是鏈上交易發布或升級的整個過程。
最初,使用者交易通過前端 RPC 提交,經基本驗證後傳遞到後端服務。Sui 後端服務負責進一步驗證傳入的交易載荷。在成功驗證了使用者的簽名後,交易被轉化為交易證書(包含交易資訊以及 Sui 的簽名)。
這些交易證書是 Sui 網路運作的基本組成部分,可以在在網路中的各個驗證節點之間傳播。對於合約建立 / 升級交易,在其可以上鏈之前,驗證節點會呼叫 Sui 驗證器檢查並驗證這些證書的合約結構 / 語義的有效性。正是在這個關鍵的驗證階段,「死迴圈」 漏洞可以被觸發利用。
當該漏洞被觸發時,它會導致驗證過程無限期中斷,有效阻礙系統處理新交易的能力,並導致網路完全關閉。雪上加霜的是,節點重啟後該情況仍然存在,這也就意味著傳統的緩解措施遠遠不夠。該漏洞一旦被觸發,則會出現 「持續破壞」 的情況從而對整個 Sui 網路留下持久影響。
Sui 的解決方法
經過 CertiK 回饋後,Sui 及時確認了該漏洞,並發布了一個修復程式來解決該關鍵缺陷。該修復程式確保了狀態改變和改變後標誌之間的一致性,消除了 「倉鼠滾輪」 攻擊造成的關鍵影響。
為了消除上述的不一致,Sui 的修復包括對 AbstractState::join 函式的一個微小但關鍵的調整。這個更新移除了在執行 AbstractValue::join 之前判定狀態合併結果的邏輯,取而代之的是首先執行 AbstractValue::join 函式進行狀態合併,通過比較最終更新結果和原始狀態值(old_value)來設定合併是否發生變化的標記。
這樣一來,狀態合併的結果與真實更新的結果將保持一致,分析過程中不會發生死迴圈。
除了修復這個特定的漏洞外,Sui 還部署了緩解措施,以減少未來驗證器漏洞的影響。根據 Sui 在 bug 報告中的回複,緩解措施涉及一個叫做 Denylist 的功能。
「然而,驗證器有一個節點配置檔案,允許他們暫時拒絕某些類別的交易。這個配置可以用來暫時禁止處理發布和軟體包升級。由於這個 bug 是在簽署發布或軟體包升級 tx 之前執行 Sui 驗證器時發生的,而拒絕列表將停止驗證器的執行並將惡意 tx 丟棄,暫時拒絕列表這些 tx 型別是一個 100% 有效的緩解措施(儘管它將暫時中斷試圖發布或升級程式碼的人的服務)。
順便提一下,我們有這個 TX 拒絕列表配置檔案已經有一段時間了,但我們也為證書添加了一個類似的機製,作為你之前報告的 「驗證器死迴圈」 漏洞的後續緩解手段。有了這個機製,我們將對這種攻擊有更大的靈活性:我們將使用證書拒絕名單配置來使驗證器忘記壞的證書(打破死迴圈),並使用 TX 拒絕名單配置來禁止發布 / 升級,從而防止建立新的惡意攻擊交易。謝謝你讓我們思考這個問題!
驗證器在簽署交易之前有有限的 “ticks”(與 gas 不同)用於位元組碼驗證,如果在交易中發布的所有位元組碼不能在這麼多 ticks 中得到驗證,驗證器將拒絕簽署該交易,防止它在網路上執行。以前,計量隻適用於一組選定的複雜驗證器通過。為了應對這個問題,我們將計量擴充套件到每個驗證器,以保證驗證器在每個 tick 的驗證過程中所執行的工作有一個約束。我們還修復了 ID 洩漏驗證器中的潛在無限迴圈錯誤。
— 來自 Sui 開發者關於漏洞修復的說明
總而言之,Denylist 使驗證者能夠通過禁用發布或升級流程來暫時規避針對驗證器中的漏洞利用並有效地防止一些惡意交易帶來的的潛在破壞。當 Denylist 的緩解措施生效時,節點通過犧牲自身的發布 / 更新合約功能,來確保自己能夠繼續工作。
總結
本文我們分享了由 CertiK Skyfall 團隊發現的 「倉鼠滾輪」 攻擊技術細節,解釋了這種新型攻擊是如何利用關鍵漏洞來導致 Sui 網路完全關閉的。此外,我們也仔細研究了 Sui 為修復這一關鍵問題所做的及時反應,並分享了漏洞修復以及後續同類漏洞緩解的方法。
📍相關報導📍
對話Mysten Labs共同創辦人兼密碼學家:Sui引入哪些密碼學創新?