Implementação de MOR segura

Resumo

  • Comportamento de MorLock, revisão 2

Última atualização

  • Agosto de 2020

Aplica-se a

  • Windows 10

  • Fornecedores de OEMs e BIOS que desejam dar suporte ao recurso credential Guard de 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 é aprimorada para dar 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 Runtime do 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 é criada com base em serviços de 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 em sistemas com módulos de plataforma confiáveis. A revisão 2 adiciona uma nova funcionalidade, desbloqueio, para atenuar as preocupações de desempenho de inicialização, especialmente em sistemas de memória grandes.

Em relação ao método de controle de _DSM ACPI para definir o estado do bit MOR (conforme descrito na Seção 6 da Especificação de Mitigação de Ataque de Redefinição de Plataforma de Redefinição de Grupo de Trabalho do Pc, Versão 1.10 (download em 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 de MorLock. Se o MorLock estiver bloqueado, com ou sem uma chave, esse método _DSM deverá falhar ao alterar MOR e retornar um valor de 1 correspondente a "Falha Geral". Nenhum mecanismo de ACPI é definido para desbloquear a revisão 2 do MorLock.

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

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

MemoryOverwriteRequestControlLock

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

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

Nome:MemoryOverwriteRequestControlLock

Atributos: NV+BS+RT

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

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

Bloqueio com SetVariable

Em cada inicialização, o BIOS deve inicializar MemoryOverwriteRequestControlLock para um valor de byte único de 0x00 (indicando desbloqueado) antes da fase de Seleção do 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 não zero válido em Dados, o modo de acesso para e MemoryOverwriteRequestControlLockMemoryOverwriteRequestControl é alterado para somente leitura, indicando que eles estão bloqueados.

As implementações de 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 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úmero aleatório 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 MemoryOverwriteRequestControlLock variáveis e MemoryOverwriteRequestControl são bloqueadas, as invocações de SetVariable (para essas variáveis) são verificadas pela primeira vez na chave registrada usando um algoritmo de tempo constante. Se ambas as chaves estiverem presentes e corresponderem, as variáveis voltarão 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 será a única maneira de desbloquear as variáveis.

O sistema operacional detecta a presença de MemoryOverwriteRequestControlLock e seu estado chamando GetVariable. Em seguida, o sistema pode bloquear o valor atual de MemoryOverwriteRequestControl definindo o MemoryOverwriteRequestControlLock valor como 0x1. Como alternativa, ele pode especificar uma chave para habilitar 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 confirma como 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 da implementação:

Inicialização

inicialização morlock.

Fluxo SetVariable

fluxo de programação morlock.

Fluxo de estado desbloqueado para SetVariable

fluxo desbloqueado do morlock.

Fluxo de estado bloqueado para SetVariable

fluxo bloqueado morlock.

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 da plataforma de trabalho do cliente pc, versão 1.10 (download em PDF)

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

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

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

Especificações de UEFI