用故障注入和二進制分析對BootLoader實施攻擊
雙電壓毛刺的故障攻擊,側信道史上首次!
背景介紹
Bootloader是一段在嵌入式操作系統內核運行之前,用于初始化硬件設備、建立內存空間映射圖,從而將系統的軟硬件環境帶到一個合適狀態,以便為最終調用操作系統內核準備好正確的環境的程序段,可以理解成BIOS,因此Bootloader對于嵌入式系統的安全運行起到至關重要的作用。
2020年12月,來自伯明翰大學的Jan、David、Flavio等人在頂級學術期刊TCHES上發表了一篇論文,該論文所做的事情就是通過硬件及軟件手段繞過Bootloader的保護機制,從而讀取微控制器內敏感的固件信息,避免與復雜的密碼算法打交道,其中硬件手段部分完成了側信道史上首次雙電壓毛刺的故障攻擊。
這篇論文的結構如下:論文首先介紹了實驗配置,然后依次對三家廠商的四種型號的微控制器進行了分析與實驗,最后總結了在實驗過程中發現的不安全的bootloader設計。
實驗配置
論文的實驗配置如圖1所示,μC是目標微控制器,GIAnT用于生成時鐘毛刺,Raspberry Pi用于給微控制器的bootloader發送指令,以及給GIAnT配置電壓毛刺參數。右圖為毛刺信號示意圖,其中VF為毛刺電壓,T為毛刺偏移,W為毛刺寬度。

圖1 實驗配置圖,左圖為實驗裝置,右圖為毛刺信號,其中VF為毛刺電壓,T為毛刺偏移,W為毛刺寬度。
實驗過程
接下來論文依次對三家廠商的四種型號的微控制器的bootloader進行了分析與實驗。
NXP LPC1xxx bootloader
恩智浦的LPC1系列bootloader根據CRP防護等級的不同存在不同級別的防護。其中論文作者通過反匯編bootloader的二進制代碼并分析后發現,LPC1系列芯片的bootloader在CRP1防護等級下存在軟件漏洞,在向內存寫入數據時不檢查寫入地址是否在棧內,導致攻擊者可以通過棧溢出的方式調用在CRP1防護下本應被禁用的”Read Memory”指令,從而繞過CRP1防護讀取RAM中的數據。
圖2是棧溢出的ROP鏈。其中從0x10001f54地址開始,棧內11個字的數據因為棧溢出攻擊而被覆寫,0x10001f54為”Write memory” 指令的返回地址,被修改成了0x1fff0cfb,剛好就是”Read memory” 指令中檢查CRP防護級別后的第一條語句。

圖2 ROP鏈,0x10001f54為“Write memory”返回地址,0x1fff0cfa為“Read memory”指令里檢查CRP防護級別后的第一條語句,藍色的FC020000為“Read memory”指令讀取的地址,綠色的0x1fff0e80為執行完一個pop語句后程序回歸正常運行狀態。
STM8 bootloader
本節論文分析了STM8L和STM8A兩個型號芯片的bootloader,圖3是將bootloader二進制代碼反匯編后得到的程序流圖。以STM8L芯片為例,為了使程序進入_SERIAL_BL狀態,即串行調試狀態,需要注入兩個電壓毛刺,第一個毛刺跳過chk_empty或者chk_bl函數,即檢查芯片是否為空或者檢查“Bootloader Enable”的值;第二個毛刺跳過chk_crp,即檢查芯片是否有crp防護。

圖3 STM8L和STM8A bootloader程序流圖,第一個電壓毛刺使程序從chk_empty或者chk_bl狀態進入chk_crp狀態,第二個電壓毛刺使程序從chk_crp狀態進入_SERIAL_BL狀態。
直接進行電壓毛刺的故障攻擊是困難的,因為在兩個電壓毛刺都成功注入前,芯片不會有任何反應,這導致我們無法根據芯片的反應來調整電壓毛刺的參數,使得毛刺參數的搜索空間變得非常龐大。為了減小毛刺參數的搜索空間,提高毛刺的注入效率,論文將影響程序執行的關鍵代碼段(在本小節就是指chk_empty、chk_bl和chk_crp)讀入到用戶程序中,然后對這些代碼段單獨進行毛刺注入的測試,極大地減少了電壓毛刺注入的參數選擇范圍,提高了故障攻擊調參的速度,最終實現了雙電壓毛刺的故障攻擊。

圖4 雙電壓毛刺故障攻擊,第一個毛刺位置為檢查芯片是否為空或者BL值,第二個毛刺位置為檢查CRP值。
Renesas 78K0 bootloader
本節對瑞薩的78K0芯片的bootloader進行了分析,不同于STM系列芯片,芯片啟動后會立刻檢查防護情況,除非防護被無效否則不會繼續運行。78K0芯片在啟用CRP防護機制后,只允許特定安全的指令可以運行,即verify和checksum。本節通過電壓毛刺修改兩條指令執行的最小字節數,從256字節減少至4字節,從而破解出了內存信息。
同樣為了減少電壓毛刺的參數組合,本節使用了symbolic execution(符號執行)的方法,將特定指令的輸入參數分成了若干等價類,從而提高了注入電壓毛刺的效率。
如圖5所示,將執行路徑相同的輸入歸類為一個等價類,可以極大地提高電壓毛刺偏移選擇的速度。

圖5 get_block_no模塊輸入參數等價類示意,圖中兩條路線對應等價類0x1004c和0x5ff。
如圖6所示,論文將checksum函數的輸入分成了9個等價類,由于執行路徑不同,9個等價類需要設置不同的毛刺偏移。由此,大大減小了電壓毛刺的參數的搜索空間。

圖6 checksum函數第一個輸入參數的9個等價類。
論文結論
攻擊的目的是為了防御,本文攻擊的設備都是商用產品,因此論文在結尾根據實驗過程中的發現,提出了10條在設計微控制器時應該加以防護的漏洞,如下所示:
①在受保護狀態下的部分RAM寫權限:有多級保護機制的芯片往往允許對芯片內存的有限debug。沒有MMU的芯片應該保證bootloader的內存空間與用戶可以存取的內存空間是分開的。
②內存或寄存器的部分泄露:在CRP防護被啟用時,有些芯片仍然提供對內存或者寄存器的讀取權限。利用這一漏洞,Obermaier等人引入了“cold boot stepping”,基于SRAM快照重建了程序的控制流。此外,通過單步加載指令和修改CPU寄存器,Brosch恢復出了藍牙芯片的固件信息。
③部分flash覆寫:有一個sector的寫權限本質上給予了攻擊者對整個芯片的讀權限。許多系統可以通過覆寫一個flash sector,使其輸出整個芯片內存而被攻破。
④不完全的或非原子的芯片擦除:許多芯片上CRP可以通過整張芯片的擦除而被無效。但是,一些情況下芯片擦除不會完全清除芯片的內部狀態,導致攻擊者可以恢復存儲在未擦除空間的密碼算法密鑰。為了避免這一情況,芯片擦除過程應該是不可中斷的和原子的,也就是說無效CRP的代碼應該在擦除程序的最后執行。
⑤非固定時間代碼:在受密碼保護的bootloader上的時間泄露,例如Renesas M16C或TI MSP430,允許攻擊者逐字節的恢復密碼并且獲得整片flash內存的讀寫權限。
⑥默認無防護:如果只有特定的值才啟用讀保護,那么相比只有特定的值才無效讀保護而言,要更容易被攻擊,例如LPC1343。
⑦對讀保護沒有冗余檢查:在沒有針對故障注入的硬件防護的芯片上,跳過一個檢查的成功率往往是很高的,但是如果有冗余檢查,就會顯著地降低攻擊的成功率。
⑧大量的防護等級:這可能會使得開發人員搞不清每一種防護等級對應哪種防護。開發者們使用的廠商提供的IDE往往會隱藏CRP細節,使得用戶不清楚他們使用的CRP防護等級。
⑨分離的On-Chip Debug(OCD)和CRP機制:在許多芯片如Renesas V850,78K0R和78K0,或者TI MSP430上,讀保護機制和OCD權限是不相關的,使得用戶需要通過軟件或者保險絲來保護。由于這一模糊性,開發人員可能會注意不到某一種對芯片內存的讀取方式,最終使得其它所有防護都無效化。
⑩復雜的bootloader邏輯:每一種bootloader的通訊協議都會增加攻擊者可以攻擊的面的寬度,導致引入更多的軟件風險。例如,特定LPC芯片的USB存儲仿真,其中包含一個FAT文件系統,可能會引出更多的問題,反觀STM8芯片,一旦CRP被啟用,所有bootloader指令都會被禁用。此外,一些Renesas芯片在一個bootloader里支持三種通訊接口,UART,single-wire UART和SPI,這極大地增加了攻擊面寬度。
參考文獻
[1]Jan Van den Herrewegen, David F. Oswald, Flavio D. Garcia, Qais Temeiza:Fill your Boots: Enhanced Embedded Bootloader Exploits via Fault Injection and Binary Analysis. IACR Trans. Cryptogr. Hardw. Embed. Syst. 2021(1): 56-81 (2021).

