演示 CVE-2022-34302,一种通过 New Horizon Datasys 签名的引导加载程序实现的 Secure Boot 绕过,该引导加载程序内置的自定义 PE/COFF 加载器可执行未签名的 UEFI 应用程序。
New Horizon Datasys Reboot Restore 引导加载程序 - 自带易受攻击的 UEFI 应用程序 (BYOVUA) - 通过签名的引导加载程序绕过安全启动,该引导加载程序内置自定义 PE/COFF 加载器,可加载未签名的 UEFI 应用程序。
本仓库演示了 BYOVUA(自带易受攻击的 UEFI 应用程序) 技术,通过利用 CVE-2022-34302 实现,这是 New Horizon Datasys 引导加载程序中的一个安全启动绕过漏洞。
与基于 UEFI Shell 的漏洞(CVE-2022-34301 和 CVE-2022-34303)不同,此引导加载程序不暴露 UEFI Shell。相反,shdloader.efi 实现了自己的自定义 PE/COFF 加载器,该加载器加载第二阶段二进制文件(shdmgr.ef_),不使用固件的 LoadImage() 函数,也不执行任何签名验证。攻击者只需将 shdmgr.ef_ 替换为任何兼容的 UEFI 应用程序,即可在启用安全启动的情况下实现任意代码执行。
这是“一个引导加载程序加载所有内容”研究中披露的三个漏洞中最危险的一个。正如 Eclypsium 所指出的:该绕过是内置的、完全静默的,并且在屏幕上没有任何视觉指示——即使在有显示器的系统上也不可见,在服务器或工业设备等无头系统上更是无法检测。
BYOVUA 是内核级使用的 BYOVD(自带易受攻击的驱动程序)技术在 UEFI 层面的等价物。攻击者不是携带一个存在漏洞的已签名内核驱动程序,而是携带一个已签名的 UEFI 应用程序,该应用程序包含能够破坏安全启动的功能。
由于 shdloader.efi 使用 Microsoft 受信任的证书签名,安全启动会毫无疑问地接受它,使其在任何将此证书包含在其安全启动数据库(db)中的系统上都被信任——这几乎是过去十年中出货的每一台支持 UEFI 的 PC。一旦运行,其内置的自定义 PE 加载器就为攻击者提供了在操作系统加载之前加载并执行任意未签名代码的能力,而且是在现代安全控制(ASLR、DEP、内核保护)根本不存在的环境中。
shdloader.efi 是 New Horizon Datasys 系统还原和恢复产品(Reboot Restore Rx、RollBack Rx)中分发的 UEFI 引导加载程序。它在合法启动链中的角色是加载一个预操作系统管理组件(shdmgr.ef_),该组件在操作系统启动之前处理快照和还原操作。
| 属性 | 值 |
|---|---|
| 文件 | shdloader.efi = EFI/Boot/bootx64.efi |
| 厂商 | New Horizon Datasys Inc |
| 产品 | Reboot Restore Rx / RollBack Rx |
| CVE | CVE-2022-34302 |
| 签名 | Microsoft Windows UEFI Driver Publisher → Microsoft Corporation UEFI CA 2011 |
| 发现者 | Eclypsium(Mickey Shkatov、Jesse Michael)- 2022 年 8 月 |
| 演讲 | DEF CON 30 - "One Bootloader to Load Them All" |
| 撤销 | 通过 Microsoft KB5012170 添加到 DBX(2022 年 8 月) |
该漏洞是引导加载程序架构中的一个设计缺陷。shdloader.efi 没有使用固件的 LoadImage() 和 StartImage() 启动服务——这些服务会强制执行安全启动签名验证——而是实现了自己的自定义 PE/COFF 加载器,直接从原始磁盘字节读取、重定位并执行 shdmgr.ef_,完全绕过了固件的安全检查。
核心问题在于:一个被安全启动信任的已签名二进制文件包含了自己的映像加载器,而该加载器不验证签名。固件将 shdloader.efi 验证为已签名,但一旦它运行起来,它就在没有任何验证的情况下加载 shdmgr.ef_。将 shdmgr.ef_ 替换为任意 UEFI 应用程序,就会导致该应用程序在拥有完整硬件访问权限的情况下运行,而安全启动却报告为已启用。
这与 CVE-2022-34301 和 CVE-2022-34303 有本质区别,后者需要攻击者与 UEFI Shell 交互并手动破坏 gSecurity2 以禁用验证。而在这里,绕过是自动且静默的——无需用户交互,没有可见输出,没有 shell 提示符。
已签名的 shdloader.efi 包含了自己的 PE/COFF 映像加载器实现。它没有调用固件的 LoadImage() 启动服务(该服务会调用安全架构协议并根据安全启动数据库验证映像的签名),而是:
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL 打开 \EFI\Boot\shdmgr.ef_.reloc 节并应用基址重定位在此过程中的任何时刻,加载器都不会验证映像的 Authenticode 签名、检查安全启动数据库(db/dbx)或调用 EFI_SECURITY2_ARCH_PROTOCOL。映像完全基于其 PE/COFF 结构有效性进行加载。```c
// Pseudocode of what shdloader.efi does internally
//
// NOTE: This is a simplified representation. The actual
// implementation was derived from reverse engineering.
EFI_STATUS LoadShdmgr(VOID) { // Step 1: Open the file File = OpenFile(L"\EFI\Boot\shdmgr.ef_");
// Step 2: Read raw bytes (no signature check)
ReadFile(File, &Buffer, &Size);
// Step 3: Parse PE/COFF headers
DosHeader = (EFI_IMAGE_DOS_HEADER *)Buffer;
PeHeader = (EFI_IMAGE_NT_HEADERS *)(Buffer + DosHeader->e_lfanew);
// Step 4: Allocate memory and copy sections
ImageBase = AllocatePages(...);
CopySections(ImageBase, Buffer, PeHeader);
// Step 5: Apply base relocations from .reloc
Delta = ImageBase - PeHeader->OptionalHeader.ImageBase;
ApplyRelocations(ImageBase, PeHeader, Delta);
// Step 6: Jump to entry point
// NO SIGNATURE VERIFICATION ANYWHERE
EntryPoint = ImageBase + PeHeader->OptionalHeader.AddressOfEntryPoint;
((EFI_IMAGE_ENTRY_POINT)EntryPoint)(ImageHandle, SystemTable);
}
---
<div id='LoadImageVsCustomLoader'/>
### ***LoadImage 与自定义加载器***
固件的 `LoadImage()` 与自定义加载器之间的差异,正是关键的安全缺口:```
┌─────────────────────────────────────────────────────────────────────────┐
│ Firmware LoadImage() - How legitimate boot chains work │
│ │
│ bootx64.efi ──> LoadImage("shdmgr.ef_") │
│ │ │
│ ├── Parse PE/COFF headers │
│ ├── Verify Authenticode signature │
│ ├── Check signature against db (allowed) │
│ ├── Check hash against dbx (revoked) │
│ ├── Call gSecurity2->FileAuthenticationState() │
│ │ │ │
│ │ ├── Signature valid? ── YES ──> Load image │
│ │ └── Signature invalid? ── NO ──> REJECT │
│ └── StartImage() │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ Custom PE Loader - What shdloader.efi does │
│ │
│ shdloader.efi ──> OpenFile("shdmgr.ef_") │
│ │ │
│ ├── ReadFile() into buffer │
│ ├── Parse PE/COFF headers │
│ ├── Allocate memory │
│ ├── Copy sections │
│ ├── Apply .reloc relocations │
│ ├── *** NO SIGNATURE CHECK *** │
│ └── Jump to EntryPoint │
│ │
│ Result: ANY valid PE/COFF EFI application runs, signed or not │
└─────────────────────────────────────────────────────────────────────────┘
自定义 PE 加载器是一个简化实现,期望特定的 PE/COFF 布局。不符合规范的二进制文件将被拒绝并报错:``` Reloc table overflows binary Relocation failed Invalid entry point
缺少其中任何一项的二进制文件都会被自定义加载器拒绝。