對聯(lián)想 ThinkPad SMM 管理 UEFI 密碼的逆向分析

UEFI是描述用于開發(fā)固件(尤其是BIOS)的標(biāo)準(zhǔn)接口集的規(guī)范。該固件是啟動時在CPU上執(zhí)行的第一批操作之一。負(fù)責(zé)初始化硬件并進(jìn)行設(shè)置,以便操作系統(tǒng)可以啟動。該固件存儲在計算機(jī)中存在的SPI閃存中,攻擊者破壞此固件的主要優(yōu)點是,可以在硬盤以外的其他地方實現(xiàn)持久性。

在之前的一篇文章中,我談到了Lenovo ThinkPad的系統(tǒng)管理模式(SMM)代碼中的漏洞。那時,我很好奇如何處理UEFI密碼(尤其是用于保護(hù)BIOS接口的密碼)。密碼的處理特定于每個構(gòu)造函數(shù),這意味著此處說明的代碼特定于Lenovo,更確切地說特定于某些ThinkPad(這對于三個不同的ThinkPad版本通常是通用的,因此,大多數(shù)內(nèi)容可能保持不變)。

在此文章中,我先回顧一下之前的漏洞,然后解釋我如何查看Lenovo密碼。首先,我們將逆向啟動方式以及固件中的各種密碼,然后再更深入地研究其中兩個:開機(jī)密碼和BIOS密碼。在這些密碼的管理中,尚未發(fā)現(xiàn)任何漏洞,但事不宜遲,讓我們開始吧。

0x01 SMM和UEFI

UEFI是描述用于開發(fā)固件(尤其是BIOS)的標(biāo)準(zhǔn)接口集的規(guī)范。該固件是啟動時在CPU上執(zhí)行的第一批操作之一。負(fù)責(zé)初始化硬件并進(jìn)行設(shè)置,以便操作系統(tǒng)可以啟動。該固件存儲在計算機(jī)中存在的SPI閃存中,攻擊者破壞此固件的主要優(yōu)點是,可以在硬盤以外的其他地方實現(xiàn)持久性。

系統(tǒng)管理模式(SMM)是Intel CPU模式。它通常被稱為ring -2, 因為它比內(nèi)核或系統(tǒng)管理程序更具特權(quán)。SMM擁有自己的稱為SMRAM的存儲空間,該存儲空間受到保護(hù),無法通過其他模式訪問。SMM可以被視為與ARM上的Trust Zone相似的“安全世界”。但是,其最初目標(biāo)不是提供安全函數(shù),而是處理計算機(jī)的特定要求,例如高級電源管理(APM,已由ACPI代替)。如今,它還用于保護(hù)對包含UEFI代碼的SPI Flash的寫訪問。

《英特爾手冊》中的“處理器工作模式之間的轉(zhuǎn)換”

從前面的示意圖中可以看出,可以從任何“正常”模式訪問SMM。SMM還支持16位,32位和64位,這使其成為所有其他模式的副本。當(dāng)觸發(fā)系統(tǒng)管理中斷(SMI)時,將在正常模式和SMM之間進(jìn)行轉(zhuǎn)換。發(fā)生這種情況時,處理器將切換到SMM:它將首先將CPU的當(dāng)前狀態(tài)保存到一個稱為“保存狀態(tài)”的存儲區(qū)(以后才能恢復(fù)該狀態(tài)是必需的),然后更改包括指令指針的上下文用于執(zhí)行SMRAM中的代碼。

基本SMRAM映射,SMBASE可能不會隨SMRAM的開始而被使用。

SMRAM是由UEFI固件保留以供SMM使用的物理RAM區(qū)域。SMRR可以保護(hù)它免受“常規(guī)”訪問,也可以保護(hù)它不受DMA訪問等等。SMBASE是一個必須在此范圍內(nèi)的地址,將用于確定在切換到SMM時必須將保存狀態(tài)存儲在哪里以及將指令指針設(shè)置在哪個位置。每個內(nèi)核只有一個SMBASE(為避免兩個內(nèi)核同時切換而彼此重寫保存的狀態(tài)),對SMRAM內(nèi)部的位置沒有限制。

存在幾種SMI,但特別是一種軟件SMI(SWSMI)對于攻擊者來說很有趣。在ioport上寫入0xb2值時將觸發(fā)SWSMI 。進(jìn)行切換后,代碼通常將搜索與ioport上寫入的值相對應(yīng)的SWSMI處理程序,這些處理程序通常以64位編寫。

最后,由UEFI固件初始化在SMM中運行的代碼(SMRAM中的設(shè)置)。特別是,通常在UEFI引導(dǎo)的驅(qū)動程序執(zhí)行環(huán)境(DXE)階段設(shè)置SWSMI處理程序。DXE階段由數(shù)百個驅(qū)動程序組成,這些驅(qū)動程序用于從硬件初始化到網(wǎng)絡(luò)堆棧實施的所有過程。

這些驅(qū)動程序提供了位于正常模式下的一組服務(wù)(尤其是 EFI_BOOT_SERVICES和EFI_RUNTIME_SERVICES),這些服務(wù)提供了一組基本函數(shù),例如分配和對非易失性變量的訪問。

該EFI_BOOT_SERVICES還允許注冊和訪問協(xié)議。協(xié)議允許驅(qū)動程序共享函數(shù),并由GUID標(biāo)識。實際上,由于在UEFI引導(dǎo)過程中所有內(nèi)存訪問均在物理內(nèi)存中進(jìn)行,因此協(xié)議僅將GUID與指針相關(guān)聯(lián)。這些協(xié)議中有一些是公開的并有文檔記錄(某些在UEFI規(guī)范中,有些在edk2中),而另一些則針對每個構(gòu)造函數(shù)。在DXE階段結(jié)束時,固件將鎖定SMRAM,阻止對其進(jìn)行訪問,然后嘗試啟動引導(dǎo)加載程序以過渡到OS。

0x02 漏洞分析

逆向分析

當(dāng)我開始逆向固件時,我首先確定驅(qū)動程序使用了哪個協(xié)議來注冊SWSMI處理程序。在這種情況下,他們使用EFI_SMM_SW_DISPATCH2_PROTOCOLedk2(MdePkg/Include/Protocol/SmmSwDispatch2.h)中定義的文檔并進(jìn)行了記錄。一旦確定了該協(xié)議,我便對使用該協(xié)議的所有驅(qū)動程序進(jìn)行了簡單的二進(jìn)制搜索,然后開始逆向。

其中一個驅(qū)動程序被命名為SmmOEMInt15,它是一個非常小的驅(qū)動程序,僅具有21個函數(shù),其中包括一個注冊SWSMI的函數(shù):

上面的代碼片段執(zhí)行以下操作:

1.使用()包含unk_protocolGUID ff052503-1af9-4aeb-83c4-c2d4ceb10ca3(UnkProtocolGuid) 檢索未記錄的協(xié)議(), 其中包含一些SMM服務(wù)。

2.使用新的未知GUID eee19e05-079a-4d17-8f46-cf811260db26(&swsmi_oemint15_guid)調(diào)用第一個函數(shù), 并將其用于檢索數(shù)字(swsmi_number)。

3.在swsmi_number在前面的步驟中檢索隨后處于后來為了注冊SWSMI處理程序使用的上下文的設(shè)置,這是0xb2必須的IOPORT被寫入的值。

4.最后,EFI_SMM_SW_DISPATCH2_PROTOCOL用于將函數(shù)注冊swsmi_handler_unk_func為SWSMI處理程序。

此代碼的第一個問題是使用未知協(xié)議來獲取SWSMI編號。幾個(但不是全部)注冊SWSMI的驅(qū)動程序使用了該協(xié)議,因此在執(zhí)行任何測試之前必須將其逆向。

SystemSwSmiAllocatorSmm

通過搜索未公開協(xié)議(ff052503-1af9-4aeb-83c4-c2d4ceb10ca3)的GUID,很容易找到實現(xiàn)該協(xié)議的驅(qū)動程序:SystemSwSmiAllocatorSmm。該驅(qū)動程序也非常簡單,函數(shù)更少。

該驅(qū)動程序的第一步是在正常情況下分配多個緩沖區(qū),其中一個特別有趣,因為它已在GUID中注冊為 配置表7E791691-5752-4392-B888-EFF9C74F5D77。所有驅(qū)動程序都可以訪問配置表,并將配置表關(guān)聯(lián)到GUID,它們通常用于將數(shù)據(jù)從一個驅(qū)動程序傳遞到另一驅(qū)動程序,而協(xié)議則用于傳遞函數(shù),實際上它們都將GUID與指針關(guān)聯(lián)。

一旦完成了這些初始步驟和一些初始化,驅(qū)動程序就會注冊我們感興趣的協(xié)議,我SystemSwsmiAllocatorProtocol 從驅(qū)動程序的名稱中對其進(jìn)行命名。該協(xié)議包含3個函數(shù): get_swsmi_num_and_add2list,get_swsmi_num_from_guid和 add_swsmi_to_list_no_check。

基本上,此驅(qū)動程序允許將SWSMI編號與GUID相關(guān)聯(lián)??梢砸篁?qū)動程序找到下一個可用的SWSMI編號(使用第一個函數(shù))或提供它(使用第三個函數(shù))。第二個函數(shù)僅允許從GUID獲取SWSMI編號。

這些關(guān)聯(lián)存儲在通常情況下的鏈表中,該鏈表由開始時注冊的配置表引用。這允許SMM外部的應(yīng)用程序獲得其希望使用的函數(shù)的正確SWSMI號。這樣做可能是為了避免在其他驅(qū)動程序與其他注冊SWSMI處理程序的組件之間發(fā)生SWSMI號沖突。

利用所有這些信息,可以很容易地動態(tài)檢索SWSMI號。使用UEFI Shell中的chipsec 4,我可以匹配SWSMI號和GUID:

1.ct_swsmi_allocator從GUID 檢索配置表7E791691-5752-4392-B888-EFF9C74F5D77。

2.ct_swsmi_allocator + 0x38是雙鏈表頂部的指針(這是一個保護(hù),在此元素后面沒有實際數(shù)據(jù))??梢栽诖肆斜砩线M(jìn)行迭代,直到再次達(dá)到頭部為止。

3.對于elt列表的每個元素,都有一些有趣的數(shù)據(jù):

· 在elt-0x8是一種magic 0x4E415353。

· SWSMI號在elt+0x10qword上。

· GUID位于elt+0x18。

一旦檢索了GUID和SWSMI編號之間的相關(guān)性,就可以觸發(fā)SWSMI處理程序的代碼。

漏洞分析

SWSMI處理程序的第一個操作SmmOEMInt15是RSI從保存的狀態(tài)中檢索寄存器的值。這是通過使用EFI_MM_CPU_PROTOCOL(以前稱為EFI_SMM_CPU_PROTOCOL)完成的, 該文件也已記錄在案,并且是edk2(MdePkg/Include/Protocol/MmCpu.h)的一部分。該協(xié)議將在保存狀態(tài)下搜索CPU保存的值以獲取寄存器并返回。對于SWSMI處理程序而言,這是一個非常有趣的開始,因為此值是實際的用戶輸入。

甚至更有趣的是,此值用作結(jié)構(gòu)上的指針,并且此結(jié)構(gòu)的前兩個字節(jié)用作調(diào)用不同處理程序的開關(guān)的枚舉。我開始快速逆向處理程序,但是由于查看處理程序時發(fā)現(xiàn)了一段非常有趣的代碼,所以從未真正逆向完成0x3E00。

該處理程序要做的第一件事是從結(jié)構(gòu)中的兩個字段計算值,并在controlled調(diào)用內(nèi)部函數(shù)之前將其設(shè)置為全局變量():

該handler_internal_3E00函數(shù)本身以兩個非常有趣的基本塊開始:

handler_internal_3E00函數(shù)開始

要做的第一件事是檢查at的值*(controlled+2)是否為0,如果是這種情況,它將經(jīng)過一些奇怪的事情(這的確是0xFFFEFFFE在該地址的寫操作,0x4因為我們在物理內(nèi)存中沒有任何保護(hù))這不會造成崩潰,請調(diào)用該 EFI_BOOT_SERVICES.LocateHandleBuffer函數(shù)。

從SMM調(diào)用此函數(shù)的問題在于,EFI_BOOT_SERVICES 是位于正常環(huán)境中的服務(wù)表。攻擊者可以簡單地更改EFI_BOOT_SERVICES表中的地址并獲得任意調(diào)用。這種類型的漏洞通常稱為SMRAM調(diào)用,它們基本上等效于從內(nèi)核調(diào)用用戶級代碼。

0x03 漏洞利用

大約2017年至2018年,SMRAM的調(diào)用非常容易利用:在觸發(fā)SWSMI之前足以更改代碼(在這種情況下為函數(shù)指針)。但是,SMM_CODE_CHK_EN從那時起,緩解措施已開始普遍使用,并且確實已在我的Lenovo P51s上激活。

SMM_CODE_CHK_EN是SMM的類似于SMEP的函數(shù):如果在SMM中執(zhí)行了SMRAM外部代碼(由SMRR定義),則計算機(jī)基本上只會崩潰。實際上,SMM_CODE_CHK_EN是在引導(dǎo)過程中由固件初始化的MSR。它可以被鎖定,一旦被鎖定,就不能被禁用。由于它是一種類似于SMEP的函數(shù),因此通常的內(nèi)核繞過將起作用,但是使用它們有一些缺點:

· 固件不如內(nèi)核標(biāo)準(zhǔn):技巧可能無法移植,

· 從正常的世界來看,SMM是一個很大的黑盒,并且數(shù)據(jù)通信受到限制,

· 沒有ASLR,但地址將取決于計算機(jī)和固件版本。

由于所有這些原因,利用漏洞可能無法在另一個易受攻擊的固件上正常工作。

此時,如果我們嘗試0x3E00使用SMRAM調(diào)用觸發(fā)處理程序的代碼,則會發(fā)生以下情況:

觸發(fā)標(biāo)注

1.我們用正確的編號,RSI和內(nèi)存中的正確值觸發(fā)SWSMI,以到達(dá)標(biāo)注。

2.CPU會將當(dāng)前狀態(tài)保存在SMRAM中的某個位置。

3.將執(zhí)行一些代碼(包括切換到64位),并將調(diào)用我們的SWSMI處理程序。

4.該0x3E00處理器將搜索EFI_BOOT_SERVICES.LocateHandleBuffer 函數(shù)指針在內(nèi)存中。

5.并調(diào)用該函數(shù)。

6.然后它將崩潰。由于SMM_CODE_CHK_EN激活了正常世界中的代碼調(diào)用,因此將永遠(yuǎn)不會執(zhí)行,因此未經(jīng)任何修改的原始代碼甚至無法工作。

現(xiàn)在我們知道了,目標(biāo)是能夠以穩(wěn)定的方式在SMM中執(zhí)行我們的代碼,并希望能夠輕松地在具有相同漏洞的兩個不同固件之間移植。為此,我使用了我先前在另一篇博客文章中詳細(xì)解釋過的技術(shù): SMM中的Code Check(mate)。

基本思想是受益于保存狀態(tài),該狀態(tài)由CPU在切換到SMM時設(shè)置。保存狀態(tài)始終位于SMBASE + 0xFC00并且包含許多通用寄存器,使我們能夠控制(在最佳情況下)0x80內(nèi)存字節(jié):

由于所有內(nèi)容都使用物理地址,并且未啟用任何內(nèi)存保護(hù),因此保存狀態(tài)的內(nèi)容將是可執(zhí)行的,而0x80字節(jié)數(shù)對于放置Shellcode而言已綽綽有余,這將使我們獲得完全控制權(quán)。

當(dāng)時的想法是:

繞過CodeChk的想法

1.首先,我們改寫的地址LocateHandleBuffer在 EFI_BOOT_SERVICES與在我們的寄存器位于shellcode的地址結(jié)構(gòu)。

2.然后,我們使用存儲在寄存器中的shellcode觸發(fā)SWSMI。我們?nèi)匀槐仨氉袷卣{(diào)用處理程序所必需的所有條件,但這將為我們的Shellcode留出足夠的空間。

3.然后,CPU將把我們的狀態(tài)保存在SMRAM中,為我們映射shellcode。

4.我們的SWSMI處理程序?qū)⒈徽{(diào)用,他自己將調(diào)用該0x3E00處理程序。

5.EFI_BOOT_SERVICES.LocateHandleBuffer將獲取用于的函數(shù)指針,但是將檢索處于保存狀態(tài)的地址。

6.我們的shellcode將被調(diào)用,并且由于保存狀態(tài)位于SMRAM內(nèi)部,SMM_CODE_CHK_EN不會被觸發(fā)。

這個想法非常簡單,它使我們可以在SMRAM內(nèi)映射shellcode,而不必依賴于固件的代碼??杀氖牵嬖谝粋€小問題:我們不知道SMBASE哪個用于計算已保存狀態(tài)的基址。

SMBASE從很長一段時間以來,獲得的價值一直是利用SMM漏洞的經(jīng)典問題。通常,檢索它的主要方法有三種:可以猜測,可以強(qiáng)行使用或讀取IA32_SMBASE包含其值的MSR 。前兩種技術(shù)極有可能使計算機(jī)崩潰,遺憾的是IA32_SMBASE 只能從SMM讀取寄存器,從而造成雞和蛋的問題。因此,我開始尋找一種更好的技術(shù),該技術(shù)將允許SMBASE可靠地獲得硬件控制。

將SMBASE在初始化PiSmmCpuDxeSMM驅(qū)動程序,該驅(qū)動程序是開源和edk2可用。初始化SMBASE第一件事情時,它計算要保留的大小。因為SMBASE每個CPU 需要一個內(nèi)存,因此不足以保留0x10000,但是為了優(yōu)化RAM空間,驅(qū)動程序避免了每個CPU保留那么多內(nèi)存。TileSize在驅(qū)動程序中計算A 來確定應(yīng)移動SMBASE多少,而實際上在驅(qū)動程序中進(jìn)行動態(tài)計算時,總是將其偏移0x2000字節(jié)。現(xiàn)在我們知道了SMBASE相互比較的位置,并且 0x10000 + TileSize * (number_of_cpu - 1)將保留內(nèi)存字節(jié)。

為了保留內(nèi)存,驅(qū)動程序在SmmAllocatePages 函數(shù)上使用包裝器,并且未指定將內(nèi)存映射到的特定地址。默認(rèn)情況下,SmmAllocatePages將首先嘗試查找空閑列表,但沒有結(jié)果將采用最高的可用地址。在啟動時,沒有理由釋放這么大的內(nèi)存,這意味著我們可以放心地忽略freelist。關(guān)于最后一個有趣的一點 SmmAllocatePages是,它也用于映射SMM驅(qū)動程序,并且當(dāng)完成SMBASE分配時,我們知道最后分配的PiSmmCpuDxeSMM驅(qū)動程序是驅(qū)動程序。

內(nèi)存布局如下:

SMBASE周圍的內(nèi)存布局

我們?nèi)匀粵]有SMBASE,但是我們開始對周圍的事物有了一個很好的了解,并且碰巧PiSmmCpuDxeSMM注冊了一個普通的協(xié)議:

在gSmmCpuPrivate->SmmConfiguration位于內(nèi)部 PiSmmCpuDxeSMM驅(qū)動器,由于它與注冊EFI_BOOT_SERVICES,該指針及其相關(guān)聯(lián)的GUID( gEfiSmmConfigurationProtocolGuid)將被保存在正常的環(huán)境中。使用, EFI_BOOT_SERVICES.LocateProtocol我們可以檢索此指針。看起來很奇怪,這實際上是“故意”制作的:普通世界的驅(qū)動程序在引導(dǎo)階段會使用此協(xié)議,而當(dāng)他們確實使用它時,SMRAM尚未鎖定。但是,可以通過在鎖定SMRAM的同時卸載此協(xié)議來避免此類泄漏。由于該驅(qū)動程序是edk2的一部分,因此大多數(shù)固件都將其集成在一起,并且該技術(shù)基本上可以在不同的構(gòu)造函數(shù)之間移植。如果你希望對泄漏進(jìn)行更詳細(xì)的描述,可以在我以前的博客文章中找到。

利用該泄漏,我們可以計算PiSmmCpuDxeSMM (base = leak - off)的基地址,用它來推導(dǎo)SMBASE 地址(base - 0x10000 - tilesize * (numcpu - 1)),然后從該計算中獲取已保存的狀態(tài)地址。我在使用此技術(shù)時遇到的一個問題是cpu(numcpu)的實際數(shù)量與實際情況不符,因此我花了一些時間來弄清該錯誤。實際上,可以使用EfiPiMpServicesProtocol可從正常世界訪問的來獲得用于計算的實際數(shù)字。

至此,我們具備了漏洞利用所需的一切:

全面開發(fā)

首先,我們需要獲取保存狀態(tài)的地址:

1.使用該EFI_BOOT_SERVICES.LocateProtocol函數(shù)檢索EfiSmmConfigurationProtocol。

2.從協(xié)議中我們可以得出PiSmmCpuDxeSMM驅(qū)動程序中的漏洞。

3.它允許計算SMBASE并推斷出我們的shellcode所在的保存狀態(tài)的地址。

然后我們需要觸發(fā)漏洞利用:

1.我們首先用EFI_BOOT_SERVICES.LocateHandleBuffer剛計算出的值重寫函數(shù)的地址。

2.我們使用存儲在寄存器中的shellcode觸發(fā)SWSMI。

3.CPU將把我們的shellcode映射到我們之前計算出的地址。

4.SmmOEMInt15的SWSMI 被調(diào)用,尤其是0x3E00處理程序。

5.嘗試獲取LocateHandleBuffer地址時,它將檢索已映射我們的shellcode的地址。

6.最后,將調(diào)用我們的shellcode,使我們在SMM中執(zhí)行代碼。

0x04 逆向固件密碼

聯(lián)想ThinkPad固件設(shè)置了幾種密碼,最初我感興趣的是一種保護(hù)BIOS設(shè)置界面的密碼。固件中的一些驅(qū)動程序包含對字符串“ passwords”的引用。我開始看的是LenovoSetupSecurityDxe因為該驅(qū)動程序似乎集中了大部分代碼,這些代碼允許使用用戶界面設(shè)置和刪除密碼。

逆向HII

在UEFI中,使用人機(jī)界面基礎(chǔ)架構(gòu)(HII)來與用戶建立接口:一組允許打印并從用戶那里獲取value的接口。特別是EFI_HII_STRING_PROTOCOL 允許使用“ a” StringId(簡單數(shù)字)從“數(shù)據(jù)庫”中檢索字符串。基本上,這意味著在逆向時不可能直接在字符串上使用外部參照。在數(shù)據(jù)庫中找到的字符串很容易識別:它們是UTF-16字符串,后跟一個字節(jié),值0x14。此值指示HII數(shù)據(jù)庫的此元素是字符串。在所有這些字符串之前,都有一個header。header遵循的 EFI_HII_STRING_PACKAGE_HDR結(jié)構(gòu)包含StringInfoOffset指示字符串開頭的字段。

一旦找到header,就可以找到該數(shù)據(jù)庫的初始化。特別是將調(diào)用gEfiHiiDatabaseProtocolInterface->NewPackageList允許使用句柄注冊數(shù)據(jù)庫字符串的函數(shù)。然后,其余代碼將使用此句柄和StringId來檢索字符串,通常是通過調(diào)用gEfiHiiStringProtocolInterface->GetString。

跟蹤字符串使用情況使我們能夠確定代碼的哪一部分用于哪些活動,并且經(jīng)過一些逆向操作之后,可以識別出一些有趣的全局變量。

聯(lián)想密碼協(xié)議

實際上,用于操作和檢查密碼的全局變量是協(xié)議。這些協(xié)議通過以下偽代碼獲?。?/p>

這段代碼不搜索一個協(xié)議,而是搜索其中的幾個協(xié)議,所有協(xié)議都具有相同的GUID LenovoPwdGuid(2846b2a8-77c8-4432-86ec-199f205d37ca)(1)。它正在檢索每個接口的接口(2),并將接口的開頭與硬編碼的GUID(Guid1在這種情況下)進(jìn)行比較(3)。根據(jù)GUID,使用相應(yīng)的全局變量來存儲該接口(4)。這樣設(shè)置了四個不同的全局變量。

這意味著幾個不同的協(xié)議都安裝了相同的GUID,然后通過比較接口結(jié)構(gòu)開始處存在的另一個GUID來區(qū)分這些協(xié)議。這些接口中的每一個都代表不同類型的密碼,并且在該初始GUID之后,提供了用于操縱它們的函數(shù)。

密碼類型

通過搜索用于比較結(jié)構(gòu)的硬編碼GUID,僅發(fā)現(xiàn)了一些二進(jìn)制文件。四個驅(qū)動程序都有一個有趣的名字:LenovoPopManagerDxe, LenovoSvpManagerDxe,LenovoHdpManagerDxe和LenovoSmpManagerDxe。查看調(diào)試字符串,很容易猜出縮寫的含義:

· POP:開機(jī)密碼

· SVP:SuperVisor密碼

· HDP:硬盤密碼

· SMP:系統(tǒng)管理密碼

還有趣的是,SVP和HDP都具有SMM驅(qū)動程序。

通過逆向一個驅(qū)動程序的用法和代碼,很容易理解GUID之后的第一個函數(shù)的用法:

除get_status之外,此接口的所有函數(shù)都執(zhí)行一些操作并更改全局共享位字段以指示結(jié)果狀態(tài):status。該get_status函數(shù)允許檢索該位字段的值,從而確定用戶是否提供了密碼。

一旦理解了這一點,我就可以開始研究所有這些如何工作。SMP和SVP密碼的工作方式幾乎相同,稍后將對其 進(jìn)行詳細(xì)說明。HDP已記錄在博客文章3中,我對此并不感興趣。最后,還有開機(jī)密碼。

0x05 開機(jī)密碼

POP是用戶可以設(shè)置的密碼,每次計算機(jī)啟動時都會詢問。它由LenovoPopManagerDxe驅(qū)動程序處理,該驅(qū)動程序公開了前面描述的接口。

密碼哈希和PCD

為了查看密碼的存儲方式,這兩個函數(shù)set_pwd和 check_pwd是最好的選擇。該函數(shù)set_pwd首先從0xC參數(shù)中給定的指針中獲取字節(jié),然后計算散列密碼。通過使用驅(qū)動程序中73e47354-b0c5-4e00-a714-9d0d5a4fdbfd實現(xiàn)的另一個協(xié)議() 計算LenovoCryptService哈希值。該協(xié)議的第一個函數(shù)允許計算SHA256,并且是用于哈希密碼的函數(shù)。哈希被加鹽,鹽通過平臺配置數(shù)據(jù)庫(PCD)獲取。

PCD是在UEFI PEI和DXE階段之間傳輸并在驅(qū)動程序之間共享的通用存儲系統(tǒng),PCD協(xié)議的實現(xiàn)在edk2中是開源的。PCD允許通過令牌ID定義共享內(nèi)存緩沖區(qū),該令牌ID在固件編譯時自動確定。靜態(tài)存儲由驅(qū)動程序加載(一個用于PEI,一個用于DXE),并存在于固件文件系統(tǒng)(FFS)中??梢酝ㄟ^搜索GUID輕松找到該存儲,PCD_DATA_BASE_SIGNATURE_GUID但通常與驅(qū)動程序位于同一“文件”中。該協(xié)議還提供了動態(tài)存儲,可用于在驅(qū)動程序之間共享數(shù)據(jù)。

如果是鹽,則使用動態(tài)存儲。鹽在最近的固件中具有0x20字節(jié)大小, 而較舊的固件具有較短的大小(0xA)。只需向PCD協(xié)議詢問正確的令牌ID,就可以很容易地從UEFI Shell中檢索鹽。由于令牌ID是在編譯時生成的,因此攻擊者必須能夠自動確定該令牌,或者簡單地逆向該特定固件的驅(qū)動程序以找到該ID。

需要注意的有趣一點是,0x00在釋放該驅(qū)動程序中用于存儲密碼和鹽的所有緩沖區(qū)之前,都要在其中重置。檢索密碼的哈希值不像在啟動后簡單地轉(zhuǎn)儲內(nèi)存那樣簡單?,F(xiàn)在讓我們看一下它的存儲方式。

存儲函數(shù)

密碼通過允許在存儲區(qū)中寫入字節(jié)的函數(shù)存儲,該函數(shù)的代碼幾乎可以自我說明:

一個IOPort用于指示讀取或?qū)懭氲钠屏?,另一個IOPort用于寫入值。讀取的工作方式相同,只不過將out值的write()替換為read(in)。四個IOPorts 0x70 到0x73是已知的,他們用于與實時時鐘(RTC)設(shè)備進(jìn)行交互。該設(shè)備的主要目標(biāo)是允許訪問時間,但它也提供了一些通常稱為CMOS的存儲空間。這些IOPorts已記錄在PCH數(shù)據(jù)表中, 但是osdev Wiki上也有不錯的資源。

關(guān)于RTC設(shè)備的一個有趣的事實是,必須始終打開它的電源,以免丟失存儲中的數(shù)據(jù)。通常,計算機(jī)中裝有一個小的電池(與主要電池不同),以確保該設(shè)備始終處于開機(jī)狀態(tài)。這意味著具有物理訪問權(quán)限的攻擊者只需刪除各種電源訪問權(quán)限,就可以繞過此密碼。聯(lián)想確實意識到了這一點,甚至對此進(jìn)行了 記錄。

快速查看開機(jī)密碼后,我也有興趣查看其他密碼。

0x06 BIOS密碼

我真正感興趣的一個密碼是保護(hù)BIOS配置的密碼,實際上SMP和SVP密碼的工作方式幾乎相同。這兩個驅(qū)動程序公開了前面介紹的密碼界面,并使用相同的存儲。

對于POP來說,了解密碼存儲方式的最簡單方法就是查看set_pwd函數(shù)。它首先使用SHA256像POP一樣對輸入的哈希執(zhí)行計算。有趣的是,此哈希使用與POP相同的鹽,但真正有趣的部分是密碼的存儲方式。

模擬Eeprom

該存儲使用帶有GUID的協(xié)議進(jìn)行, 82b244dc-8503-454b-a96a-d0d2e00bf86a該協(xié)議由驅(qū)動程序注冊EmulatedEepromDxe。憑借其顯式名稱,我們可以推斷出這可能是存儲API,有趣的是,聯(lián)想過去似乎在計算機(jī)中嵌入了eeprom。該協(xié)議注冊了三個函數(shù),但只有前兩個用于密碼管理,這意味著我們可能具有讀取和寫入函數(shù)。第一個函數(shù)同時用于測試和設(shè)置密碼,而第二個函數(shù)僅用于設(shè)置密碼的函數(shù):這似乎很有力地表明第一個函數(shù)允許讀取,而第二個函數(shù)則允許寫作?,F(xiàn)在,真正有趣的問題是EmulatedEepromDxe 驅(qū)動程序?qū)嶋H上在哪里存儲該數(shù)據(jù)?

該協(xié)議的第一個函數(shù)具有以下原型:

EFI_STATUS EmulEeprom_Read(void *this, UINT64 unk_enum, UINT64 index, UINT8 *pRes)

第一個參數(shù)(this)只是協(xié)議接口上的指針,最后一個參數(shù)(pRes)顯然用于檢索讀取的值,另外兩個參數(shù)清楚地指示要使用的存儲空間。在index這個存儲空間的偏移,但unk_enum還不清楚。與NOR或NAND閃存相反,Eeprom在擦除大小上可以具有良好的粒度。但是,由于用于處理小尺寸擦除的電路占用了空間,可用于更多存儲,因此擦除通常是在“存儲體”中重新分組的幾個字節(jié)上進(jìn)行的。實際上,這意味著編程接口實際上與NOR或NAND閃存非常相似。這是大多數(shù)eeprom被更便宜的NOR或NAND閃存取代的原因之一。在我們的情況下unk_enum實際上是在模擬的eeprom中的一個庫號,在代碼中將該庫號翻譯并添加到該index編號中,以計算讀取或?qū)懭霑r的偏移量。

EmulEeprom_Read函數(shù)上提供的值進(jìn)行一些檢查,并調(diào)用另一個函數(shù),perform_read與bank_num,該index和pRes。實際上是執(zhí)行實際讀取的函數(shù)。此函數(shù)調(diào)用在IOPort上讀寫的其他幾個函數(shù)。這是逆向固件的要點,如果未記錄IOPort,固件通常會很痛苦。使用了三個不同的IOPort,第一個是IOPort 0x1808,它僅在in循環(huán)讀取()時使用,后跟pausex86指令。如果那還不是很明顯,那就是計時器,尤其是PM計時器。在Linux上,簡單的dmesg提示會給你很大的提示:ACPI: PM-Timer IO Port: 0x1808。但是,另外兩個IOPort 0x1630和0x1634,并不那么容易理解。

IOPort逆向

這兩個IOPort顯然是用于讀取和寫入數(shù)據(jù)的IOPort,每個都用于讀取(out)和寫入(in)。IOPort 0x1634通常用常量寫入,而不依賴于偏移量,讀取時通常將結(jié)果檢查為位字段。另一方面,IOPort 0x1630既用于寫入先前計算的偏移量,又用于讀取實際結(jié)果。在至少一項函數(shù)中,對此IOPort進(jìn)行讀取并將結(jié)果丟棄。這是與其他硬件設(shè)備連接的典型模式:一個IOPort是“配置” IOPort,用于檢查另一設(shè)備的狀態(tài),指示執(zhí)行的操作的類型,依此類推,在本例中為IOPort 0x1634。第二個IOPort(0x1630)是用于傳輸數(shù)據(jù)(無論是讀取還是寫入)的一種。在IOPort上進(jìn)行讀取可能會對設(shè)備產(chǎn)生副作用,因此在丟棄結(jié)果的同時執(zhí)行了一次讀取。這是與使用IOPort與外部設(shè)備進(jìn)行討論的經(jīng)典模式,基本上與以SPI閃存進(jìn)行討論或與PCI設(shè)備進(jìn)行接口的方式相同。

因此,在這一點上,我們知道這些密碼的哈希值未存儲在SPI閃存中,而是存儲在計算機(jī)中的另一臺設(shè)備上(再次),所以現(xiàn)在的問題是哪個?使用的兩個IOPort是可變的(與固定端口相反,例如用于PCI的IOPort),這意味著這些端口號取決于系統(tǒng)的配置。搜索使用那些IOPort的設(shè)備通常很復(fù)雜,在那種情況下,我首先搜索了PCI設(shè)備聲明的IOPorts(使用lspci),但沒有找到可用信息。下一步是查看在CPU和Platorm Controller Hub(PCH)數(shù)據(jù)表中定義的變量IOPort。列舉了一些時間之后,我終于在低引腳數(shù)(LPC)控制器中找到了這些IOPort的注冊為LPC通用IO范圍1(LGIR1)。

低引腳數(shù)總線用于與計算機(jī)內(nèi)部的多個設(shè)備進(jìn)行通信。特別是,它用于與稱為嵌入式控制器(EC)的設(shè)備進(jìn)行通信,該設(shè)備在PCH數(shù)據(jù)表中被簡稱為“微控制器1”。EC是一種微控制器,以負(fù)責(zé)為筆記本電腦供電而著稱。那時,我還記得閱讀 過Alex Matrosov和Alexandre Gazet 的演講 Breaking Through Another Side,他們在演講中談到了EC及其安全影響?;仡櫵麄兊恼勗挘易⒁獾狡渲羞€引用了這兩個IOPort,因此密碼的哈希存儲在EC中。

EC有自己的固件,因此查看它不是該項目的一部分。但是,我可以做的一件事就是嘗試以與驅(qū)動程序相同的方式讀取密碼。我使用chipsec實現(xiàn)了與EC交互的小腳本 ,但是當(dāng)嘗試讀取密碼的哈希值時,只有空字節(jié)。由于我能夠讀取其他模擬“存儲區(qū)”的內(nèi)容,因此這似乎是一種保護(hù)機(jī)制:固件可能在引導(dǎo)階段完成后鎖定對哈希的訪問。

最后一件事引起了我的興趣:我之前提到,LenovoSvpManagerSmm存在用于SVP密碼的SMM驅(qū)動程序。由于SMM與OS并行運行,因此我對查看SMM如何檢索密碼的哈希值很感興趣。進(jìn)行一些逆向之后,似乎該驅(qū)動程序使用了SMM替代EmulatedEepromDxe驅(qū)動程序:EmulatedEepromSmm。該驅(qū)動程序與的工作方式相同,EmulatedEepromDxe對相同的IOPort執(zhí)行相同的操作。但是,LenovoSvpManagerSmm實際上是在初始化過程中檢索哈希并將其存儲在SMRAM中的緩沖區(qū)中。這似乎表明,如我之前的博文中所述,SMM漏洞 應(yīng)允許檢索這些哈希。

實際上,BIOS固件密碼的哈希值存儲在嵌入式控制器中,并且在引導(dǎo)結(jié)束后似乎已鎖定。攻擊者應(yīng)該能夠利用UEFI或SMM漏洞來獲取攻擊者,但這已經(jīng)是一項更加復(fù)雜的任務(wù)。它們的安全性仍基于EC的安全性,但這將是另一次研究。

0x07 分析總結(jié)

總而言之,這里研究的Lenovo密碼的處理非常好,擁有硬件訪問計算機(jī)權(quán)限的攻擊者應(yīng)該能夠繞過那些密碼,但這并不像我最初預(yù)期的那樣容易。

開機(jī)密碼可以很容易地重置,這是最有問題的事情,但是仍然需要通過硬件訪問(當(dāng)然,或者是固件中的漏洞)。BIOS密碼未存儲在SPI閃存中,而是存儲在EC閃存中,并且引導(dǎo)后似乎已鎖定了讀/寫訪問權(quán)限。這意味著計算機(jī)用戶在不物理打開計算機(jī)的情況下應(yīng)該無法輕松刪除或更改BIOS密碼。

還可以看到一個有趣的趨勢:被認(rèn)為是整個系統(tǒng)信任根的UEFI固件越來越多地被其他固件取代。Lenovo似乎將EC用于其安全性的某些部分,而且管理引擎(ME)和驗證碼模塊(ACM)現(xiàn)在已成為UEFI固件的信任根。在實踐中,這使攻擊者的生活更加困難,但同時也提供了潛在的更廣闊的攻擊面,而改變信任根可能只是在改變問題。

THEEND

最新評論(評論僅代表用戶觀點)

更多
暫無評論