影響62億臺裝置:解讀我是如何發現Marvell Avastar Wi-Fi遠端程式碼執行漏洞的
一、概述
在本次研究中,我將主要分析Marvell WiFi FullMAC SoC的安全性。由於我們尚未完成對該產品中帶有晶片的無線裝置的研究,因此其中可能包含大量未經審計的程式碼,這可能會出現嚴重的安全問題。本文是基於我在ZeroNights 2018演講整理而成,演講的展示幻燈片請參見 這裡 。此外,很多安全團隊都針對無線SoC安全這一話題開展了研究。例如,Google Project Zero在2017年4月釋出了一系列部落格文章,描述瞭如何在智慧手機上利用Broadcom Wi-Fi棧。這一話題同樣也在2017年BlackHat大會上進行了討論。一些智慧手機基帶漏洞利用的文章,可能有助於各位讀者理解用於逆向無線SoC韌體的技術。
二、無線裝置工作原理
一般來說,Wi-Fi的加密狗可以分為兩大類:FullMAC和SoftMAC。二者都需要韌體映像,在每次裝置啟動時都應該上傳。裝置製造商會提供適當的韌體映像和作業系統裝置驅動程式,所以在啟動期間,驅動程式可以上載韌體,從而使其主要功能可以被Wi-Fi SoC使用。下圖說明了這一過程。
啟動SoftMAC和FullMAC加密狗之間的主要區別在於其韌體功能。此外,FullMAC加密狗的韌體具有MLME(MAC層管理實體,MAC Layer Management Entity)。換而言之,它能夠在沒有作業系統驅動程式任何支援的情況下,處理一些Wi-Fi管理幀和事件實體。顯然,FullMAC加密狗的攻擊面會更大,所以我們也傾向於尋找這些裝置上潛在的漏洞。
三、Wi-Fi SoC與驅動程式之間的互動
在Linux核心中,有兩個版本的驅動程式用於處理Marvell Wi-Fi:
1、mwifiex驅動程式(可以在官方的Linux repo中找到);
2、mlan和mlinux驅動程式(可以在官方的steamlink-sdk repo中找到)。
二者都具有一些除錯功能,可以允許我們讀寫SoC記憶體。驅動程式使用內部格式,將資訊傳送到Wi-Fi SoC,並從SoC接收事件或響應。Wi-Fi SoC有如下幾種特定型別的資料:
·COMMAND(命令)
· EVENT(事件)
· DATA(資料)
· SINGLE PORT AGGREGATED DATA(單埠彙總資料)
Wi-Fi SoC和裝置驅動程式之間的互動模式如下所示:
我更傾向於考慮由韌體實現的API命令。這些命令可分為以下幾組:
1、SoC儲存器的讀/寫命令;
2、來自韌體的擴充套件版本資訊(例如:適用於SteamLink的15.68.7.p206、w8897o-B0、RF8XXX、FP68)
3、與Wi-Fi相關的內容(例如:協作、掃描等)
其中的一些命令,可以通過驅動程式實現的IOCTL或者一些特殊的debugfs檔案,從使用者模式(Usermode)訪問。驅動程式最有用的功能之一就是它可以進行韌體記憶體轉儲。這將有助於除錯我們的動態檢測方法或漏洞利用方法。根據分析,超時機制似乎是在作業系統驅動程式中實現的。因此,當命令響應超時時,驅動程式將嘗試轉儲Wi-Fi SoC記憶體,並將其儲存在主檔案系統中。在mwifiex和mlan+mlinx驅動程式中,記憶體轉儲具有不同的格式。經過對mwifiex PCI驅動程式進行研究,我發現它是以類似韌體映像的格式儲存了完整的Wi-Fi SoC記憶體轉儲。SDIO版本的mlan+mlinx驅動程式僅以二進位制格式儲存了ITCM、DTCM和SQRAM區域。
四、韌體分析
如前所述,Marvell Avastar Wi-Fi晶片組系列使用了韌體檔案,並在這些檔案中承載了大部分裝置的功能。ROM中包含啟動程式碼,並在將主韌體載入到晶片RAM之前負責與主機進行互動。官方linux-firmware git repo提供了幾個版本的韌體。因此,我們首先需要研究驅動程式初始化Wi-Fi SoC所使用的韌體映像。
4.1 靜態韌體檔案分析
為了獲得與韌體RAM映像結構相關的資訊,我們可以檢視Marvell Wi-Fi驅動程式程式碼,該程式碼將韌體載入到Wi-Fi SoC(drivers/net/wireless/marvell/mwifiex/fw.h)。
... struct mwifiex_fw_header { __le32 dnld_cmd; __le32 base_addr; __le32 data_length; __le32 crc; } __packed; struct mwifiex_fw_data { struct mwifiex_fw_header header; __le32 seq_num; u8 data[1]; } __packed; ...
各位讀者可能會注意到,韌體檔案中包含一些帶有頭部和校驗和(Checksum)的記憶體塊。在塊的頭部,還包含SoC中的地址,該記憶體即將被載入。掌握了這些知識以後,我們可以在IDA Pro中檢視Marvell Avastar的韌體檔案,從而進行進一步研究。
在初步逆向之後,我們可以發現88W8897是具有8個MPU區域的ARM946微控制器。所有記憶體都是RWX許可權。韌體檔案中,還包含對ROM功能的引用。因此,為了進一步研究,我們需要一個ROM轉儲。下圖展示了88W8897 Wi-Fi晶片的記憶體對映,未知的記憶體區域似乎是記憶體對映的暫存器區域。
4.2 動態韌體分析 – ThreadX執行時結構恢復
我們可以利用讀寫命令,建立一個可以轉儲記憶體和指示韌體的簡單工具。利用該工具,我們可以獲得一些執行時資訊。值得注意的是,韌體是一個非常大的二進位制檔案。其中只包含幾個字串,並且沒有任何關於裝置執行原理以及潛在的漏洞挖掘位置的提示資訊。但是,在研究了ROM轉儲(利用我們的工具)之後,我們可以發現,這是一個基於ThreadX的韌體。
ThreadX是一種廣泛應用於智慧裝置的特有RTOS。可以使用許可證,獲取此RTOS的原始碼。ThreadX基本上只是一個執行時環境,是用於管理動態記憶體、執行緒以及執行緒之間通訊的函式。ThreadX也是最受歡迎的RTOS之一,部署數量超過60億(根據官方提供的數字)。根據其中的ID欄位,我們可以在記憶體中搜索ThreadX執行時結構,如果該結構有效,那麼ID欄位中似乎包含特定值。例如,線上程結構中:
typedefstruct TX_THREAD_STRUCT { /* The first section of the control block contains critical information that is referenced by the port-specific assembly language code.Any changes in this section could necessitate changes in the assembly language.*/ ULONGtx_thread_id;/* Control block ID*/ ... }
第一個4位元組欄位tx_thread_id中,必須包含值0x54485244(ASCII值為THRD)。這就給了我們更多的資訊,因為其中一些ThreadX物件可以包含名稱,這樣我們就能部分猜測出它們的用途。我們將ThreadX執行時結構進行重構,作為IDA指令碼。這樣一來,它就可以用於研究另一個基於ThreadX的裝置記憶體轉儲。ThreadX結構重構後的一些摘要資訊請參見下表(地址是用於內部版本為w8897o-B0、RF8XXX、FP68、15.68.7.p206的預設Steamlink韌體):
4.3 動態韌體分析 – 動態韌體檢測
我們的目標是尋找韌體中存在的漏洞,但我們關於目標的資訊非常少,具體是由於如下原因:
1、原始碼不可用;
2、處理或解析幀的程式碼是未知的;
3、Wi-Fi SoC包含少量記憶體,足以滿足其目的,我們無法將程式碼放在Wi-Fi SoC中進行模糊測試或量化測試。
針對上面的第一點和第三點,我們找不到任何變通方法。因此,我們可以嘗試執行韌體,來尋找負責處理Wi-Fi幀的函式。藉助讀寫命令,可以在Wi-Fi SoC上進行幾種型別的執行時分析。
1、利用一些拼接技術,我們可以掛鉤一個函式;
2、可以替換一些類似除錯或日誌的例程的指標;
3、可以跟蹤池分配/釋放;
4、甚至可以使用靜態Thumb函式呼叫(例如:函式級別的DBI)來檢測整個程式碼區域。
藉助我們的Wi-Fi SoC研究工具,前三點非常簡單。但最後一個問題可能看起來比較棘手。基本上,我們的工具使用capstone反彙編引擎來查詢Thumb BL(用於函式呼叫)指令,並將其替換為對instrumentation stub的呼叫。該instrumentation stub負責呼叫我們的自定義DBI工具,具有正確引數的原始韌體函式,並返回呼叫。各位讀者可能會發現,它與具有函式級別檢測的DBI框架非常相似。該演算法非常簡單:
我們可以從下圖中,看到有關instrumentation stub工作流程的更多詳細資訊:
綜上所述,為了在Wi-Fi SoC上設定程式碼,我們需要:
1、從Wi-Fi SoC讀取待檢測的儲存區域。
2、使用capstone對其進行反編譯。
3、編寫修補程式碼,將其用於修補記憶體中的韌體,並呼叫檢測使用者定義例程的結構。
4、將這些結構、特定的修補程式碼、stub和使用者定義的例程複製到Wi-Fi SoC。
5、藉助掛鉤後的例程,並使用常規韌體API對其進行呼叫,來執行修補程式。驅動程式很少會呼叫這一韌體功能。這會確保我們可以安全地禁用中斷處理和程序檢測。
6、在檢測工具收集到必要的執行時資訊之後,使用韌體/驅動程式功能訪問Wi-Fi SoC記憶體,從Wi-Fi SoC記憶體中複製結果。
值得注意的是,該型別的instrumentation存在一些微架構(Microarchitectural)問題,其原因在於我們對instrumentation stub的新BL呼叫覆蓋了Wi-Fi SoC上的指令。因此,在I/D-Cache快取不一致的情況下,我們可能會丟失一些結果(某些呼叫可能無法執行,因為來自I-cache的舊原始指令仍然有效)。我們推測,其原因在於韌體鎖定ARM CP15協處理器(Coprocessor)暫存器,用於在初始化後執行寫入。因此,要在Wi-Fi SoC上重新整理I-cache並不是一件輕而易舉的事。還有另外一種技術可以研究Wi-Fi SoC內部,就是靜態韌體instrumentation。然而,它需要每次重建Wi-Fi韌體,以應用新的分析Payload。還需要裝置的重新啟動,以啟動已檢測的韌體。有幾種型別的DBI工具,可以在這裡提供幫助:
1、能搜尋函式引數(例如BSSID或MAC)中籤名的工具;
2、能收集有關呼叫棧資訊(該資訊可以對逆向工程或韌體模糊測試提供幫助)的工具;
3、能監視ThreadX塊池狀態的工具。
上述這些工具,都為我們提供了程式碼處理框架的資訊,因為我們可以使用不同的客戶端二進位制檔案自定義DBI工具。在沒有原始碼和任何逆向提示(例如:記錄字串、匯出的函式名稱)的情況下,這是向前邁出的一大步。在將這種型別的動態分析應用於執行中的韌體之後,我們就可以瞭解哪些函式用於解析輸入幀和引數,其中輸入資料被傳遞到這些函式。之後,就可以使用多種型別的二進位制分析和漏洞搜尋技術。
五、尋找漏洞
儘管我們已經在韌體記憶體轉儲和Wi-Fi SoC內部,採用了各種型別的二進位制分析(包括靜態和動態),但仍然很難搜尋漏洞。
5.1 模糊測試
就目前而言,似乎只有兩種型別的模糊測試可以使用:
1、無線隨機模糊測試;
2、模擬環境中對韌體進行模糊測試。
第一種型別的模糊測試,可以直接對Wi-Fi SoC進行。通常,可以使用包括JTAG、ARM ETM或Intel Process Tracing技術在內的處理器自身功能,來實現收集邊緣覆蓋的目標。但是,這一過程還需要晶片自身的硬體支援,以及一些硬體黑客方面的知識儲備,才能在生產環境的裝置中使用硬體除錯功能。這是一項非常重要的工程任務,因此盲目地進行模糊測試是非常愚蠢的。接下來,我們就嘗試第二種型別。第二種型別的模糊測試依賴於韌體模擬,因此在一些反饋驅動演算法(Feedback-driven Algorithm)的幫助下,收集突變輸入(Mutating Input)的邊緣覆蓋就相對容易。這實際上是對無線裝置進行SMART模糊測試。說起來可能令人驚訝,但是允許我們以這種方式來進行模糊測試的工具已經淘汰了。因此,我們可以使用由原始的AFL模糊工具和名為 afl-unicorn 的Unicorn CPU模擬器的混合工具,最初由Nathan Voss編寫。我們可以檢視相關文件以瞭解其工作原理, 包括模糊任意程式碼的方法 ,以及 CGC二進位制示例 。因此,要使用afl-unicorn工具來對Wi-Fi韌體進行模糊測試,我們需要識別解析例程(例如:使用Wi-Fi SoC DBI工具),並編寫一個將突變輸入(Mutated Input,Wi-Fi幀)輸入這些例程的模糊工具。大致來說,模糊工具應該完成以下工作:
1、使用修改版本的Unicorn對映必要的記憶體區域;
2、設定暫存器上下文;
3、讀取改變後的輸入檔案,並將其對映到模擬器記憶體中;
4、開始執行程式碼;
5、通過傳送特定訊號,模擬韌體崩潰。
看起來,這是一種簡單有效的技術,但仍然存在一些缺點。最值得注意的是對全域性狀態的依賴性,這一點是在建立Wi-Fi SoC記憶體轉儲時捕獲的。該狀態可以包含一些已經儲存的全域性變數,這些變數可能會組織模糊工具訪問某些執行路徑。由於沒有動態記憶體訪問清理,很難找到並刪除校驗和(Checksum)驗證程式碼。此外,無法實現RTOS任務之間的通訊,因此這也會阻止一些潛在的有趣的執行路徑。但是,使用這種模糊測試技術,還是有一些結果的:
藉助這種技術,我成功在韌體的某些部分識別出大約4個記憶體損壞問題。儘管如此,由於AFL能夠以某種方式改變輸入(例如:在模糊函式之前執行一些檢查),所以就無法傳遞給模糊後的函式,因此很難研究由這些問題導致的潛在影響。我還嘗試在不同版本的韌體和不同版本的無線SoC上覆現這些漏洞,看上去很多漏洞都存在於其中。
六、漏洞利用
其中的一個漏洞,是ThreadX塊池溢位的特例。在掃描可用網路期間,無需與使用者進行互動,即可觸發這一漏洞。無論裝置是否連線到某個Wi-Fi網路,該過程都將每分鐘啟動一次。這樣一來,就充分說明了這一漏洞的嚴重性,它導致在任何無線連線狀態下,即使裝置沒有連線到網路,也可以在無需互動的情況下,實現漏洞利用。例如,只需通過一臺Samsung Chromebook,即可實現遠端程式碼執行漏洞的利用。因此,總結一下我們發現的漏洞:
1、無需任何使用者互動;
2、在GNU/Linux作業系統下,可以每隔5分鐘觸發一次;
3、無需知道Wi-Fi網路名稱或密碼/金鑰;
4、裝置可以不連線到任何Wi-Fi網路,只要開啟電源即可觸發漏洞。
在這裡,我將詳細描述如何在Wi-Fi SoC上實現任意程式碼執行。關於提升技術的詳細資訊也會在後面進一步介紹。
6.1 基本ThreadX塊池溢位漏洞利用
ThreadX塊池是一個連續的記憶體區域被分為較小的塊。每個塊池都由執行時結構表示,可以通過IDA指令碼在記憶體轉儲中具體看到。在每個塊的開頭,都有一個指向下一個空閒塊的指標。在最後一個空閒塊之前,存在NULL指標。第一個空閒指標儲存在ThreadX塊池管理結構中。指向該結構的指標,將用於塊池分配和銷燬功能。
我們發現,攻擊者可以覆蓋指向下一個空閒塊和控制位置的指標,從而分配下一個塊。通過控制下一個塊分配的位置,攻擊者可以將此塊放置到某些關鍵執行時結構或指標所在的位置,從而實現程式碼執行。
6.2 Marvell Avastar ThreadX塊池溢位漏洞利用
在Marvell Avastar韌體中的大多數記憶體管理例程,都依賴於特殊的包裝函式(Wrapper Function)。該函式在每個ThreadX塊的開頭使用特殊元資料頭。通過對此函式進行逆向工程,可以發現這些頭中包含特殊指標,這些指標在釋放塊之前就被呼叫。因此,針對Marvell Avastar的韌體,攻擊者可以輕鬆地在無線SoC上實現程式碼執行。下面是允許執行人意指標的塊解除分配器(Deallocator)的虛擬碼:
要執行程式碼,攻擊者只需覆蓋下一個塊的更多額外空間(僅在它被使用的情況下):
6.3 二者結合的漏洞利用
因此,我們有兩種技術可以實現ThreadX塊池溢位漏洞的利用。一個是通用技術,可以應用於任何基於ThreadX的韌體,另一種技術特定於Marvell Wi-Fi韌體的實現。如果將二者結合在一起,那我們就可以實現可靠的利用。
七、Valve Steamlink提升至應用程式處理器的示例
Valve Steamlink是一個簡單的桌面流媒體裝置,可以讓使用者在計算機上執行電腦遊戲,並將遊戲介面以流媒體的方式傳輸到電視盒子。該裝置的韌體基於類似Debian的GNU/Linux作業系統,使用3.8.13-mrvl Linux核心,在ARM7l應用處理器上工作。該產品也包含Marvell 88W8897無線晶片組,可以連線SDIO匯流排、專有的mlan.ko以及mlinux.ko裝置驅動程式。我們注意到,可能是由於其高效能的802.11ac和Bluetooth COMBO,大多數使用了Marvell Wi-Fi的都是遊戲裝置,例如PS4。因為這些裝置具有DRM保護,所以我們很難對其進行研究。因此,我們選擇了SteamLink,因為它沒有DRM,並且可以輕鬆啟動其工具和核心模組來研究無線SoC。值得一提的是,Microsoft Surface和Samsung Chromebook都使用了Marvell Wi-Fi。
7.1 升級攻擊面
要在SteamLink的應用程式處理器上執行程式碼,我們需要進行第二次許可權提升,因為SDIO匯流排沒有設計從裝置到主機的直接記憶體訪問。像PCIe這樣的匯流排是允許DMA的,因此提升過程會簡單得多。在這種情況下,許可權提升漏洞的利用類似於遠端漏洞利用。唯一的區別就在於攻擊者是通過SDIO匯流排,從受控Wi-Fi SoC傳送資料,而不是通過網路傳送資料。我們可以將典型的裝置驅動程式,看作是裝置與應用程式或作業系統之間的橋樑。因此,它應該從裝置接收資料,並進行解析,然後將其傳送到應用程式(作業系統)上,反之亦然。其中包含解析從裝置接收的資料的程式碼。在Marvell Wi-Fi驅動程式中,這部分程式碼負責處理由資訊元素(Information Elements)組成的許多型別的訊息。事實上,升級後的攻擊面非常廣泛。
7.2 利用AP裝置驅動程式漏洞
我們發現的漏洞也非常容易利用,是基於棧的緩衝區溢位漏洞。Linux核心“3.8.13-mrvl”中也沒有二進位制漏洞利用的緩解措施。但是,由於I/D-cache不連續,並且回寫緩衝區延時提交(Write-back Buffer Deffer Commit),還需要一些準備階段。此外,由於函式尾部無法控制棧,因此會從棧本身彈出(POP)棧指標:
LDMFDSP, {R4-R11,SP,PC}
要想成功利用許可權提升漏洞,應該執行以下操作:
1、呼叫v7_flush_kern_cache_louis Linux核心函式;
2、執行ShellCode。
由於棧指標丟失,我們無法將小工具(Gadget)放在棧上。相反,我們可能依賴於暫存器R4-R11,它們也會在執行繼續道回覆PC位置之前從棧中恢復。首先,我們需要在一個基本塊中找到一個包含兩個不同暫存器呼叫的特殊小工具。這個小工具表示兩個主要操作的呼叫:重新整理快取、呼叫ShellCode。
BLXR3 MOVR1, R4 MOVR2, R5 SUBSR3, R0, #0 MOVR0, R10 BNEloc_C00E7678 BLXR9
儘管它包含一個條件分支,但永遠不會被佔用,因為v7_flush_kern_cache_louis總會返回0。並且,它也不會破壞R9,這個地方可以被攻擊者控制。然而,第一次呼叫是使用R3暫存器進行的,該暫存器不會從棧中恢復。在這種情況下,應該搜尋小工具,以在呼叫主要值之前先在R3中放置受控制的值。例如,像這樣:
MOVR3, R8 BLXR7
最後的小工具應該負責計算ShellCode的位置,並將執行轉到相應位置。在這種情況下,我們可以使用R0、R1、R2、R3和R12,因為其中可以包含棧指標。對於Marvell的驅動程式來說,R12確實包含棧中的地址。因此,需要找到一個小工具,它將使用受控制的暫存器和R12來計算實際的ShellCode位置,並轉到執行,如下所示:
LDRR6, [R12,R4,LSL#4] MOVR7, R0 ADDR4, R12, R4,LSL#4 MOVR8, R2 BLXR6
還應該注意的是,攻擊者可以通過使用thumb指令編碼,顯著增加可以使用的小工具的數量。實際上,在溢位期間有幾種不同的R12指標位置。我認為,這是取決於當前的掃描狀態。我們可以研究如何正確地將事件緩衝區從Wi-Fi SoC傳送到AP,最終的棧佈局將始終相同。總體來說,利用的成功率在50%-60%左右。
7.3 利用Valve Steamlink的要求
在研究中,我在監控模式(Monitor Mode)下使用ALFA網路無線介面卡,它是基於Realtek 8187無線晶片組。該漏洞可以使用Python Scapy框架實現。出於某種原因,Ubuntu GNU/Linux版本不太適合迅速注入Wi-Fi幀,我們最好使用Kali。我們可以在這個視訊中,看到完整漏洞利用演示,演示的Payload只是週期性在核心日誌中列印資訊。
視訊連結: https://youtu.be/syWIn62M72Y
八、總結
從這個故事中,我們可以學習到:
1、無線裝置通常存在巨大的攻擊面;
2、通常,在無線SoC上,沒有漏洞利用的緩解措施;
3、裝置驅動程式可能會暴露出更廣泛的攻擊面,以便從裝置升級到主機應用程式處理器,即使在裝置無法直接訪問主機記憶體的情況下也是如此。