安全 MOR 实现

总结

  • MorLock 的行为,修订版 2

上次更新时间

  • 2020 年 8 月

适用于

  • Windows 10

  • 想要支持 Windows 10 的 Credential Guard 功能的 OEM 和 BIOS 供应商。

官方规范

概述

本主题介绍了 MemoryOverwriteRequestControlLock UEFI 变量(修订版 2)的行为和用法。

为了防止高级内存攻击,改进了现有的系统 BIOS 安全缓解 MemoryOverwriteRequestControl,以支持锁定,防止新威胁。 将威胁模型扩展为将主机 OS 内核作为对手,因此在内核特权级别执行的 ACPI 和 UEFI 运行时服务不受信任。 与安全启动实现类似,MorLock 应在特权固件执行上下文中实现,该上下文不能被主机 OS 内核(例如,系统管理模式、TrustZone、BMC 等)篡改。 该接口基于 UEFI 变量服务构建,此类服务在 UEFI 规范版本 2.5(名为“变量服务”的第 7.2 节)中介绍。

此缓解措施(称为 MorLock)必须在所有新系统上实现,不仅仅限于具有受信任平台模块的系统。 修订版 2 添加了解锁这一新功能,以缓解启动性能问题,尤其是在大型内存系统上。

对于用于设置 MOR 位状态的 ACPI _DSM 控制方法,如电脑客户端工作组平台重置攻击缓解措施规范,版本 1.10(PDF 下载)的第 6 节中所述,建议从新式 BIOS 实现中移除此 _DSM 方法。

但是,如果 BIOS 实现此 _DSM 方法,则必须遵循 MorLock 的状态。 如果 MorLock 已锁定,并且没有键,则此 _DSM 方法肯定无法更改 MOR,并返回与“常规失败”对应的值 1。 未定义 ACPI 机制来解锁 MorLock 修订版 2。

请注意,自 Windows 7 以来,Windows 尚未直接调用此 _DSM 方法,并认为它已弃用。 当 Windows 调用作为 MOR 自动检测清理关断的 ACPI _PTS 时,某些 BIOS 会间接调用此 _DSM 方法(如电脑客户端工作组平台重置攻击缓解措施规范,版本 1.10(PDF 下载)的第 2.3 节中所述。

此 MOR 自动检测的 ACPI _PTS 实现缺少足够的安全性,不应使用。

MemoryOverwriteRequestControlLock

包含经过改进的缓解措施的 BIOS 会在早期启动期间创建此 UEFI 变量:

VendorGuid: {BB983CCF-151D-40E1-A07B-4A17BE168292}

名称MemoryOverwriteRequestControlLock

属性:NV+BS+RT

Data 参数中的 GetVariable 值:0x0(已解锁);0x1(无键锁定);0x2(使用键锁定)

Data 参数中的 SetVariable 值:0x0(已解锁);0x1(已锁定)

使用 SetVariable 锁定

在启动设备选择 (BDS) 阶段之前,每次启动时,BIOS 都应将 MemoryOverwriteRequestControlLock 初始化为 0x00 的单字节值(指示已解锁)(DRIVER####、SYSPREP####、BOOT####、*RECOVERY*...) 对于 MemoryOverwriteRequestControlLock(和 MemoryOverwriteRequestControl),BIOS 应防止删除变量,且属性必须固定到 NV+BS+RT。

Data 中传递有效的非零值来首次调用 MemoryOverwriteRequestControlLockSetVariable 时,MemoryOverwriteRequestControlLockMemoryOverwriteRequestControl 的访问模式将更改为只读,指示它们已锁定。

修订版 1 实现仅接受 MemoryOverwriteRequestControlLock 的单字节 0x00 或 0x01。

修订版 2 还接受表示共享密钥的 8 字节值。 如果在 SetVariable 中指定了任何其他值,则调用会以 EFI_INVALID_PARAMETER 状态失败。 若要生成该密钥,请使用高质量的熵资源,例如受信任的平台模块或硬件随机数生成器。

设置密钥后,调用方和固件应将此密钥的副本保存在受机密保护的位置,例如 IA32/X64 上的 SMRAM 或具有受保护存储的服务处理器。

获取系统状态

在修订版 2 中,锁定 MemoryOverwriteRequestControlLockMemoryOverwriteRequestControl 变量后,系统会首先使用常量时间算法依据已注册密钥检查 SetVariable(对于这些变量)的调用。 如果两个密钥都存在且匹配,这些变量将转换回解锁状态。 首次尝试后或如果未注册任何密钥,后续尝试设置此变量会以 EFI_ACCESS_DENIED 失败,以防止暴力攻击。 在这种情况下,只能通过系统重启应是解锁这些变量。

操作系统会通过调用 GetVariable 来检测是否存在 MemoryOverwriteRequestControlLock 及其状态。 然后,系统可以通过将 MemoryOverwriteRequestControlLock 值设置为 0x1 来锁定 MemoryOverwriteRequestControl 的当前值。 或者,它可以指定一个密钥,以便日后在从内存中安全清除机密数据后启用解锁。

调用 MemoryOverwriteRequestControlLockGetVariable 会返回 0x0、0x1 或 0x2,以指示未锁定、无密钥锁定或有密钥锁定状态。

设置 MemoryOverwriteRequestControlLock 不会提交到闪存(只会更改内部锁定状态)。 获取该变量将返回内部状态,而绝不会公开密钥。

操作系统的用法示例:

if (gSecretsInMemory)
{
    char data = 0x11;
    SetVariable(MemoryOverwriteRequestControl, sizeof(data), &data);
}

// check presence
status = GetVariable(MemoryOverwriteRequestControlLock, &value);  

if (SUCCESS(status))
{
    // first attempt to lock and establish a key
    // note both MOR and MorLock are locked if successful

    GetRNG(8, keyPtr);
    status = SetVariable(MemoryOverwriteRequestControlLock, 8, keyPtr);

    if (status != EFI_SUCCESS)
    {
        // fallback to revision 1 behavior
        char data = 0x01;
        status = SetVariable(MemoryOverwriteRequestControlLock, 1, &data);
        if (status != EFI_SUCCESS) { // log error, warn user }
    }
}
else
{
    // warn user about potentially unsafe system
}

// put secrets in memory

// … time passes …

// remove secrets from memory, flush caches

SetVariable(MemoryOverwriteRequestControlLock, 8, keyPtr);

MorLock 实现流

这些流程图显示了实现的预期行为:

初始化

morlock 初始化。

SetVariable 流

morlock 编程流。

SetVariable 的解锁状态流

morlock 解锁流。

SetVariable 的锁定状态流

morlock 锁定流。

GetVariable 的流

morlock getvariable。

另请参阅

适用于 SoC 平台上的所有 Windows 版本的 UEFI 要求

电脑客户端工作组平台重置攻击缓解措施规范,版本 1.10(PDF 下载)

保护 BitLocker 免受冷攻击(和其他威胁)

在 EDKII 中使用 UEFI TPM2 支持的 BIOS 以外的教程

使用 Credential Guard 保护派生的域凭据

UEFI 规范