Partilhar via


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

Neste artigo, você encontra uma lista de verificação de segurança de driver para desenvolvedores que desejam reduzir o risco de comprometimento dos drivers.

Visão geral da segurança do driver

Falha de segurança pode ser qualquer falha que possibilite a um invasor causar 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, possibilitando o comprometimento de todo o sistema operacional. O objetivo da maioria dos desenvolvedores que trabalham com drivers é fazer com que o driver funcione corretamente, e não se haverá um invasor mal-intencionado tentando explorar vulnerabilidades em seu código.

Entretanto, depois que um driver é lançado, os invasores podem tentar investigar e identificar falhas de segurança. Os desenvolvedores devem pensar nesses problemas durante a fase de design e implementação para minimizar a probabilidade dessas vulnerabilidades. O objetivo é eliminar todas as falhas de segurança conhecidas antes que o driver seja lançado.

A criação de drivers mais seguros exige cooperação do arquiteto do sistema (que pensa cuidadosamente nas possíveis ameaças ao driver), do desenvolvedor que implementa o código (que codifica defensivamente operações comuns que podem ser a causa das explorações) e da equipe de teste (que tenta proativamente encontrar pontos fracos e vulnerabilidades). Com a coordenação adequada de todas essas atividades, a segurança do driver aumenta drasticamente.

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

Lista de verificação de segurança: Realize 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 há necessidade de um driver de kernel

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

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

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

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Faça uma 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.Implemente 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.Faça revisão de código por pares

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

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.Execute a assinatura adequada do driver da versão

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Use 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.Adicione 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.Use o Driver Verifier 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.Verifique 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.Analise técnicas e extensões do depurador

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Use o Microsoft Vulnerable and Malicious Driver Reporting Center para saber como os drivers são reportados

Caixa de seleção desmarcada que representa um item na lista de verificação de segurança.Examine os recursos de codificação segura

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

Confirme se há necessidade de um driver de kernel

Item 1 da lista de verificação de segurança: Confirme se há necessidade de um driver de kernel 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. Problemas ao executar no kernel podem expor todo o sistema operacional. Se houver qualquer outra opção disponível, provavelmente será de menor custo e terá menos riscos associados do que criar um novo driver de kernel. Para obter mais informações sobre como usar os drivers internos do Windows, consulte Precisa escrever um driver?.

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

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

Use as estruturas do driver

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

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

Escrever um driver de WDM (Windows Driver Model) antigo leva mais tempo, é caro e quase sempre envolve recriar o código disponível nas estruturas do driver.

O Windows Driver Framework tem código-fonte aberto e está disponível no GitHub. É o mesmo código-fonte usado para criar a biblioteca de runtime do WDF fornecida no Windows 10. Você poderá depurar seu driver com mais eficiência quando seguir as interações entre o driver e o WDF. Para baixá-lo, acesse https://github.com/Microsoft/Windows-Driver-Frameworks.

Controle o acesso a drivers que são 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. Eles podem ser executados em qualquer PC. Esse driver pode ser usado para outros fins, 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 procurando na tabela SMBIOS a presença de um hardware específico).

Por exemplo, imagine que o 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, o sistema poderá sofrer danos ou instabilidade. Os sistemas da Fabrikam devem incluir uma ID PnP exclusiva para permitir a criação de um driver PnP que também possa ser atualizado 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 está sendo executado em um sistema da Fabrikam (por exemplo, examinando a tabela SMBIOS antes de habilitar qualquer recurso).

Não assine o código do driver de teste em produção

Item 4 da lista de verificação de segurança: Não assine o código de driver de kernel em produção para desenvolvimento, teste e fabricação.

O código de driver de kernel usado para desenvolvimento, teste ou fabricação pode incluir recursos perigosos que representam um risco à segurança. Esse código perigoso nunca deve ser assinado com um certificado que seja 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 que seja gerado pelo makecert.exe).

O código assinado por um SPC (Certificado de Editores de Software) ou WHQL (Windows Hardware Quality Labs) confiável 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 um SPC ou WHQL confiável, primeiro verifique se ele está em conformidade com as diretrizes de Como criar drivers confiáveis no modo kernel. Além disso, o código não deve conter comportamentos perigosos, como descrito abaixo. Para obter mais informações sobre a assinatura do driver, consulte Assinatura de driver da versão mais adiante neste artigo.

Exemplos de comportamentos perigosos incluem o seguinte:

  • Possibilitar o mapeamento de um kernel arbitrário, memória física ou de dispositivo para o modo de usuário.
  • Possibilitar a leitura ou gravação de um kernel arbitrário, memória física ou dispositivo, incluindo entrada/saída (E/S) de porta.
  • Conceder acesso ao armazenamento a ponto de ignorar o controle de acesso do Windows.
  • Possibilitar a modificação de um hardware ou firmware que o driver não foi desenvolvido para gerenciar.

Faça uma 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 pensar em 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 com antecedência os possíveis vetores de ataque contra um driver. 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 do driver.

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

Exemplo de diagrama de fluxo de dados que ilustra um driver de modo kernel hipotético.

As práticas recomendadas de 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 as Recomendações de 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 no código áreas 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 casos de substituição ou abrangência de locais de memória que seu driver utiliza.

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

Buffers de memória

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 Usar E/S em buffer
METHOD_IN_DIRECT ou METHOD_OUT_DIRECT Usado em algumas E/S de HW de alta velocidade Usar E/S direta
METHOD_NEITHER Se possível, evite Usar E/S direta ou em buffer

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, existem riscos, como ponteiros inseridos, que devem ser atenuados.

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

Erros ao usar E/S em buffer IOCTL

  • Verifique o tamanho dos buffers relacionados a 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, retorne o comprimento adequado para OutputBuffer no campo Informações da estrutura de IO_STATUS_BLOCK. Não retorne diretamente o comprimento de uma solicitação READ. Por exemplo, pense em 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 precisa retornar apenas 200 bytes, mas retorna 4K no campo Informações, então 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 de tudo o 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, não apenas com IOCTLs.

Erros na E/S direta de IOCTL

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

Erros ao referenciar endereços de espaço do usuário

Leituras e gravações de registro específicas do modelo MSR

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 de leitura ou gravação está restrito ao índice ou intervalo esperado.

Para obter mais informações e exemplos de código, consulte Possibilitar leitura e gravação de MSRs em Práticas recomendadas de segurança de desenvolvimento para desenvolvedores de driver do Windows.

Vulnerabilidades TOCTOU

Existe uma possível vulnerabilidade 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, então 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). Em seguida, 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), além de oferecer melhor proteção contra ataques de estouro.

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

Para permitir que os drivers permitam a 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

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 leitura, gravação e controle de E/S do dispositivo em objetos de solicitação de estrutura que o KMDF/UMDF recebe em filas de E/S.

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

Gerencie adequadamente os buffers de E/S de IRP

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

DispatchReadWrite usando E/S em buffer

Erros na E/S em buffer

DispatchReadWrite usando E/S direta

Erros na E/S direta

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

Valide os 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.

Gerencie 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 aceite e processe o IRP. Para obter informações sobre as formas corretas de gerenciar operações de conclusão de IRP, consulte Como concluir IRPs.

Gerencie o estado pendente de IRP do driver

O driver deve marcar o IRP pendente antes de salvar o IRP, considerando 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 Como reter IRPs de entrada quando um dispositivo está pausado.

Gerencie operações de cancelamento de IRP corretamente

Pode ser difícil codificar corretamente as operações de cancelamento porque elas normalmente são executadas de forma assíncrona. Os problemas no código que gerencia operações de cancelamento podem passar despercebidos por muito tempo, pois esse código normalmente não é executado com frequência em um sistema em execução. Você deve ler e entender todas as informações apresentadas em Como cancelar IRPs. Preste atenção especial a Sincronização do cancelamento de IRP e Pontos a serem considerados ao cancelar IRPs.

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

Gerencie a limpeza do IRP e feche as operações corretamente

Entenda 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 são concluídas ou canceladas. Para obter mais informações, consulte os seguintes artigos:

Rotinas DispatchCreate, DispatchClose e DispatchCreateClose

Rotinas do DispatchCleanup

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

Para obter mais informações sobre como gerenciar IRPs corretamente, consulte Erros adicionais ao gerenciar IRPs.

Outros problemas de segurança

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

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

  • Verifique se não há filtros TDI ou LSPs instalados pelo driver ou pacotes de software associados durante a instalação ou o uso.

Use funções seguras

Vulnerabilidades adicionais de código

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

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

Gerencie o controle de acesso do driver

Item 7 da lista de verificação de segurança: Revise o driver para saber se você está controlando corretamente o acesso.

Como gerenciar o controle de acesso do driver – WDF

A função dos drivers é impedir que os usuários acessem indevidamente os dispositivos e arquivos de um computador. Para impedir o acesso não autorizado a dispositivos e arquivos, você deve:

  • Nomear 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 não PNP/dispositivo de controle. Os drivers WDF não precisam nomear seu dispositivo PnP FDO para criar um link simbólico usando WdfDeviceCreateSymbolicLink.

  • Proteger o acesso 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 Usar 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, o Windows aplicará um descritor de segurança padrão. Para obter mais informações, consulte Proteger objetos de dispositivo e SDDL para objetos de dispositivo.

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

Controlar o acesso ao dispositivo em drivers KMDF

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

Como gerenciar 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. Fazer isso pode 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:

Controlar o acesso ao dispositivo

Controlar o acesso ao namespace do dispositivo

Modelo de segurança do Windows para desenvolvedores de drivers

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 resumido, o risco aumenta à medida que você permite que SIDs de privilégios mais baixos acessem as funcionalidades do 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 melhor 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 driver 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 abordadas anteriormente.

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

Definir códigos de controle de E/S

Implemente 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 com 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 a 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 preparaçã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: Analise as seguintes diretrizes específicas de tecnologia para o driver.

Sistemas de Arquivos

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

Introdução à segurança dos 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 de 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: Analise as diretrizes de criação e instalação de informações de driver para saber se você está seguindo as práticas recomendadas.

Ao criar o código que instala seu driver, você deve sempre realizar a instalação do seu dispositivo de maneira segura. Uma instalação segura de dispositivo 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:

Criar instalações de dispositivos seguros

Diretrizes para usar SetupAPI

Usar funções de instalação de dispositivo

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

Faça revisão de código por pares

Item 11 da lista de verificação de segurança: Faça a revisão de código por pares para procurar problemas não identificados por outras ferramentas e processos

Busque revisores de código experientes que consigam detectar problemas que você possa ter deixado passar. Um segundo olhar geralmente encontra problemas que você pode ter esquecido.

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

Execute a assinatura adequada do driver da 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 disponibilizar publicamente um pacote de driver, 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.

Use 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.

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

Adicione 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.

O SAL (linguagem de anotação de código-fonte) fornece um conjunto de anotações para descrever como uma função usa seus parâmetros, as suposições que ela faz sobre eles e as garantias obtidas ao concluir. 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 de SAL para reduzir defeitos de código do 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/

Use o Driver Verifier para verificar vulnerabilidades

Item 15 da lista de verificação de segurança: Use o Driver Verifier para verificar vulnerabilidades no código do driver.

O Driver Verifier 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 possíveis bugs nos drivers.

O Driver Verifier possibilita testar o driver em tempo real. 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 Driver Verifier pode submeter drivers do Windows a uma variedade de pressões e testes para encontrar comportamentos inadequados. Para obter mais informações, consulte Driver Verifier.

Observe que o DV suporta apenas determinados tipos de drivers. 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 do 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 toaster 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 estão configuradas para minimizar problemas de segurança conhecidos.

Use o BinSkim para examinar arquivos binários e identificar práticas de codificação e compilação que podem 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, pois isso maximiza o uso das mitigações de segurança atuais no nível do compilador e fornecidas pelo sistema operacional.
  • Configurações de compilação desprotegidas – 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 devem ser assinados com algoritmos de criptografia forte.

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 doBinSkim.

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 fazer o download 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. Acesse 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. Acesse o novo diretório binskim criado pelo comando clone.

    C:\> Cd \binskim-master\binskim
    
  8. Execute BuildAndTest.cmd para 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. Acesse 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 da 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.

...

Definir 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 dele, 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 entender todos os riscos possíveis 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 saber mais sobre sympath, consulte Caminho de símbolo para depuradores do 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 .sys do driver compilado.

    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 detalhes, verá apenas os defeitos detectados pelo BinSkim. A opção --verbose normalmente não é recomendada para sistemas de automação reais, pois ela aumenta o tamanho dos arquivos de log e dificulta a detecção de 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 pelo 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 - 'declaration': 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.

Verifique 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 usa 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 que podem ser úteis para buscar comportamentos associados a vulnerabilidades de código do driver:

DF: Teste de IOCTL aleatório de fuzzing (Confiabilidade)

DF: Teste subaberto de fuzzing (Confiabilidade)

DF: Teste de FSCTL de fuzzing do buffer de comprimento zero (Confiabilidade)

DF: Teste de FSCTL aleatório de fuzzing (Confiabilidade)

DF: Teste de API diverso de fuzzing (Confiabilidade)

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

Os testes de CHAOS (Concurrent Hardware and Operating System) verificam o driver PnP, testes de fuzzing de driver de dispositivo e testes de sistema de energia simultaneamente. Para obter mais informações, consulte Testes de CHAOS (fundamentos do dispositivo).

Os testes de penetração de fundamentos 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 (fundamentos 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 que conheçam análise e prevenção de invasão de segurança.

Analise técnicas e extensões do depurador

Item 18 da lista de verificação de segurança: Analise 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 Determinar 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.

Microsoft Vulnerable and Malicious Driver Reporting Center

Qualquer pessoa pode enviar um driver suspeito 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 Microsoft Vulnerable and Malicious Driver Reporting Center

O Reporting Center 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 drivers vulneráveis são confirmados, eles são notificados adequadamente e adicionados à lista de bloqueio de drivers vulneráveis. Para obter mais informações sobre esse assunto, 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.

Examine os recursos de codificação segura

Item 19 da lista de verificação de segurança: Examine esses recursos para entender melhor as práticas recomendadas de codificação segura aplicáveis aos desenvolvedores de driver.

Analise estes recursos para saber mais sobre segurança do driver

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

Criar drivers confiáveis no modo kernel

Organizações de codificação segura

Carnegie Mellon University SEI CERT

Carnegie Mellon University SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition).

MITRE - Weaknesses Addressed by the CERT C Secure Coding Standard

Building Security In Maturity Model (BSIMM) - https://www.bsimm.com/

SAFECode - https://safecode.org/

Recursos 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 - Como tornar os objetos de dispositivo acessíveis... e SAFE

You've Gotta Use Protection -- Inside Driver & Device Security

Locking Down Drivers - A Survey of Techniques

Meltdown and Spectre: What about drivers?

Estudo de caso

From alert to driver vulnerability: Microsoft Defender ATP investigation unearths privilege escalation flaw

Manuais

24 deadly sins of software security : programming flaws and how to fix them de Michael Howard, David LeBlanc e John Viega

The art of software security assessment : identifying and preventing software vulnerabilities, Mark Dowd, John McDonald e Justin Schuh

Writing Secure Software Second Edition, Michael Howard e David LeBlanc

The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities, Mark Dowd e John McDonald

Secure Coding in C and C++ (SEI Series in Software Engineering) 2nd Edition, Robert C. Seacord

Programming the Microsoft Windows Driver Model (2nd Edition), Walter Oney

Developing Drivers with the Windows Driver Foundation (Developer Reference), Penny Orwick e Guy Smith

Treinamento

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

O treinamento online de codificação segura está disponível em várias fontes. Por exemplo, este curso está disponível na Coursera em:

Identificando vulnerabilidades de segurança na programação do 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. Problemas ao executar no kernel podem expor todo o sistema operacional. Por isso, preste muita atenção à segurança do driver e projete sempre pensando em segurança.

  • Aplique o princípio de privilégios mínimos:

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

    b. Restrinja ainda mais os IOCTLs individuais

  • Crie um modelo de ameaça para identificar vetores de ataque e pense em outras restrições que podem ajudar.

  • 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 except, e são propensos a problemas de tempo de verificação/tempo 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 possíveis problemas identificados.

  • Busque revisores de código experientes que consigam detectar problemas que você possa ter deixado passar.

  • Use o Driver Verifier e teste o driver com várias entradas, incluindo casos menos frequentes.