攻擊UEFI運行時服務(wù)和Linux
前言
具有物理訪問權(quán)限的攻擊者能夠在許多具有DMA-直接內(nèi)存訪問的完全修補的計算機上攻擊固件。一旦在UEFI/EFI運行時服務(wù)中執(zhí)行代碼,就可以使用腳本來控制正在運行的Linux系統(tǒng)。
Linux 4.8內(nèi)核完全隨機化了內(nèi)核的物理內(nèi)存地址。如果計算機擁有足夠大的內(nèi)存,內(nèi)核很有可能隨機化到4G以上的地址空間上。這意味著DMA硬件攻擊僅能夠攻擊32位的地址(4GB),如PCILeech,不能直接攻擊Linux內(nèi)核。
由于EFI運行時服務(wù)通常位于4GB以下,因此它們在高內(nèi)存EFI引導(dǎo)系統(tǒng)上提供了一種進入Linux的方法。
什么是EFI運行時服務(wù)?
PC上的UEFI,mac上的EFI是現(xiàn)代化的BIOS。UEFI是統(tǒng)一的可擴展固件接口的縮寫。UEFI負責檢測硬件和配置設(shè)備,以便于將控制移交給正在加載的操作系統(tǒng)。
UEFI的兩個主要組件是引導(dǎo)服務(wù)和運行時服務(wù)。操作系統(tǒng)上很早就調(diào)用ExitBootServices。隨后引導(dǎo)服務(wù)就不再使用。
即使在操作系統(tǒng)加載和運行后,EFI Runtime Services仍然保持運行。它們提供操作系統(tǒng)可以調(diào)用的各種功能。UEFI規(guī)范指定了運行時服務(wù)應(yīng)該向操作系統(tǒng)提供的一組固定的函數(shù),如下所示。
UEFI運行時服務(wù)
最初,運行時服務(wù)功能的位置通過運行時服務(wù)表傳送到操作系統(tǒng)。每個函數(shù)的物理地址是64位/8字節(jié)長,并以小端存儲的形式保存在內(nèi)存中。然而,到目前為止,所有系統(tǒng)的內(nèi)存地址都在32位范圍內(nèi)。物理內(nèi)存地址似乎是完全不變的,即在重新啟動之間不發(fā)生隨機化。
Linux隨后將這些地址映射到虛擬地址空間,并用相應(yīng)的虛擬地址覆蓋表中的初始地址。初始表和被Linux修改后的表的如下所示。
EFI Runtime服務(wù)表,原始在左邊,Linux修改的為右邊。
攻擊
如果我們使用DMA用自己的代碼覆蓋EFI運行時服務(wù)表會發(fā)生什么?或者,如果我們修改EFI運行時服務(wù)表的指針,讓它指向之前插入的攻擊代碼,這又會發(fā)生什么?
當操作系統(tǒng)調(diào)用EFI運行時服務(wù)時,會在目標系統(tǒng)上執(zhí)行代碼-例如當它讀取一個EFI變量。在特殊上下文中獲得執(zhí)行代碼,其中較低的內(nèi)存頁面在虛擬地址與物理地址之間以1:1映射。在正常的虛擬地址中是可以訪問到Linux內(nèi)核的。在ring0/supervisor模式下執(zhí)行。
然而,調(diào)用Linux內(nèi)核中的所有函數(shù)是不可能的。一些函數(shù)會調(diào)用失敗,因為Linux已經(jīng)為運行時服務(wù)設(shè)置了特殊的EFI上下文。針對這種情況,可以在Linux內(nèi)核中patch 一個“隨機”的鉤子函數(shù)。當“正常”內(nèi)核線程命中該鉤子時,被hook的內(nèi)核代碼將會被執(zhí)行.
目標系統(tǒng)運行Ubuntu 16.10,連接著攻擊硬件的PCILeech。右視圖為PCILeech
目標
已經(jīng)在一臺具有8GB內(nèi)存的聯(lián)想T430和一臺具有32G內(nèi)存的 Intel NUC Skull Canyon做過了測試。T430的ExpressCard插槽用于獲得DMA訪問,在NUC上,在“BIOS”中設(shè)置Thunderbolt模式為“Legacy” – 允許通過Thunderbolt3 / USB-C進行DMA訪問。
T430非常簡單,只需插入PCILeech設(shè)備并調(diào)用pcileech.exe kmdload -kmd linux_x64_efi命令。PCILeech將搜索EFI運行時服務(wù)表并hook它。然后要求用戶執(zhí)行某些操作來觸發(fā)已hook過的服務(wù)。
NUC是不同的。PCILeech在找到目標之前如果遇到不可讀的內(nèi)存就會失敗。幸運的是,運行時服務(wù)表的地址是靜態(tài)的,在重新啟動之間不會改變。這適用于所有已測試的系統(tǒng)。找到地址最簡單的方法是通過在同一系統(tǒng)或相似系統(tǒng)上以EFI模式用USB引導(dǎo)啟動Linux。一旦啟動就調(diào)用命令cat /sys/firmware/efi/runtime來查看運行時服務(wù)表的物理地址。一旦知道地址是0x3b294e18,我們可以調(diào)用命令
- pcileech.exe kmdload -kmd linux_x64_efi -min 0x3b294000 -max 0x3b295000
顯示被攻擊的運行時服務(wù)表
備注
如果你也想試試,可以查看Github上的PCILeech。
雖然漏洞總是在運作,但是攻擊并不是100%穩(wěn)定的。有時,搜索運行時服務(wù)表會失敗,有時很難觸發(fā)對運行時服務(wù)表的調(diào)用。有時目標系統(tǒng)也會崩潰。
結(jié)論
在Linux 4.8操作系統(tǒng)上,如Ubuntu 16.10。由于可以利用PCILeech,所以不再是完全安全的。 即使你的筆記本電腦可能沒有一個ExpressCard插槽,但是如果擰開后蓋,它可能會有一個mini-PCIe或M.2插槽。
操作系統(tǒng)將重要的數(shù)據(jù)放在高于32-bit/4GB的地址上并不能阻止DMA攻擊。32位硬件(如PCILeech)會攻擊低于4GB的代碼和數(shù)據(jù)。其他惡意硬件也可能攻擊多達到64位地址空間。
操作系統(tǒng)應(yīng)該啟用VT-d來保護自身和固件(例如運行時服務(wù))免受惡意設(shè)備的攻擊。