Numen Cyber獨家發現move語言又一高危漏洞

2022-11-22 22:11:59

0x0 前言 

之前我們發現了一個Aptos Moveevm()的嚴重漏洞,經過深入研究,我們發現了另外一個新的整數溢出漏洞,這次一的漏洞觸發過程相對更有趣一點, 下面是對這個漏洞的深入分析過程,裏面包含了很多Move語言本身的背景知識.通過本文講解相信你會對move語言有更深入的理解。 

衆所周知,Move 語言在執行字節碼之前會驗證代碼單元。驗證代碼單元的過程,分為4步。這個漏洞就出現在 reference_safety 的步驟中。 


      Numen Cyber獨家發現move語言又一高危漏洞

如上面代碼所示, 此模塊定義了用於驗證過程主體的引用安全性的轉移函數。其檢查包括(但不限於)驗證沒有懸空引用、對可變引用的訪問是否安全、對全局存儲引用的訪問是否安全。 

下面是引用安全驗證入口函數, 它將調用analyze_function. 


      Numen Cyber獨家發現move語言又一高危漏洞

在analyze_function中,函數將對每一個基本塊進行驗證,那么什么是基本塊呢? 

在代碼編譯領域,基本塊是一個代碼序列,除了入口之外沒有分支指令,除了出口之外沒有分支指令。 

Move 語言是如何識別基本塊? 

在Move 語言中, 基本塊是通過遍歷字節碼、查找所有分支指令以及循環指令序列來確定的。以下是核心代碼: 


      Numen Cyber獨家發現move語言又一高危漏洞


      Numen Cyber獨家發現move語言又一高危漏洞

接下來,我們來分享一個move ir代碼基本塊的例子, 如下所示, 它 3 個基本塊。分別由分支指令:BrTrue,Branch,Ret確定。 


      Numen Cyber獨家發現move語言又一高危漏洞

0x1 Move 中的參考安全性 

參考Rust語言的思想,Move 支持兩種類型的引用類型。不可變引用&(例如&T)和可變引用&mut(例如&mut T)。你可以使用不可變 (&) 引用從結構中讀取數據,使用可變 (&mut)引用 修改它們。通過使用恰當的引用類型,有助於維護安全性以及識別讀取模塊。這樣可以讓讀者清晰地知道此方法是更改值還是僅讀取。 下面是官方Move教程中的示例: 


      Numen Cyber獨家發現move語言又一高危漏洞

在示例中,我們可以看到mut_ref_t是 t 的可變引用。 

所以在Move 引用安全模塊中,嘗試通過以函數為單元,掃描函數中的基本塊中的字節碼指令驗證判斷所有引用操作是否合法。 

下圖顯示了驗證引用安全性的主要流程。 


      Numen Cyber獨家發現move語言又一高危漏洞

這裏的state是AbstractState結構體, 它包含了borrow graph 和locals ,他們共同用於確保引用函數中的引用安全性。 


      Numen Cyber獨家發現move語言又一高危漏洞

這裏borrow graph是用來表示局部變量引用之間關系的圖。 

從上圖中可以看到, 這裏有一個pre state ,其包含locals 和borrow graph (L ,BG)。然後執行了basic block 生成一個post state (L’, BG’)。然後將前後的state合並以更新塊狀態並將該塊的後置條件傳播到後續塊。這就像 V8 turbofan中的Sea of Nodes 思想。 

下面的代碼是上圖對應的主循環。首先, 執行塊代碼(如果執行指令不成功,將返回 AnalysisError)然後嘗試通過join_result是否更改,來合並pre state和post state。如果更改並且當前塊本身包含一個後向的邊指向自己(這意味着有一個循環)將跳回到循環的开頭, 在下一輪循環仍將執行此基本塊,直到post state等於pre state或因某些錯誤而中止。 


      Numen Cyber獨家發現move語言又一高危漏洞

因此在引用安全模塊,如何判斷 join的結果是否改變? 


      Numen Cyber獨家發現move語言又一高危漏洞

通過上面的代碼,我們可以通過判斷locals和borrow關系是否發生變化來判斷join結果是否發生變化。 這裏的 join_ 函數用於更新本地變量和 borrow關系圖 。 

下面是join_ 函數代碼,第 6 行是初始化一個新的 locals Map 對象。 第 9 行迭代 locals 中的所有索引,如果pre state與 post state都值為 None,則不要插入到新的 locals 映射中,如果 pre state 有值,post state為None,則需要釋放 brow_graph id ,意味着這裏消除該值的借用關系, 反之亦然。特別的,當pre state與 post state 兩個值都存在且相同時,像第30-33行一樣將它們插入到新的map中,然後在第38行合並 borrow graph。 


      Numen Cyber獨家發現move語言又一高危漏洞

通過上面代碼,我們可以看到 self.iter_locals() 是locals變量的個數。 請注意,此局部變量不僅包括函數的真實局部變量,還包括參數。 

0x2 漏洞 

在這裏我們已經覆蓋了所有與漏洞相關的代碼,你找到漏洞了嗎?

如果你沒有發現漏洞也沒關系,下面我會詳細說明漏洞觸發過程。 

首先在下面的代碼中,如果參數長度添加局部長度大於 256。這似乎沒有問題? 


      Numen Cyber獨家發現move語言又一高危漏洞

當函數 join_() 中是 function_view.parameters().len() 和 function_view.locals().len() 組合值大於 256,由於語句 for local in self.iter_locals()中 local 是 u8類型,此時執行此語句對造成溢出。 

實際上Move有校驗locals個數的過程,可惜在check bounds模塊只校驗locals,並沒有不包括參數length。 

开發者似乎只檢查了 Move Modules代碼中的 locals+parameter 長度,而忽略了script。 


      Numen Cyber獨家發現move語言又一高危漏洞

0x3 Move overflow to DoS 

通過上面的介紹,我們知道有一個主循環來掃描代碼塊,然後調用execute_block函數,之後會合並執行前後的state,當move代碼中存在循環,則會跳轉到代碼塊开始,再次執行基本塊。 因此,如果我們制造一個循環代碼塊並利用溢出改變塊的state,使 AbstractState 對象中的新的locals map與之前不同,當再次執行 execute_block 函數時,在分析basic block中字節碼指令序列的時候會訪問新的locals map,這時候如果指令中需要訪問的索引在新的AbstractState locals map中不存在,將導致DoS。 


      Numen Cyber獨家發現move語言又一高危漏洞

在審核代碼後,我發現在 reference safety模塊中,MoveLoc/CopyLoc/FreeRef 操作碼,我們可以實現這個目標。 

這裏讓我們看一下文件路徑中execute_block函數調用的copy_loc函數作為一個說明: 

move/language/move-bytecode-verifier/src/reference_safety/abstract_state.rs 


      Numen Cyber獨家發現move語言又一高危漏洞

在第287行,代碼嘗試通過LocalIndex作為參數獲取本地值,如果LocalIndex不存在會導致panic,想象一下當節點執行滿足上述條件代碼的時候,會導致整個節點崩潰。 

0x4 PoC 

下面是你可以在git裏面重現的PoC:

commit:add615b64390ea36e377e2a575f8cb91c9466844


      Numen Cyber獨家發現move語言又一高危漏洞

這是崩潰日志: 

thread 'regression_tests::reference_analysis::PoC' panicked at 'called `Option::unwrap()` on a `None` value', language/move-bytecode-verifier/src/reference_safety/abstract_state.rs:287:39 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 

DoS的觸發步驟:

我們可以看到PoC中代碼塊存在一個basic block,分支指令是一個無條件分支指令,每次執行最後一條指令時,branch(0) 將跳回第一條指令,因此這個代碼塊將多次調用 execute_block 和 join 函數。

1.在第一次執行完 execute_block 函數,當這裏設置 parameters 為SignatureIndex(0),locals為SignatureIndex(0)會導致num_locals為132*2=264。 所以在執行join_函數下面這行代碼之後  

將會導致新的locals map長度為8. 

2.在第二次執行execute_block函數時,執行move代碼第一條指令copyloc(57),57是locals需要壓入棧的offset,但是這次locals只有長度8,offset 57不存在,所以會導致 get(57).unwrap() 函數返回 none ,最後導致panic。 

0x5 總結

以上就是這個漏洞的來龍去脈。首先這個漏洞說明沒有絕對安全的代碼, Move語言在代碼執行之前確實做了很好的靜態校驗,但是就像這個漏洞一樣,可以通過溢出漏洞完全繞過之前的邊界校驗。再者代碼審計很重要,程序員難免會疏忽。作為Move語言安全研究的領導者,我們將繼續深挖Move的安全問題。第三點,對於Move語言,我們建議語言設計者在move運行時增加更多的檢查代碼,以防止意外情況的發生。目前move語言主要是在verify階段進行一系列的安全檢查,但我覺得這還不夠,一旦驗證被繞過,運行階段沒有過多的安全加固,將導致危害進一步加深,引發更嚴重的問題。最後,我們還發現了Move語言的另一個漏洞,後續會繼續分享給大家。 

0x6 參考資料 

  • https://github.com/move-language/move 

  • https://github.com/MystenLabs/awesome-move 

  • https://move-language.github.io/move/ 

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。

推薦文章

Layer2 格局劇變:Base 生態有哪些關鍵亮點?

在激烈競爭的 L2 賽道中,原本穩坐釣魚臺的 Arbitrum 和 Optimism 似乎面臨着前...

加密泡泡啊
71 1個月前

XRP 漲至 7.5 美元?分析師告訴 XRP 大軍為純粹的煙火做好准備!

加密貨幣分析師 EGRAG 表示,XRP 即將迎來關鍵時刻,價格可能大幅上漲,這取決於能否突破關鍵...

加密泡泡啊
73 1個月前

以太坊ETF通過後 將推動山寨幣和整個加密生態大爆發

比特幣ETF通過後市場動蕩,以太坊ETF交易前景分析 比特幣ETF通過後,市場出現了先跌後漲的走勢...

加密泡泡啊
78 1個月前

ZRO為啥這么能漲?

ZRO概述 ZRO代幣,全稱為LayerZero,是LayerZero協議的本地代幣,旨在作為治理...

加密泡泡啊
66 1個月前

今晚ETH迎來暴漲時代 op、arb、metis等以太坊二層項目能否跑出百倍幣?

北京時間7月23日晚上美股开盤後 ETH 的ETF开始交易。ETH的裏程碑啊,新的時代开啓。突破前...

BNBCCC
75 1個月前

Mt Gox 轉移 28 億美元比特幣 加密貨幣下跌 ETH ETF 提前發行

2014 年倒閉的臭名昭著的比特幣交易所 Mt Gox 已向債權人轉移了大量比特幣 (BTC),作...

加密圈探長
71 1個月前