Compartilhar via


Lista de verificação de segurança do driver

Este artigo fornece uma lista de verificação de segurança de driver para desenvolvedores de driver para ajudar a reduzir o risco de comprometimento de drivers.

Visão geral da segurança do driver

Uma falha de segurança é qualquer falha que permite que um invasor cause mau funcionamento de um driver de tal forma que faça com que o sistema trave ou se torne inutilizável. Além disso, vulnerabilidades no código do driver podem permitir que um invasor obtenha acesso ao kernel, criando a possibilidade de comprometer todo o sistema operacional. Quando a maioria dos desenvolvedores está trabalhando em seu driver, seu foco é fazer com que o driver funcione corretamente, e não se um invasor mal-intencionado tentará explorar vulnerabilidades em seu código.

Depois que um driver é lançado, no entanto, os invasores podem tentar investigar e identificar falhas de segurança. Os desenvolvedores devem considerar esses problemas durante a fase de design e implementação para minimizar a probabilidade de tais vulnerabilidades. O objetivo é eliminar todas as falhas de segurança conhecidas antes que o driver seja liberado.

A criação de drivers mais seguros requer a cooperação do arquiteto do sistema (pensando conscientemente em possíveis ameaças ao driver), do desenvolvedor que implementa o código (codificando defensivamente operações comuns que podem ser a fonte de explorações) e da equipe de teste (tentando proativamente encontrar pontos fracos e vulnerabilidades). Ao coordenar adequadamente todas essas atividades, a segurança do motorista é drasticamente aprimorada.

Além de evitar os problemas associados a um driver que está sendo atacado, muitas das etapas descritas, como o uso mais preciso da memória do kernel, aumentarão a confiabilidade do driver. Isso reduzirá os custos de suporte e aumentará a satisfação do cliente com seu produto. Concluir as tarefas da lista de verificação abaixo ajudará a atingir todos esses objetivos.

Lista de verificação de segurança: conclua a tarefa de segurança descrita em cada um desses tópicos.

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Confirme se um driver de kernel é necessário

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Usar as estruturas de driver

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Controle o acesso a drivers somente de software

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Não assinar o código do driver de teste de produção

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Realizar análise de ameaças

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Siga as diretrizes de codificação segura do driver

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Implementar código compatível com HVCI

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Siga as práticas recomendadas de código específicas da tecnologia

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Executar revisão de código por pares

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Gerenciar o controle de acesso do motorista

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Aumente a segurança da instalação do dispositivo

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Executar a assinatura adequada do driver de versão

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Usar o CodeQL para verificar o código do driver

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Adicionar anotações SAL ao código do driver

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Usar o Verificador de Driver para verificar vulnerabilidades

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Verifique o código com o BinSkim Binary Analyzer

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Verificar o código com os testes do programa de compatibilidade de hardware

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Examinar técnicas e extensões do depurador

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Entender como os drivers são relatados usando o Centro de Relatórios de Drivers Vulneráveis e Mal-Intencionados da Microsoft

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Examinar recursos de codificação seguros

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança. Revise o resumo das principais conclusões

Confirme se um driver de kernel é necessário

Item #1 da lista de verificação de segurança: confirme se um driver de kernel é necessário e se uma abordagem de menor risco, como o serviço ou aplicativo do Windows, não é uma opção melhor.

Os drivers residem no kernel do Windows e ter um problema ao executar no kernel expõe todo o sistema operacional. Se qualquer outra opção estiver disponível, provavelmente será de menor custo e terá menos risco associado do que criar um novo driver de kernel. Para obter mais informações sobre como usar os drivers internos do Windows, consulte Você precisa escrever um driver?.

Para obter informações sobre como usar tarefas em segundo plano, consulte Dar suporte ao seu aplicativo com tarefas em segundo plano.

Para obter informações sobre como usar os Serviços do Windows, consulte Serviços.

Usar as estruturas de driver

Item #2 da lista de verificação de segurança: use as estruturas de driver para reduzir o tamanho do código e aumentar sua confiabilidade e segurança.

Use o Windows Driver Frameworks para reduzir o tamanho do código e aumentar sua confiabilidade e segurança. Para começar, examine Usando o WDF para desenvolver um driver. Para obter informações sobre como usar o driver de estrutura de modo de usuário de baixo risco (UMDF), consulte Escolhendo um modelo de driver.

Escrever um driver WDM (Windows Driver Model) antiquado é mais demorado, caro e quase sempre envolve a recriação do código disponível nas estruturas de driver.

O código-fonte do Windows Driver Framework é de software livre e está disponível no GitHub. Esse é o mesmo código-fonte do qual a biblioteca de runtime do WDF fornecida no Windows 10 é criada. Você pode depurar seu driver com mais eficiência quando pode seguir as interações entre o driver e o WDF. Faça o download em https://github.com/Microsoft/Windows-Driver-Frameworks.

Controle o acesso a drivers somente de software

Item #3 da lista de verificação de segurança: Se um driver somente de software for criado, o controle de acesso adicional deverá ser implementado.

Os drivers de kernel somente de software não usam PnP (plug-and-play) para serem associados a IDs de hardware específicos e podem ser executados em qualquer PC. Esse driver pode ser usado para fins diferentes do originalmente pretendido, criando um vetor de ataque.

Como os drivers de kernel somente de software contêm risco adicional, eles devem ser limitados para serem executados em hardware específico (por exemplo, usando uma ID PnP exclusiva para habilitar a criação de um driver PnP ou verificando a tabela SMBIOS para a presença de hardware específico).

Por exemplo, imagine que a OEM Fabrikam queira distribuir um driver que habilite um utilitário de overclock para seus sistemas. Se esse driver somente de software for executado em um sistema de um OEM diferente, poderá ocorrer instabilidade ou danos ao sistema. Os sistemas da Fabrikam devem incluir uma ID PnP exclusiva para permitir a criação de um driver PnP que também seja atualizável por meio do Windows Update. Se isso não for possível e a Fabrikam criar um driver herdado, esse driver deverá encontrar outro método para verificar se ele está sendo executado em um sistema da Fabrikam (por exemplo, examinando a tabela SMBIOS antes de habilitar qualquer recurso).

Não assinar código de teste de produção

Item #4 da lista de verificação de segurança: não assinar código de produção código de driver de kernel, teste e fabricação.

O código do driver do kernel usado para desenvolvimento, teste ou fabricação pode incluir recursos perigosos que representam um risco de segurança. Esse código perigoso nunca deve ser assinado com um certificado confiável para o Windows. O mecanismo correto para executar o código de driver perigoso é desabilitar a Inicialização Segura UEFI, habilitar o BCD "TESTSIGNING" e assinar o código de desenvolvimento, teste e fabricação usando um certificado não confiável (por exemplo, um gerado pelo makecert.exe).

O código assinado por uma assinatura confiável do SPC (Certificado de Editores de Software) ou WHQL (Windows Hardware Quality Labs) não deve facilitar o desvio das tecnologias de segurança e integridade de código do Windows. Antes que o código seja assinado por uma assinatura SPC ou WHQL confiável, primeiro verifique se ele está em conformidade com as diretrizes de Criando drivers confiáveis no modo kernel. Além disso, o código não deve conter nenhum comportamento perigoso, descrito abaixo. Para obter mais informações sobre a assinatura do driver, consulte Liberar assinatura do driver mais adiante neste artigo.

Exemplos de comportamento perigoso incluem o seguinte:

  • Fornecendo a capacidade de mapear kernel arbitrário, memória física ou de dispositivo para o modo de usuário.
  • Fornecendo a capacidade de ler ou gravar kernel arbitrário, memória física ou de dispositivo, incluindo entrada/saída de porta (E/S).
  • Fornecer acesso ao armazenamento que ignora o controle de acesso do Windows.
  • Fornecer a capacidade de modificar hardware ou firmware que o driver não foi projetado para gerenciar.

Realizar análise de ameaças

Item #5 da lista de verificação de segurança: modifique um modelo de ameaça de driver existente ou crie um modelo de ameaça personalizado para o driver.

Ao considerar a segurança, uma metodologia comum é criar modelos de ameaças específicos que tentam descrever os tipos de ataques possíveis. Essa técnica é útil ao projetar um driver porque força o desenvolvedor a considerar os possíveis vetores de ataque contra um driver com antecedência. Depois de identificar possíveis ameaças, um desenvolvedor de driver pode considerar meios de se defender contra essas ameaças para reforçar a segurança geral do componente de driver.

Este artigo fornece diretrizes específicas do driver para criar um modelo de ameaça leve: modelagem de ameaças para drivers. O artigo fornece um exemplo de diagrama de modelo de ameaça de driver que pode ser usado como ponto de partida para o driver.

Diagrama de fluxo de dados de exemplo que ilustra um driver de modo kernel hipotético.

As práticas recomendadas do SDL (Ciclo de Vida de Desenvolvimento de Segurança) e as ferramentas associadas podem ser usadas por IHVs e OEMs para melhorar a segurança de seus produtos. Para obter mais informações, consulte Recomendações do SDL para OEMs.

Siga as diretrizes de codificação segura do driver

Item #6 da lista de verificação de segurança: revise seu código e remova todas as vulnerabilidades de código conhecidas.

A atividade principal da criação de drivers seguros é identificar áreas no código que precisam ser alteradas para evitar vulnerabilidades de software conhecidas. Muitas dessas vulnerabilidades de software conhecidas lidam com o controle estrito do uso de memória para evitar problemas com outras pessoas substituindo ou compreendendo os locais de memória que seu driver usa.

Ferramentas de varredura de código, como CodeQL e testes específicos de driver, podem ser usadas para ajudar a localizar algumas, mas não todas, dessas vulnerabilidades. Essas ferramentas e testes são descritos posteriormente neste tópico.

Buffers de memória

  • Sempre verifique os tamanhos dos buffers de entrada e saída para garantir que os buffers possam conter todos os dados solicitados. Para obter mais informações, consulte Falha ao verificar o tamanho dos buffers.

  • Inicialize corretamente todos os buffers de saída com zeros antes de retorná-los ao chamador. Para obter mais informações, consulte Falha ao inicializar buffers de saída.

  • Valide buffers de comprimento variável. Para obter mais informações, consulte Falha ao validar buffers de comprimento variável. Para obter mais informações sobre como trabalhar com buffers e usar ProbeForRead e ProbeForWrite para validar o endereço de um buffer, consulte Tratamento de buffer.

Use o método apropriado para acessar buffers de dados com IOCTLs

Uma das principais responsabilidades de um driver do Windows é transferir dados entre aplicativos no modo de usuário e os dispositivos de um sistema. Os três métodos para acessar buffers de dados são mostrados na tabela a seguir.

Tipo de buffer IOCTL Resumo Para obter mais informações
METHOD_BUFFERED Recomendado para a maioria das situações Usando E/S em buffer
METHOD_IN_DIRECT ou METHOD_OUT_DIRECT Usado em algumas E/S de HW de alta velocidade Usando E/S direta
METHOD_NEITHER Evite se possível Não usando E/S em buffer nem direta

Em geral, a E/S em buffer é recomendada, pois fornece os métodos de buffer mais seguros. Mas mesmo ao usar E/S em buffer, há riscos, como ponteiros inseridos, que devem ser mitigados.

Para obter mais informações sobre como trabalhar com buffers em IOCTLs, consulte Métodos para acessar buffers de dados.

Erros no uso de E/S em buffer IOCTL

  • Verifique o tamanho dos buffers relacionados ao IOCTL. Para obter mais informações, consulte Falha ao verificar o tamanho dos buffers.

  • Inicialize corretamente os buffers de saída. Para obter mais informações, consulte Falha ao inicializar buffers de saída.

  • Valide corretamente os buffers de comprimento variável. Para obter mais informações, consulte Falha ao validar buffers de comprimento variável.

  • Ao usar E/S em buffer, certifique-se de retornar o comprimento adequado para o OutputBuffer no campo Informações da estrutura IO_STATUS_BLOCK . Não retorne diretamente o comprimento diretamente de uma solicitação READ. Por exemplo, considere uma situação em que os dados retornados do espaço do usuário indicam que há um buffer de 4K. Se o driver realmente deve retornar apenas 200 bytes, mas apenas retornar 4K no campo Informações, ocorreu uma vulnerabilidade de divulgação de informações. Esse problema ocorre porque, em versões anteriores do Windows, o buffer que o Gerenciador de E/S usa para E/S em buffer não é zerado. Assim, o aplicativo do usuário recupera os 200 bytes originais de dados mais 4K-200 bytes do que estava no buffer (conteúdo do pool não paginado). Esse cenário pode ocorrer com todos os usos de E/S em buffer e não apenas com IOCTLs.

Erros na E/S direta do IOCTL

Lide com buffers de comprimento zero corretamente. Para obter mais informações, consulte Erros na E/S direta.

Erros na referência a endereços de espaço do usuário

  • Valide ponteiros inseridos em solicitações de E/S em buffer. Para obter mais informações, consulte Erros na referência a endereços de espaço do usuário.

  • Valide qualquer endereço no espaço do usuário antes de tentar usá-lo, usando APIs como ProbeForRead e ProbeForWrite quando apropriado.

O registro específico do modelo MSR lê e grava

Os intrínsecos do compilador, como __readmsr e __writemsr , podem ser usados para acessar os registros específicos do modelo. Se esse acesso for necessário, o driver sempre deverá verificar se o registro para ler ou gravar está restrito ao índice ou intervalo esperado.

Para obter mais informações e exemplos de código, consulte Fornecendo a capacidade de ler/gravar MSRs em Práticas recomendadas de segurança de desenvolvimento para desenvolvedores de driver Windows.

Vulnerabilidades TOCTOU

Há uma vulnerabilidade potencial de tempo de verificação para tempo de uso (TOCTOU) ao usar E/S direta (para IOCTLs ou para leitura/gravação). Lembre-se de que o driver está acessando o buffer de dados do usuário, o usuário pode acessá-lo simultaneamente.

Para gerenciar esse risco, copie todos os parâmetros que precisam ser validados do buffer de dados do usuário para a memória que só pode ser acessada no modo kernel (como a pilha ou o pool). Então, quando os dados não puderem ser acessados pelo aplicativo do usuário, valide e opere nos dados que foram passados.

O código do driver deve fazer uso correto da memória

  • Todas as alocações de pool de drivers devem estar no pool NX (não executável). O uso de pools de memória NX é inerentemente mais seguro do que o uso de pools executáveis não paginados (NP) e fornece melhor proteção contra ataques de estouro.

  • Os drivers de dispositivo devem lidar adequadamente com várias solicitações de E/S de modo de usuário, bem como de kernel para kernel.

Para permitir que os drivers ofereçam suporte à virtualização HVCI, há requisitos adicionais de memória. Para obter mais informações, consulte Implementar código compatível com HVCI mais adiante neste artigo.

Alças

Objetos de dispositivo

  • Proteger objetos de dispositivo. Para obter mais informações, consulte Protegendo objetos de dispositivo.

  • Valide objetos de dispositivo. Para obter mais informações, consulte Falha ao validar objetos de dispositivo.

IRPs

WDF e IRPs

Uma vantagem de usar o WDF é que os drivers WDF normalmente não acessam IRPs diretamente. Por exemplo, a estrutura converte os IRPs do WDM que representam operações de controle de E/S de leitura, gravação e dispositivo em objetos de solicitação de estrutura que o KMDF/UMDF recebe em filas de E/S.

Se você estiver escrevendo um driver WDM, examine as diretrizes a seguir.

Gerenciar adequadamente os buffers de E/S do IRP

Os artigos a seguir fornecem informações sobre como validar valores de entrada IRP:

DispatchReadWrite usando E/S em buffer

Erros na E/S em buffer

DispatchReadWrite usando E/S direta

Erros em E/S diretas

Problemas de segurança para códigos de controle de E/S

Considere validar valores associados a um IRP, como endereços e comprimentos de buffer.

Se você optar por usar Nenhuma E/S, lembre-se de que, ao contrário de Leitura e Gravação, e ao contrário de E/S em buffer e E/S direta, ao usar Nenhuma E/S IOCTL, os ponteiros e comprimentos de buffer não são validados pelo Gerenciador de E/S.

Lidar com operações de conclusão de IRP corretamente

Um driver nunca deve concluir um IRP com um valor de status de STATUS_SUCCESS a menos que ele realmente dê suporte e processe o IRP. Para obter informações sobre as maneiras corretas de lidar com operações de conclusão de IRP, consulte Concluindo IRPs.

Gerenciar estado pendente de IRP do driver

O driver deve marcar o IRP pendente antes de salvar o IRP e deve considerar a inclusão da chamada para IoMarkIrpPending e a atribuição em uma sequência interligada. Para obter mais informações, consulte Falha ao verificar o estado de um driver e retenção de IRPs de entrada quando um dispositivo está pausado.

Lidar com operações de cancelamento de IRP corretamente

As operações de cancelamento podem ser difíceis de codificar corretamente porque normalmente são executadas de forma assíncrona. Problemas no código que manipula operações de cancelamento podem passar despercebidos por um longo tempo, pois esse código normalmente não é executado com frequência em um sistema em execução. Certifique-se de ler e entender todas as informações fornecidas em Cancelamento de IRPs. Preste atenção especial à sincronização do cancelamento de IRP e pontos a serem considerados ao cancelar IRPs.

Uma maneira recomendada de minimizar os problemas de sincronização associados às operações de cancelamento é implementar uma fila IRP segura para cancelamento.

Lidar com a limpeza do IRP e fechar as operações corretamente

Certifique-se de entender a diferença entre solicitações IRP_MJ_CLEANUP e IRP_MJ_CLOSE. As solicitações de limpeza chegam depois que um aplicativo fecha todos os identificadores em um objeto de arquivo, mas às vezes antes que todas as solicitações de E/S sejam concluídas. As solicitações de fechamento chegam depois que todas as solicitações de E/S para o objeto de arquivo foram concluídas ou canceladas. Para obter mais informações, consulte os seguintes artigos:

Rotinas DispatchCreate, DispatchClose e DispatchCreateClose

Rotinas DispatchCleanup

Erros no tratamento de operações de limpeza e fechamento

Para obter mais informações sobre como lidar com IRPs corretamente, consulte Erros adicionais no tratamento de IRPs.

Outros problemas de segurança

  • Use um cadeado ou uma sequência intertravada para evitar condições de corrida. Para obter mais informações, consulte Erros em um ambiente multiprocessador.

  • Verifique se os drivers de dispositivo lidam corretamente com várias solicitações de E/S de modo de usuário, bem como de kernel para kernel.

  • Certifique-se de que nenhum filtro TDI ou LSPs seja instalado pelo driver ou pacotes de software associados durante a instalação ou o uso.

Use funções seguras

Vulnerabilidades de código adicionais

Além das possíveis vulnerabilidades abordadas aqui, este artigo fornece informações adicionais sobre como aprimorar a segurança do código do driver do modo kernel: Criando drivers confiáveis no modo kernel.

Para obter informações adicionais sobre a codificação segura C e C++, consulte Recursos de codificação segura no final deste artigo.

Gerenciar o controle de acesso do motorista

Item #7 da lista de verificação de segurança: revise seu driver para garantir que você esteja controlando corretamente o acesso.

Gerenciando o controle de acesso do driver – WDF

Os drivers devem funcionar para impedir que os usuários acessem inadequadamente os dispositivos e arquivos de um computador. Para impedir o acesso não autorizado a dispositivos e arquivos, você deve:

  • Nomeie objetos de dispositivo somente quando necessário. Os objetos de dispositivo nomeados geralmente são necessários apenas por motivos herdados, por exemplo, se você tiver um aplicativo que espera abrir o dispositivo usando um nome específico ou se estiver usando um dispositivo/dispositivo de controle não PNP. Observe que os drivers WDF não precisam nomear seu dispositivo PnP FDO para criar um link simbólico usando WdfDeviceCreateSymbolicLink.

  • Acesso seguro a objetos e interfaces do dispositivo.

Para permitir que aplicativos ou outros drivers WDF acessem o PDO do dispositivo PnP, você deve usar interfaces de dispositivo. Para obter mais informações, consulte Usando interfaces de dispositivo. Uma interface de dispositivo serve como um link simbólico para o PDO da pilha de dispositivos.

Uma das melhores maneiras de controlar o acesso ao PDO é especificando uma string SDDL em seu INF. Se a cadeia de caracteres SDDL não estiver no arquivo INF, Windows aplicará um descritor de segurança padrão. Para obter mais informações, consulte Protegendo objetos de dispositivo e SDDL para objetos de dispositivo.

Para obter mais informações sobre como controlar o acesso, consulte os seguintes artigos:

Controlando o acesso ao dispositivo em drivers KMDF

Nomes, descritores de segurança e classes de dispositivo - Tornando os objetos de dispositivo acessíveis... e SEGURO do The NT Insider Newsletter de janeiro de fevereiro de 2017 publicado pela OSR.

Gerenciando o controle de acesso do driver – WDM

Se você estiver trabalhando com um driver WDM e tiver usado um objeto de dispositivo nomeado, poderá usar IoCreateDeviceSecure e especificar um SDDL para protegê-lo. Ao implementar IoCreateDeviceSecure , sempre especifique um GUID de classe personalizado para DeviceClassGuid. Você não deve especificar um GUID de classe existente aqui. Isso tem o potencial de interromper as configurações de segurança ou compatibilidade de outros dispositivos pertencentes a essa classe. Para obter mais informações, consulte WdmlibIoCreateDeviceSecure.

Para obter mais informações, consulte os seguintes artigos:

Controlando o acesso ao dispositivo

Controlando o acesso ao namespace do dispositivo

Modelo de segurança do Windows para desenvolvedores de driver

Hierarquia de risco de SIDs (identificadores de segurança)

A seção a seguir descreve a hierarquia de risco dos SIDs comuns usados no código do driver. Para obter informações gerais sobre SDDL, consulte SDDL para objetos de dispositivo, cadeias de caracteres SID e sintaxe de cadeia de caracteres SDDL.

É importante entender que, se chamadores de privilégios mais baixos tiverem permissão para acessar o kernel, o risco de código aumentará. Neste diagrama de resumo, o risco aumenta à medida que você permite que SIDs de privilégios mais baixos acessem sua funcionalidade de driver.

SY (System)
\/
BA (Built-in Administrators)
\/
LS (Local Service)
\/
BU (Built-in User)
\/
AC (Application Container)

Seguindo o princípio geral de segurança de privilégios mínimos, configure apenas o nível mínimo de acesso necessário para que o driver funcione.

Controle de segurança IOCTL granular do WDM

Para gerenciar ainda mais a segurança quando IOCTLs são enviados por chamadores no modo de usuário, o código do driver pode incluir a função IoValidateDeviceIoControlAccess . Essa função permite que um motorista verifique os direitos de acesso. Ao receber um IOCTL, um driver pode chamar IoValidateDeviceIoControlAccess, especificando FILE_READ_ACCESS, FILE_WRITE_ACCESS ou ambos.

A implementação do controle de segurança IOCTL granular não substitui a necessidade de gerenciar o acesso do driver usando as técnicas discutidas acima.

Para obter mais informações, consulte os seguintes artigos:

Definindo códigos de controle de E/S

Implementar código compatível com HVCI

Item #8 da lista de verificação de segurança: valide se o driver usa memória para que ele seja compatível com HVCI.

Uso de memória e compatibilidade HVCI

O HVCI usa tecnologia de hardware e virtualização para isolar a função de tomada de decisão de CI (Integridade de Código) do restante do sistema operacional. Ao usar a segurança baseada em virtualização para isolar o CI, a única maneira de a memória do kernel se tornar executável é por meio de uma verificação de CI. Isso significa que as páginas de memória do kernel nunca podem ser graváveis e executáveis (W + X) e o código executável não pode ser modificado diretamente.

Para implementar o código compatível com HVCI, verifique se o código do driver faz o seguinte:

  • Aceita o NX por padrão
  • Usa APIs/sinalizadores NX para alocação de memória (NonPagedPoolNx)
  • Não usa seções que são graváveis e executáveis
  • Não tenta modificar diretamente a memória do sistema executável
  • Não usa código dinâmico no kernel
  • Não carrega arquivos de dados como executáveis
  • O alinhamento da seção é um múltiplo de 0x1000 (PAGE_SIZE). Por exemplo, DRIVER_ALIGNMENT=0x1000

Para obter mais informações sobre como usar a ferramenta e uma lista de chamadas de memória incompatíveis, consulte Implementar código compatível com HVCI.

Para obter mais informações sobre o teste de segurança de conceitos básicos do sistema relacionado, consulte Teste de prontidão de integridade de código do hipervisor e HVCI (integridade de código protegida por hipervisor).

Siga as práticas recomendadas de código específicas da tecnologia

Item #9 da lista de verificação de segurança: examine as seguintes diretrizes específicas de tecnologia para o driver.

Sistemas de Arquivos

Para obter mais informações sobre a segurança do driver do sistema de arquivos, consulte os seguintes artigos:

Introdução à segurança de sistemas de arquivos

Problemas de segurança do sistema de arquivos

Recursos de segurança para sistemas de arquivos

Coexistência com outros drivers de filtro do sistema de arquivos

NDIS - Rede

Para obter informações sobre a segurança do driver NDIS, consulte Problemas de segurança para drivers de rede.

Exibição

Para obter informações sobre a segurança do driver de exibição, consulte <Conteúdo pendente>.

Impressoras

Para obter informações relacionadas à segurança do driver de impressora, consulte Considerações sobre segurança do driver de impressora V4.

Problemas de segurança para drivers WIA (aquisição de imagem do Windows)

Para obter informações sobre a segurança do WIA, consulte Problemas de segurança para drivers WIA (aquisição de imagem do Windows).

Aumente a segurança da instalação do dispositivo

Item #10 da lista de verificação de segurança: examine as diretrizes de criação e instalação de inf de driver para garantir que você esteja seguindo as práticas recomendadas.

Ao criar o código que instala seu driver, você deve certificar-se de que a instalação do seu dispositivo sempre será realizada de maneira segura. Uma instalação segura de dispositivo é aquela que faz o seguinte:

  • Limita o acesso ao dispositivo e suas classes de interface de dispositivo
  • Limita o acesso aos serviços de driver que foram criados para o dispositivo
  • Protege os arquivos de driver contra modificação ou exclusão
  • Limita o acesso às entradas de registro do dispositivo
  • Limita o acesso às classes WMI do dispositivo
  • Usa as funções SetupAPI corretamente

Para obter mais informações, consulte os seguintes artigos:

Criando instalações seguras de dispositivos

Diretrizes para usar a SetupAPI

Usando as funções de instalação do dispositivo

Tópicos avançados de instalação de dispositivos e drivers

Executar revisão de código por pares

Item #11 da lista de verificação de segurança: Executar revisão de código por pares, para procurar problemas não revelados pelas outras ferramentas e processos

Procure revisores de código experientes para procurar problemas que você possa ter perdido. Um segundo par de olhos geralmente verá problemas que você pode ter esquecido.

Se você não tiver uma equipe adequada para revisar seu código internamente, considere contratar ajuda externa para essa finalidade.

Executar a assinatura adequada do driver de versão

Item #12 da lista de verificação de segurança: use o portal do parceiro do Windows para assinar corretamente o driver para distribuição.

Antes de lançar um pacote de driver para o público, recomendamos que você envie o pacote para certificação. Para obter mais informações, consulte Testar o desempenho e a compatibilidade, Introdução ao programa de hardware, Hardware Dashboard Services e Atestar a assinatura de um driver de kernel para versão pública.

Usar o CodeQL para verificar o código do driver

Item #13 da lista de verificação de segurança: use o CodeQL para verificar vulnerabilidades no código do driver.

O CodeQL, do GitHub, é um mecanismo de análise de código semântico, e a combinação de um amplo conjunto de consultas de segurança com uma plataforma robusta o torna uma ferramenta inestimável para proteger o código do driver. Para obter mais informações, consulte CodeQL e o teste de logotipo de ferramentas estáticas.

Adicionar anotações SAL ao código do driver

Item #14 da lista de verificação de segurança: adicione anotações SAL ao código do driver.

A SAL (linguagem de anotação de código-fonte) fornece um conjunto de anotações que você pode usar para descrever como uma função usa seus parâmetros, as suposições que ela faz sobre eles e as garantias que ela faz quando é concluída. As anotações são definidas no arquivo de cabeçalho sal.h. A análise de código do Visual Studio para C++ usa anotações da SAL para modificar a análise de funções. Para obter mais informações sobre o desenvolvimento de driver SAL 2.0 para Windows, consulte Anotações SAL 2.0 para drivers Windows e Usando anotações SAL para reduzir defeitos de código C/C++.

Para obter informações gerais sobre SAL, consulte este artigo disponível na OSR. https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/

Usar o Verificador de Driver para verificar vulnerabilidades

Item #15 da lista de verificação de segurança: use o Verificador de Driver para verificar se há vulnerabilidades no código do driver.

O Verificador de Driver usa um conjunto de regras de interface e um modelo do sistema operacional para determinar se o driver interage corretamente com o sistema operacional Windows. O DV encontra defeitos no código do driver que podem apontar para possíveis bugs nos drivers.

O Verificador de Driver permite o teste ao vivo do driver. O Driver Verifier monitora drivers gráficos e drivers no modo kernel do Windows para detectar chamadas de função ilegais ou ações que podem corromper o sistema. O Verificador de Driver pode sujeitar os drivers do Windows a uma variedade de estresses e testes para encontrar um comportamento impróprio. Para obter mais informações, consulte Verificador de Driver.

Observe que apenas determinados tipos de drivers são suportados pelo DV. Para obter mais informações sobre os drivers que o DV pode verificar, consulte Drivers com suporte. Consulte as páginas a seguir para obter informações sobre os testes DV disponíveis para o tipo de driver com o qual você está trabalhando.

Para se familiarizar com o DV, você pode usar um dos drivers de exemplo (por exemplo, o exemplo de torradeira em destaque: https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured).

Verifique o código com o BinSkim Binary Analyzer

Item #16 da lista de verificação de segurança: siga estas etapas para usar o BinSkim para verificar se as opções de compilação e compilação estão configuradas para minimizar problemas de segurança conhecidos.

Use o BinSkim para examinar arquivos binários para identificar práticas de codificação e construção que podem potencialmente tornar o binário vulnerável.

O BinSkim verifica:

  • Uso de conjuntos de ferramentas do compilador desatualizados – os binários devem ser compilados em relação aos conjuntos de ferramentas do compilador mais recentes sempre que possível para maximizar o uso das mitigações de segurança atuais no nível do compilador e fornecidas pelo sistema operacional.
  • Configurações de compilação inseguras – os binários devem ser compilados com as configurações mais seguras possíveis para habilitar mitigações de segurança fornecidas pelo sistema operacional, maximizar erros do compilador e relatórios de avisos acionáveis, entre outras coisas.
  • Problemas de assinatura – os binários assinados devem ser assinados com algoritmos criptograficamente fortes.

O BinSkim é uma ferramenta de código aberto e gera arquivos de saída que usam o formato SARIF (Static Analysis Results Interchange Format). O BinSkim substitui a antiga ferramenta BinScope .

Para obter mais informações sobre o BinSkim, consulte o Guia do usuário do BinSkim.

Siga estas etapas para validar se as opções de compilação de segurança estão configuradas corretamente no código que você está enviando.

  1. Baixe e instale o SDK do .NET Core multiplataforma.

  2. Confirme se o Visual Studio está instalado. Para obter informações sobre como baixar e instalar o Visual Studio, consulte Instalar o Visual Studio.

  3. Há várias opções para baixar o BinSkim, como um pacote NuGet. Neste exemplo, usaremos a opção git clone para fazer o download aqui: https://github.com/microsoft/binskim e instalá-lo em um PC com Windows de 64 bits.

  4. Abra uma janela do Prompt de Comando do Desenvolvedor do Visual Studio e crie um diretório, por exemplo C:\binskim-master.

    C:\> Md \binskim-master
    
  5. Vá para o diretório que você acabou de criar.

    C:\> Cd \binskim-master
    
  6. Use o comando git clone para baixar todos os arquivos necessários.

    C:\binskim-master> git clone --recurse-submodules https://github.com/microsoft/binskim.git
    
  7. Vá para o novo binskim diretório criado pelo comando clone.

    C:\> Cd \binskim-master\binskim
    
  8. Execute BuildAndTest.cmd para garantir que a compilação de versão seja bem-sucedida e que todos os testes sejam aprovados.

    C:\binskim-master\binskim> BuildAndTest.cmd
    
    Welcome to .NET Core 3.1!
    ---------------------
    SDK Version: 3.1.101
    
    ...
    
    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64\BinSkim.Sdk.dll
    1 File(s) copied
    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\linux-x64\BinSkim.Sdk.dll
    1 File(s) copied
    
    ...
    
    
  9. O processo de compilação cria um conjunto de diretórios com os executáveis BinSkim. Vá para o diretório de saída de build win-x64.

    C:\binskim-master\binskim> Cd \binskim-master\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64>
    
  10. Exiba a ajuda para a opção de análise.

C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim help analyze

BinSkim PE/MSIL Analysis Driver 1.6.0.0

--sympath                      Symbols path value, e.g., SRV*http://msdl.microsoft.com/download/symbols or Cache*d:\symbols;Srv*http://symweb. See
                              https://learn.microsoft.com/windows-hardware/drivers/debugger/advanced-symsrv-use for syntax information. Note that BinSkim will clear the
                              _NT_SYMBOL_PATH environment variable at runtime. Use this argument for symbol information instead.

--local-symbol-directories     A set of semicolon-delimited local directory paths that will be examined when attempting to locate PDBs.

-o, --output                   File path to which analysis output will be written.

--verbose                      Emit verbose output. The resulting comprehensive report is designed to provide appropriate evidence for compliance scenarios.

...

Definindo o caminho do símbolo

Se você estiver criando todo o código que está analisando no mesmo computador em que está executando o BinSkim, normalmente não será necessário definir o caminho do símbolo. Isso ocorre porque seus arquivos de símbolo estão disponíveis na caixa local onde você compilou. Se você estiver usando um sistema de compilação mais complexo ou redirecionando seus símbolos para um local diferente (não ao lado do binário compilado), use --local-symbol-directories para adicionar esses locais à pesquisa do arquivo de símbolo. Se o código fizer referência a um binário compilado que não faz parte do código, o sympath do depurador do Windows poderá ser usado para recuperar símbolos para verificar a segurança dessas dependências de código. Se você encontrar um problema nessas dependências, talvez não consiga corrigi-lo. Mas pode ser útil estar ciente de qualquer possível risco de segurança que você esteja aceitando ao assumir essas dependências.

Dica

Ao adicionar um caminho de símbolo (que faz referência a um servidor de símbolos em rede), adicione um local de cache local para especificar um caminho local para armazenar os símbolos em cache. Não fazer isso pode comprometer muito o desempenho do BinSkim. O exemplo a seguir especifica um cache local em d:\symbols. --sympath Cache*d:\symbols;Srv*http://symweb Para obter mais informações sobre sympath, consulte Caminho de símbolo para depuradores Windows.

  1. Execute o comando a seguir para analisar um binário de driver compilado. Atualize o caminho de destino para apontar para o arquivo de .sys do driver em conformidade.

    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\echo.sys"
    
  2. Para obter informações adicionais, adicione a opção detalhada como esta.

    C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys" --verbose
    

    Observação

    A opção --verbose produzirá resultados explícitos de aprovação/reprovação para cada verificação. Se você não fornecer detalhado, verá apenas os defeitos detectados pelo BinSkim. A opção --verbose normalmente não é recomendada para sistemas de automação reais devido ao aumento do tamanho dos arquivos de log e porque torna mais difícil detectar falhas individuais quando elas ocorrem, pois elas serão incorporadas no meio de um grande número de resultados de 'aprovação'.

  3. Revise a saída do comando para procurar possíveis problemas. Este exemplo de saída mostra três testes que passaram. Informações adicionais sobre as regras, como BA2002, estão disponíveis no Guia do Usuário do BinSkim.

    Analyzing...
    Analyzing 'osrusbfx2.sys'...
    ...
    
    C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys\Debug\osrusbfx2.sys: pass BA2002: 'osrusbfx2.sys' does not incorporate any known vulnerable dependencies, as configured by current policy.
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: pass BA2005: 'osrusbfx2.sys' is not known to be an obsolete binary that is vulnerable to one or more security problems.
    C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys: pass BA2006: All linked modules of 'osrusbfx2.sys' generated by the Microsoft front-end satisfy configured policy (compiler minimum version 17.0.65501.17013).
    
  4. Esta saída mostra que o teste BA3001 não é executado, pois a ferramenta indica que o driver não é um binário ELF.

    ...
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: notapplicable BA3001: 'osrusbfx2.sys' was not evaluated for check 'EnablePositionIndependentExecutable' as the analysis is not relevant based on observed metadata: image is not an ELF binary.
    
  5. Esta saída mostra um erro para o teste BA2007.

    ...
    
    C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: error BA2007: 'osrusbfx2.sys' disables compiler warning(s) which are required by policy.
    A compiler warning is typically required if it has a high likelihood of flagging memory corruption, information disclosure, or double-free vulnerabilities.
    To resolve this issue, enable the indicated warning(s) by removing /Wxxxx switches (where xxxx is a warning id indicated here) from your command line, and resolve any warnings subsequently raised during compilation.
    

Para habilitar esses avisos no Visual Studio, em C/C++ nas páginas de propriedades do projeto, remova os valores que você não deseja excluir em Desabilitar Avisos Específicos.

Captura de tela da caixa de diálogo para desabilitar avisos específicos no Visual Studio 2019.

As opções de compilação padrão no Visual Studio para projetos de driver podem desabilitar avisos como os seguintes. Esses avisos serão relatados pela BinSkim.

C4603 - 'name': a macro não está definida ou a definição é diferente após o uso do cabeçalho pré-compilado

C4627 - 'description': ignorado ao procurar o uso de cabeçalho pré-compilado

C4986 - 'declaração': a especificação de exceção não corresponde à declaração anterior

Para obter mais informações sobre os avisos do compilador, consulte Avisos do compilador por versão do compilador.

Verificar o código com os testes do programa de compatibilidade de hardware

Item #17 da lista de verificação de segurança: use os testes do programa de compatibilidade de hardware relacionados à segurança para verificar se há problemas de segurança.

O programa de compatibilidade de hardware inclui testes relacionados à segurança que podem ser usados para procurar vulnerabilidades de código. O Programa de Compatibilidade de Hardware do Windows aproveita os testes no HLK (Windows Hardware Lab Kit). Os testes de Conceitos Básicos do Dispositivo HLK podem ser usados na linha de comando para exercitar o código do driver e investigar se há pontos fracos. Para obter informações gerais sobre os testes de conceitos básicos do dispositivo e o programa de compatibilidade de hardware, consulte Windows Hardware Lab Kit.

Os testes a seguir são exemplos de testes que podem ser úteis para verificar o código do driver em busca de alguns comportamentos associados a vulnerabilidades de código:

DF - Teste de fuzz aleatório IOCTL (Confiabilidade)

DF - Teste de sub-aberturas difusas (Confiabilidade)

DF - Teste FSCTL de buffer de comprimento zero fuzz (Confiabilidade)

DF - Teste FSCTL aleatório de fuzz (Confiabilidade)

DF - Teste de API Fuzz Misc (Confiabilidade)

Você também pode usar o fuzzing de atraso de sincronização do kernel incluído no Verificador de Driver.

Os testes CHAOS (Concurrent Hardware and Operating System) executam vários testes de driver PnP, testes de fuzz de driver de dispositivo e testes de sistema de energia simultaneamente. Para obter mais informações, consulte Testes CHAOS (conceitos básicos do dispositivo).

Os testes de penetração de conceitos básicos do dispositivo executam várias formas de ataques de entrada, que são um componente crítico dos testes de segurança. Os testes de ataque e penetração podem ajudar a identificar vulnerabilidades nas interfaces de software. Para obter mais informações, consulte Testes de penetração (conceitos básicos do dispositivo).

Use o Device Guard – Teste de Conformidade, juntamente com as outras ferramentas descritas neste artigo, para confirmar se o driver é compatível com HVCI.

Ferramentas de teste personalizadas e específicas do domínio

Considere o desenvolvimento de testes de segurança personalizados específicos do domínio. Para desenvolver testes adicionais, reúna informações dos designers originais do software, bem como de recursos de desenvolvimento não relacionados familiarizados com o tipo específico de driver que está sendo desenvolvido e de uma ou mais pessoas familiarizadas com a análise e prevenção de intrusão de segurança.

Examinar técnicas e extensões do depurador

Item #18 da lista de verificação de segurança: examine essas ferramentas de depuração e considere seu uso no fluxo de trabalho de depuração de desenvolvimento.

A extensão !acl formata e exibe o conteúdo de uma ACL (lista de controle de acesso). Para obter mais informações, consulte Determinando a ACL de um objeto e !acl.

A extensão !token mostra uma exibição formatada de um objeto de token de segurança. Para obter mais informações, consulte !token.

A extensão !tokenfields exibe os nomes e deslocamentos dos campos dentro do objeto de token de acesso (a estrutura TOKEN). Para obter mais informações, consulte !tokenfields.

A extensão !sid mostra o identificador de segurança (SID) no endereço especificado. Para obter mais informações, consulte !sid.

A extensão !sd exibe o descritor de segurança no endereço especificado. Para obter mais informações, consulte !sd.

Centro de Relatórios de Drivers Vulneráveis e Mal-Intencionados da Microsoft

Qualquer pessoa pode enviar um driver questionável usando o Microsoft Vulnerable and Malicious Driver Reporting Center. Consulte esta entrada de blog para obter informações sobre como os drivers são enviados para análise - Melhore a segurança do kernel com o novo Centro de Relatórios de Drivers Vulneráveis e Mal-Intencionados da Microsoft

A Central de Relatórios pode verificar e analisar drivers do Windows criados para arquiteturas x86 e x64. Drivers verificados vulneráveis e mal-intencionados são sinalizados para análise e investigação pela equipe de Drivers Vulneráveis da Microsoft. Depois que os motoristas vulneráveis são confirmados, ocorre uma notificação apropriada e eles são adicionados à lista de bloqueios de drivers vulneráveis. Para obter mais informações sobre isso, consulte Regras de bloqueio de driver recomendadas pela Microsoft. Essas regras são aplicadas por padrão a dispositivos habilitados para HVCI (integridade de código protegida por hipervisor) e Windows 10 no modo S.

Examinar recursos de codificação seguros

Item #19 da lista de verificação de segurança: examine esses recursos para expandir sua compreensão das práticas recomendadas de codificação segura aplicáveis aos desenvolvedores de driver.

Revise esses recursos para saber mais sobre a segurança do driver

Diretrizes de codificação de driver de modo kernel seguro

Criando drivers confiáveis no modo kernel

Organizações de codificação seguras

Universidade Carnegie Mellon SEI CERT

Padrão de codificação SEI CERT C da Carnegie Mellon University: regras para o desenvolvimento de sistemas seguros, confiáveis e protegidos (edição de 2016).

MITRE - Pontos Fracos Abordados pelo Padrão de Codificação Segura CERT C

Construindo o Modelo de Segurança na Maturidade (BSIMM) - https://www.bsimm.com/

COFRECode - https://safecode.org/

Recursos da CISA

OSR

A OSR fornece treinamento de desenvolvimento de drivers e serviços de consultoria. Esses artigos do boletim informativo da OSR destacam os problemas de segurança do driver.

Nomes, descritores de segurança e classes de dispositivo - Tornando os objetos de dispositivo acessíveis... e SEGURO

Você precisa usar proteção - segurança interna de driver e dispositivo

Bloqueando Drivers - Uma Pesquisa de Técnicas

Meltdown e Spectre: E os pilotos?

Estudo de caso

Do alerta à vulnerabilidade do driver: a investigação do Microsoft Defender ATP revela falha de escalonamento de privilégios

Manuais

24 pecados capitais da segurança de software: falhas de programação e como corrigi-las por Michael Howard, David LeBlanc e John Viega

A arte da avaliação de segurança de software: identificando e prevenindo vulnerabilidades de software, Mark Dowd, John McDonald e Justin Schuh

Escrevendo Software Seguro Segunda Edição, Michael Howard e David LeBlanc

A Arte da Avaliação de Segurança de Software: Identificando e Prevenindo Vulnerabilidades de Software, Mark Dowd e John McDonald

Codificação Segura em C e C++ (Série SEI em Engenharia de Software) 2ª Edição, Robert C. Seacord

Programando o modelo de driver do Microsoft Windows (2ª edição), Walter Oney

Desenvolvendo drivers com o Windows Driver Foundation (Referência do desenvolvedor), Penny Orwick e Guy Smith

Treinamento

O treinamento em sala de aula do driver do Windows está disponível em fornecedores como os seguintes:

O treinamento on-line de codificação segura está disponível em várias fontes. Por exemplo, este curso está disponível no coursera em:

Identificando vulnerabilidades de segurança na programação C/C++.

O SAFECode também oferece treinamento gratuito:

SAFECode.org/training

Certificação Profissional

O CERT oferece uma Certificação Profissional de Codificação Segura.

Resumo das principais conclusões

A segurança do driver é uma tarefa complexa que contém muitos elementos, mas aqui estão alguns pontos-chave a serem considerados:

  • Os drivers residem no kernel do Windows e ter um problema ao executar no kernel expõe todo o sistema operacional. Por isso, preste muita atenção à segurança do motorista e projete com a segurança em mente.

  • Aplique o princípio do privilégio mínimo:

    a. Use uma cadeia de caracteres SDDL estrita para restringir o acesso ao driver

    b. Restringir ainda mais os IOCTLs individuais

  • Crie um modelo de ameaça para identificar vetores de ataque e considere se algo pode ser restringido ainda mais.

  • Tenha cuidado com relação aos ponteiros inseridos que estão sendo passados do modo de usuário. Eles precisam ser investigados, acessados dentro de try, exceto, e são propensos a problemas de tempo de tempo de verificação de uso (ToCToU), a menos que o valor do buffer seja capturado e comparado.

  • Se você não tiver certeza, use METHOD_BUFFERED como um método de buffer IOCTL.

  • Use utilitários de varredura de código para procurar vulnerabilidades de código conhecidas e corrigir quaisquer problemas identificados.

  • Procure revisores de código experientes para procurar problemas que você possa ter perdido.

  • Use verificadores de driver e teste seu driver com várias entradas, incluindo casos extremos.