安全 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 initialization.

SetVariable 流

morlock programming flow.

SetVariable 的解锁状态流

morlock unlocked flow.

SetVariable 的锁定状态流

morlock locked flow.

GetVariable 的流

morlock getvariable.

另请参阅

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

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

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

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

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

UEFI 规范