Compartilhar via


Implementação segura de MOR

Resumo

  • Comportamento de MorLock, revisão 2

Última atualização

  • Agosto de 2020

Aplicável ao

  • Windows 10

  • OEMs e fornecedores de BIOS que desejam dar suporte ao recurso Credential Guard do Windows 10.

Especificações oficiais

Visão geral

Este tópico descreve o comportamento e o uso da MemoryOverwriteRequestControlLock variável UEFI, revisão 2.

Para evitar ataques avançados de memória, a mitigação de segurança do BIOS do sistema existente MemoryOverwriteRequestControl foi aprimorada para oferecer suporte ao bloqueio para se defender contra novas ameaças. O modelo de ameaça é expandido para incluir o kernel do sistema operacional host como um adversário, portanto, os serviços de tempo de execução ACPI e UEFI em execução no nível de privilégio do kernel não são confiáveis. Semelhante às implementações de Inicialização Segura, o MorLock deve ser implementado em um contexto de execução de firmware privilegiado que não pode ser adulterado pelo kernel do sistema operacional host (por exemplo, Modo de Gerenciamento do Sistema, TrustZone, BMC e assim por diante). A interface é construída com base em serviços variáveis UEFI, que são descritos na Especificação UEFI versão 2.5, Seção 7.2 chamada "Serviços Variáveis".

Essa mitigação, chamada MorLock, deve ser implementada em todos os novos sistemas e não apenas limitada a sistemas com Trusted Platform Modules. A revisão 2 adiciona um novo recurso, unlock, para mitigar as preocupações de desempenho de inicialização, especialmente em sistemas de memória grande.

Em relação ao método de controle de _DSM ACPI para definir o estado de bit MOR (conforme descrito na Seção 6 da Especificação de Mitigação de Ataque de Redefinição de Plataforma de Grupo de Trabalho do Cliente do PC, Versão 1.10 (download de PDF)), recomendamos remover esse método _DSM das implementações modernas do BIOS.

No entanto, se um BIOS implementar esse método _DSM, ele deverá respeitar o estado do MorLock. Se o MorLock estiver bloqueado, com ou sem chave, este método _DSM deve falhar ao alterar o MOR e retornar um valor de 1 correspondente a "Falha Geral". Nenhum mecanismo ACPI é definido para desbloquear a revisão 2 do MorLock.

Observe que o Windows não invoca diretamente esse método _DSM desde o Windows 7 e o considera preterido. Alguns BIOS invocam indiretamente esse método _DSM quando Windows invoca _PTS ACPI como uma implementação da Detecção Automática de Desligamento Normal do MOR (conforme descrito na Seção 2.3 da Especificação de Mitigação de Ataque de Redefinição de Plataforma de Grupo de Trabalho do Cliente do PC, Versão 1.10 (download de PDF)).

Essa implementação de _PTS ACPI da Detecção Automática MOR é deficiente em segurança e NÃO deve ser usada.

MemoryOverwriteRequestControlLock

O BIOS que contém a mitigação aprimorada cria esta variável UEFI durante a inicialização inicial:

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

Nome: MemoryOverwriteRequestControlLock

Atributos: NV+BS+RT

Valor GetVariable no parâmetro Dados : 0x0 (desbloqueado); 0x1 (bloqueado sem chave); 0x2 (bloqueado com chave)

Valor de SetVariable no parâmetro Dados : 0x0 (desbloqueado); 0x1 (bloqueado)

Bloqueio com SetVariable

Em cada inicialização, o BIOS deve inicializar MemoryOverwriteRequestControlLock com um valor de byte único de 0x00 (indicando desbloqueado) antes da fase de seleção de dispositivo de inicialização (BDS) (DRIVER####, SYSPREP####, BOOT####, *RECOVERY*, ...). Para MemoryOverwriteRequestControlLock (e MemoryOverwriteRequestControl), o BIOS deve impedir a exclusão da variável e os atributos devem ser fixados em NV+BS+RT.

Quando SetVariable for MemoryOverwriteRequestControlLock é chamado pela primeira vez passando um valor válido diferente de zero em Data, o modo de acesso para ambos MemoryOverwriteRequestControlLock e MemoryOverwriteRequestControl é alterado para somente leitura, indicando que eles estão bloqueados.

As implementações da Revisão 1 aceitam apenas um único byte de 0x00 ou 0x01 para MemoryOverwriteRequestControlLock.

A revisão 2 também aceita um valor de 8 bytes que representa uma chave secreta compartilhada. Se qualquer outro valor for especificado em SetVariable, a chamada falhará com o status EFI_INVALID_PARAMETER. Para gerar essa chave, use uma fonte de entropia de alta qualidade, como o Trusted Platform Module ou o gerador de números aleatórios de hardware.

Depois de definir uma chave, o chamador e o firmware devem salvar cópias dessa chave em um local protegido por confidencialidade, como SMRAM em IA32/X64 ou um processador de serviço com armazenamento protegido.

Obtendo o estado do sistema

Na Revisão 2, quando as variáveis e MemoryOverwriteRequestControl são bloqueadas, as MemoryOverwriteRequestControlLock invocações de SetVariable (para essas variáveis) são verificadas primeiro em relação à chave registrada usando um algoritmo de tempo constante. Se ambas as chaves estiverem presentes e corresponderem, as variáveis farão a transição de volta para um estado desbloqueado. Após essa primeira tentativa ou se nenhuma chave for registrada, as tentativas subsequentes de definir essa variável falharão com EFI_ACCESS_DENIED para evitar ataques de força bruta. Nesse caso, a reinicialização do sistema deve ser a única maneira de desbloquear as variáveis.

O sistema operacional detecta a presença e MemoryOverwriteRequestControlLock seu estado chamando GetVariable. O sistema pode então bloquear o valor atual de MemoryOverwriteRequestControl definindo o MemoryOverwriteRequestControlLock valor como 0x1. Como alternativa, ele pode especificar uma chave para permitir o desbloqueio no futuro depois que os dados secretos forem limpos com segurança da memória.

Chamar GetVariable para MemoryOverwriteRequestControlLock retorna 0x0, 0x1 ou 0x2 para indicar desbloqueado, bloqueado sem chave ou bloqueado com estados de chave.

A configuração MemoryOverwriteRequestControlLock não se compromete com o flash (apenas altera o estado de bloqueio interno). Obter a variável retorna o estado interno e nunca expõe a chave.

Exemplo de uso pelo sistema operacional:

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);

Fluxo de implementação do MorLock

Esses fluxogramas mostram o comportamento esperado de sua implementação:

Inicialização

Inicialização do Morlock.

Fluxo SetVariable

Fluxo de programação de Morlock.

Fluxo de estado desbloqueado para SetVariable

Morlock desbloqueou o fluxo.

Fluxo de estado bloqueado para SetVariable

Morlock bloqueou o fluxo.

Fluxo para GetVariable

Morlock getVariable.

Confira também

Requisitos de UEFI que se aplicam a todas as edições do Windows em plataformas SoC

Especificação de mitigação de ataque de redefinição de plataforma de grupo de trabalho de cliente de PC, versão 1.10 (download de PDF)

Protegendo o BitLocker contra ataques frios (e outras ameaças)

Um tour além do BIOS com o suporte UEFI TPM2 no EDKII

Proteger as credenciais de domínio derivadas com o Credential Guard

Especificações UEFI