安全 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 中传递有效的非零值来首次调用 MemoryOverwriteRequestControlLock
的 SetVariable 时,MemoryOverwriteRequestControlLock
和 MemoryOverwriteRequestControl
的访问模式将更改为只读,指示它们已锁定。
修订版 1 实现仅接受 MemoryOverwriteRequestControlLock
的单字节 0x00 或 0x01。
修订版 2 还接受表示共享密钥的 8 字节值。 如果在 SetVariable 中指定了任何其他值,则调用会以 EFI_INVALID_PARAMETER 状态失败。 若要生成该密钥,请使用高质量的熵资源,例如受信任的平台模块或硬件随机数生成器。
设置密钥后,调用方和固件应将此密钥的副本保存在受机密保护的位置,例如 IA32/X64 上的 SMRAM 或具有受保护存储的服务处理器。
获取系统状态
在修订版 2 中,锁定 MemoryOverwriteRequestControlLock
和 MemoryOverwriteRequestControl
变量后,系统会首先使用常量时间算法依据已注册密钥检查 SetVariable(对于这些变量)的调用。 如果两个密钥都存在且匹配,这些变量将转换回解锁状态。 首次尝试后或如果未注册任何密钥,后续尝试设置此变量会以 EFI_ACCESS_DENIED 失败,以防止暴力攻击。 在这种情况下,只能通过系统重启应是解锁这些变量。
操作系统会通过调用 GetVariable 来检测是否存在 MemoryOverwriteRequestControlLock
及其状态。 然后,系统可以通过将 MemoryOverwriteRequestControlLock
值设置为 0x1 来锁定 MemoryOverwriteRequestControl
的当前值。 或者,它可以指定一个密钥,以便日后在从内存中安全清除机密数据后启用解锁。
调用 MemoryOverwriteRequestControlLock
的 GetVariable 会返回 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 实现流
这些流程图显示了实现的预期行为:
初始化
SetVariable 流
SetVariable 的解锁状态流
SetVariable 的锁定状态流
GetVariable 的流
另请参阅
适用于 SoC 平台上的所有 Windows 版本的 UEFI 要求
电脑客户端工作组平台重置攻击缓解措施规范,版本 1.10(PDF 下载)