Lista de verificação de segurança do condutor
Este artigo fornece uma lista de verificação de segurança para desenvolvedores de drivers, ajudando a reduzir o risco de comprometimento dos mesmos.
Visão geral da segurança do controlador
Uma falha de segurança é qualquer falha que permite que um invasor faça com que um driver funcione mal de tal forma que faça com que o sistema falhe 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 é liberado, 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, a fim de minimizar a probabilidade de tais vulnerabilidades. O objetivo é eliminar todas as falhas de segurança conhecidas antes que o driver seja liberado.
Criar drivers mais seguros requer a cooperação do arquiteto do sistema (pensando conscientemente em ameaças potenciais ao driver), do desenvolvedor que implementa o código (codificando defensivamente operações comuns que podem ser a fonte de exploits) e da equipe de teste (tentando proativamente encontrar fraquezas e vulnerabilidades). Ao coordenar adequadamente todas estas atividades, a segurança do condutor é dramaticamente melhorada.
Além de evitar os problemas associados a um driver sendo atacado, muitas das etapas descritas, como o uso mais preciso da memória do kernel, aumentarão a confiabilidade do seu driver. Isso reduzirá os custos de suporte e aumentará a satisfação do cliente com seu produto. Completar as tarefas na lista de verificação abaixo ajudará a alcançar todos esses objetivos.
Lista de verificação de segurança:Conclua a tarefa de segurança descrita em cada um destes tópicos.
Confirme se é necessário um controlador de kernel
Use as estruturas de controladores
Controlar o acesso apenas aos drivers de software
Não assine o código do driver de teste para produção
Siga as diretrizes de codificação segura do driver
Implementar código compatível com HVCI
Siga as práticas de código recomendadas específicas para a tecnologia
Executar revisão de código por pares
Gerir o controlo de acesso do motorista
Melhorar a segurança de instalação do dispositivo
Assine corretamente o driver de liberação
Use o CodeQL para verificar o código do driver
Adicione anotações SAL ao seu código de driver
Use o Verificador de Driver para verificar se há vulnerabilidades
Verificar o código com o BinSkim Binary Analyzer
Verifique o código com os testes do programa de compatibilidade de hardware
Reveja recursos de codificação seguros
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 serviço ou aplicativo do Windows, não é uma opção melhor.
Os drivers vivem 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á menor custo e terá menos risco associado do que criar um novo driver 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 Services.
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 seu código e aumentar sua confiabilidade e segurança.
Use as Estruturas de Driver do Windows para reduzir o tamanho do seu código e aumentar a sua confiabilidade e segurança. Para começar, revise Usando o WDF para criar um controlador. Para obter informações sobre como utilizar o driver do framework UMDF em modo de utilizador com menor risco, consulte Escolhendo um modelo de driver.
Escrever um driver
O código-fonte do Windows Driver Framework é de código aberto e está disponível no GitHub. Este é o mesmo código-fonte a partir do qual a biblioteca de tempo de execução WDF fornecida no Windows 10 é criada. Você pode depurar seu driver de forma mais eficaz quando puder acompanhar as interações entre o driver e o WDF. Faça o download de https://github.com/Microsoft/Windows-Driver-Frameworks.
Controle o acesso apenas aos drivers de software
Item #3 da lista de verificação de segurança:Se um driver somente de software vai ser criado, controle de acesso adicional deve ser implementado.
Os controladores de núcleo baseados apenas em software não utilizam o plug-and-play (PnP) para se associarem a IDs de hardware específicos e podem ser executados em qualquer PC. Tal driver poderia 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 um ID PnP exclusivo para habilitar a criação de um driver PnP ou verificando a tabela SMBIOS quanto à presença de hardware específico).
Por exemplo, imagine que a Fabrikam OEM deseja distribuir um driver que habilita um utilitário de overclocking para seus sistemas. Se este driver somente de software fosse executado em um sistema de um OEM diferente, a instabilidade do sistema ou danos poderiam resultar. Os sistemas da Fabrikam devem incluir um ID PnP exclusivo para permitir a criação de um driver PnP que também seja atualizável através 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 quaisquer recursos).
Não enviar código de teste para produção
Item #4 da lista de verificação de segurança:Não assine digitalmente em produção o código de desenvolvimento, teste e fabricação de drivers do kernel.
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 pelo Windows. O mecanismo correto para executar 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 por makecert.exe).
O código assinado por um certificado de editores de software confiável (SPC) ou assinatura do Windows Hardware Quality Labs (WHQL) não deve facilitar o desconhecimento da integridade do código do Windows e das tecnologias de segurança. Antes de o código ser assinado por uma assinatura confiável SPC ou WHQL, primeiro verifique se está em conformidade com as orientações de Criar Drivers Confiáveis Kernel-Mode. Além disso, o código não deve conter quaisquer comportamentos perigosos, descritos abaixo. Para obter mais informações sobre a assinatura de driver, consulte Release driver signing mais adiante neste artigo.
Exemplos de comportamento perigoso incluem o seguinte:
- Fornecendo a capacidade de mapear o kernel arbitrário, memória física ou de dispositivo para o modo de utilizador.
- Fornecer a capacidade de ler ou escrever no núcleo, memória física ou de dispositivo de forma arbitrária, 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 seu driver.
Ao considerar a segurança, uma metodologia comum é criar modelos de ameaça 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 potenciais vetores de ataque contra um driver com antecedência. Tendo identificado ameaças potenciais, um desenvolvedor de driver pode considerar meios de se defender contra essas ameaças, a fim de reforçar a segurança geral do componente de driver.
Este artigo fornece orientação específica para drivers na criação de 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 controlador que pode ser usado como um ponto de partida para o seu controlador.
As práticas recomendadas do Security Development Lifecycle (SDL) 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 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 principal atividade 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 rigoroso do uso da memória para evitar problemas com outras entidades a sobrescreverem ou de outra forma comprometerem as posições de memória que o seu driver utiliza.
Ferramentas de verificação 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 devolvê-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 Variable-Length. Para obter mais informações sobre como trabalhar com buffers e usar ProbeForRead e ProbeForWrite para validar o endereço de um buffer, consulte Buffer Handling.
Use o método apropriado para acessar buffers de dados com IOCTLs
Uma das principais responsabilidades de um driver do Windows é a transferência de dados entre aplicativos de modo de usuário e 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 mais informações |
---|---|---|
Método com Buffer | Recomendado para a maioria das situações | Usando E/S bufferizada |
METHOD_IN_DIRECT ou METHOD_OUT_DIRECT | Usado em algumas interfaces de entrada/saída de hardware (E/S HW) de alta velocidade. | Usando E/S Direta |
METHOD_NEITHER | Evite, se possível | usando de 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, há riscos, como ponteiros incorporados que devem ser mitigados.
Para obter mais informações sobre como trabalhar com buffers em IOCTLs, consulte Métodos para Aceder Buffers de Dados.
Erros na utilização de E/S em modo de buffer IOCTL
Verifique o tamanho dos buffers relacionados à 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 buffers de comprimento variável. Para obter mais informações, consulte Falha ao validar buffers de Variable-Length.
Ao usar E/S em buffer, certifique-se de retornar o comprimento adequado para o OutputBuffer no campo Informação da estrutura IO_STATUS_BLOCK. Não retorne apenas diretamente o comprimento de uma solicitação READ. Por exemplo, considere uma situação em que os dados retornados do espaço de utilizador indicam que há um buffer de 4K. Se o driver realmente deve retornar apenas 200 bytes, mas em vez disso apenas retorna 4K no campo Informações, ocorreu uma vulnerabilidade de divulgação não autorizada 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 é zero. Assim, o aplicativo do usuário recebe de volta 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 IOCTL
Lide corretamente com buffers de comprimento zero. Para obter mais informações, consulte Erros de no Direct I/O.
Erros na referência a endereços de espaço do usuário
Valide ponteiros incorporados em solicitações de E/S em buffer. Para obter mais informações, consulte Erros na referência de endereços User-Space.
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
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 deve sempre verificar se o registro para ler ou gravar está restrito ao índice ou intervalo esperado.
Para mais informações e exemplos de código, consulte Fornecer a capacidade de ler/escrever MSRs em Práticas recomendadas para restringir o comportamento de alto privilégio em drivers de modo kernel.
Vulnerabilidades do TOCTOU
Há uma vulnerabilidade potencial de tempo de verificação ao tempo de uso (TOCTOU) ao usar E/S direto (para IOCTLs ou para Leitura/Gravação). Esteja ciente 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ó é acessível a partir do modo kernel (como a pilha ou o pool). Em seguida, uma vez que os dados não podem ser acessados pelo aplicativo do usuário, valide e opere com base 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 não executável (NX). 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 controladores de dispositivo devem lidar adequadamente com várias solicitações no modo de utilizador, assim como pedidos de E/S de kernel para kernel.
Para permitir que os drivers suportem 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.
Puxadores
- Valide manipuladores passados entre a memória em modo de utilizador e a memória em modo kernel. Para obter mais informações, consulte Gestão de Manipuladores e Falha em Validar Manipuladores de Objetos.
Objetos de dispositivo
Proteja os objetos do 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 WDF é que os drivers WDF normalmente não acessam diretamente IRPs. Por exemplo, a estrutura converte os IRPs WDM que representam operações de controle de E/S de leitura, gravação e dispositivo em objetos de solicitação de estrutura que KMDF/UMDF recebem em filas de E/S.
Se você estiver escrevendo um driver WDM, revise as diretrizes a seguir.
Gerencie corretamente os buffers de E/S de IRP
Os seguintes artigos fornecem informações sobre como validar valores de entrada IRP:
DispatchReadWrite usando E/S em modo bufferizado
DispatchReadWrite utilizando I/O direto
Erros de de E/S direta
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, esteja ciente 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 finalizar um IRP com um valor de status de STATUS_SUCCESS a menos que este realmente 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 o estado pendente do IRP do driver
O driver deve marcar o IRP como pendente antes de salvar o IRP e deve considerar incluir a chamada para IoMarkIrpPending e a atribuição numa sequência sincronizada. 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á em pausa.
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 lida com operações de cancelamento podem passar despercebidos por um longo tempo, porque esse código normalmente não é executado com freqüência em um sistema em execução. Certifique-se de ler e entender todas as informações fornecidas em Cancelando IRPs. Preste especial atenção à sincronização do cancelamento de IRP e aos pontos a considerar ao cancelar IRPs.
Uma maneira recomendada de minimizar os problemas de sincronização associados às operações de cancelamento é implementar uma fila de IRP segura para cancelamento .
Garanta o tratamento adequado das operações de limpeza e encerramento de IRP.
Certifique-se de que compreende a diferença entre IRP_MJ_CLEANUP e IRP_MJ_CLOSE requisições. As solicitações de limpeza chegam depois que um aplicativo fecha todas as alças em um objeto de arquivo, mas às vezes antes de todas as solicitações de E/S serem 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:
DispatchCreate, DispatchClose e DispatchCreateClose rotinas
Rotinas de Limpeza de Despacho
Erros na Manipulação de Operações de Limpeza e Fechamento
Para obter mais informações sobre como manipular IRPs corretamente, consulte Erros adicionais no tratamento de IRPs.
Outras questões de segurança
Use um bloqueio ou uma sequência intertravada para evitar condições de corrida. Para obter mais informações, consulte erros em um ambiente multiprocessador.
Certifique-se de que os drivers de dispositivo lidam adequadamente com várias requisições de modo de utilizador, bem como requisições de E/S 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 uso.
Use funções seguras
Use funções de cadeia de caracteres seguras. Para obter mais informações, consulte Usando funções de cadeia de caracteres seguras.
Use funções aritméticas seguras. Para obter mais informações, consulte Safe Integer Library Routines
Use funções de conversão seguras.
Vulnerabilidades de código adicionais
Além das possíveis vulnerabilidades abordadas aqui, este artigo fornece informações adicionais sobre como melhorar a segurança do código do driver do modo kernel: Criando drivers Kernel-Mode confiáveis.
Para obter informações adicionais sobre codificação segura C e C++, consulte Recursos de codificação segura no final deste artigo.
Gerenciar controle de acesso do motorista
Item #7 da lista de verificação de segurança:Revise seu driver para certificar-se de que você está controlando corretamente o acesso.
Gestão do controlo de acesso dos controladores - WDF
Os drivers devem trabalhar para evitar que os usuários acessem inadequadamente os dispositivos e arquivos de um computador. Para impedir o acesso não autorizado a dispositivos e ficheiros, deve:
Nomeie objetos de dispositivo somente quando necessário. Os objetos de dispositivo nomeados geralmente só são necessários 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.
Especificar uma cadeia de caracteres SDDL no seu INF é uma das melhores maneiras de controlar o acesso ao PDO. 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 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 a dispositivos em drivers KMDF
Nomes, Descritores de Segurança e Classes de Dispositivos - Tornando os Objetos de Dispositivo Acessíveis... e SEGURO de janeiro e fevereiro de 2017 o NT Insider Newsletter publicado por OSR.
Gerir o controlo de acesso do controlador - 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. Quando implementar IoCreateDeviceSecure, especifique sempre um GUID de classe personalizada para o DeviceClassGuid. Você não deve especificar um GUID de classe existente aqui. Isso tem o potencial de quebrar as configurações de segurança ou compatibilidade para 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 drivers
Hierarquia de risco dos identificadores de segurança (SIDs)
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 for Device Objects, SID Stringse SDDL String Syntax.
É importante entender que, se chamadores com privilégios mais baixos tiverem permissão para acessar o kernel, o risco de código será aumentado. Neste diagrama de resumo, o risco aumenta à medida que você permite que SIDs com privilégios mais baixos acessem a funcionalidade 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 WDM
Para gerenciar ainda mais a segurança quando IOCTLs são enviadas por chamadores de modo de usuário, o código do driver pode incluir a função
A implementação de um controle de segurança IOCTL granular não substitui a necessidade de gerenciar o acesso ao driver usando as técnicas discutidas acima.
Para obter mais informações, consulte os seguintes artigos:
Definição de 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 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 Integridade de Código (CI) do resto do sistema operacional. Ao usar a segurança baseada em virtualização para isolar a CI, a única maneira pela qual a memória do kernel pode 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, certifique-se de que o código do driver faça o seguinte:
- Opta pelo 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 executável do sistema
- 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 dos fundamentos do sistema, consulte o teste de prontidão de integridade de código do HyperVisor e a integridade de código (HVCI) Hypervisor-Protected.
Siga as melhores práticas de código específicas para a tecnologia
Item #9 da lista de verificação de segurança:Reveja as seguintes orientações específicas da tecnologia para o seu motorista.
Sistemas de arquivos
Para obter mais informações, sobre segurança de driver de sistema de arquivos, consulte os seguintes artigos:
Introdução à Segurança dos Sistemas de Ficheiros
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.
Ecrã
Para obter informações sobre a segurança do driver de vídeo, consulte <Conteúdo pendente>.
Impressoras
Para obter informações relacionadas à segurança do driver de impressora, consulte V4 Printer Driver Security Considerations.
Problemas de segurança para drivers do Windows Image Acquisition (WIA)
Para obter informações sobre segurança WIA, consulte Problemas de Segurança para Controladores de Aquisição de Imagem do Windows (WIA).
Melhore a segurança da instalação do dispositivo
Item #10 da lista de verificação de segurança:Revise as diretrizes de criação e instalação do driver inf para certificar-se de que você está seguindo as práticas recomendadas.
Quando você cria o código que instala o driver, você deve certificar-se de que a instalação do seu dispositivo será sempre realizada de forma 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 do driver contra modificação ou exclusão
- Limita o acesso às entradas de registo do dispositivo
- Limita o acesso às classes WMI do dispositivo
- Usa funções SetupAPI corretamente
Para obter mais informações, consulte os seguintes artigos:
Criação de instalações seguras de dispositivos
Diretrizes para usar o SetupAPI
Utilização de funções para instalação de dispositivos
Tópicos avançados de instalação de dispositivos e drivers
Fazer revisão de código por pares
Item #11 da lista de verificação de segurança:Executar revisão de código de mesmo nível, para procurar problemas não surgidos pelas outras ferramentas e processos
Procura revisores de código experientes para identificar problemas que possas ter perdido. Um segundo conjunto de olhos muitas vezes verá problemas que você pode ter ignorado.
Se você não tiver uma equipe adequada para revisar seu código internamente, considere contratar ajuda externa para essa finalidade.
Realize a assinatura correta do driver de lançamento
Item #12 da lista de verificação de segurança:Use o portal de parceiros do Windows para assinar corretamente seu 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 Teste de desempenho e compatibilidade, Introdução ao programa de hardware, Hardware Dashboard Servicese Assinatura de atestado de um driver de kernel para lançamento público.
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, por GitHub, é um mecanismo de análise de código semântico, e a combinação de um extenso conjunto de consultas de segurança, juntamente com uma plataforma robusta, torná-lo uma ferramenta inestimável para proteger o código de driver. Para obter mais informações, consulte CodeQL e o Teste de Logotipo de Ferramentas Estáticas.
Adicionar anotações SAL ao seu código de driver
Item #14 da lista de verificação de segurança:Adicione anotações SAL ao seu código de driver.
A linguagem de anotação de código-fonte (SAL) fornece um conjunto de anotações que você pode usar para descrever como uma função usa seus parâmetros, as suposições que faz sobre eles e as garantias que faz quando termina. 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 SAL para modificar sua análise de funções. Para obter mais informações sobre o desenvolvimento de drivers SAL 2.0 para Windows, consulte Anotações SAL 2.0 para drivers do 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 em OSR. https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/
Use 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.
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. DV encontra defeitos no código do driver que podem apontar para possíveis bugs nos drivers.
Driver Verifier permite o teste ao vivo do motorista. Driver Verifier monitora drivers do Windows em modo núcleo e drivers gráficos para detetar chamadas de função ilegais ou ações que podem corromper o sistema. Driver Verifier pode sujeitar os drivers do Windows a uma variedade de tensões e testes para encontrar um comportamento inadequado. Para obter mais informações, consulte Verificador de Controlador.
Observe que apenas certos tipos de drivers são suportados pelo DV. Para obter mais informações sobre os drivers que o DV pode verificar, consulte Drivers suportados. Consulte as páginas seguintes para obter informações sobre os testes DV disponíveis para o tipo de driver com o qual você está trabalhando.
- Regras para Drivers WDM
- Regras para Controladores KMDF
- Regras para Controladores NDIS
- Regras para Drivers Storport
- Regras para Drivers de Áudio
- Regras para Drivers AVStream
Para te familiarizares com o DV, podes usar um dos drivers de exemplo (por exemplo, o exemplo de torradeira destacado: 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 BinSkim para verificar se as opções de compilação e compilação estão configuradas para minimizar problemas de segurança conhecidos.
Use 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.
BinSkim verifica:
- Uso de conjuntos de ferramentas de compilador desatualizados - Os binários devem ser compilados em relação aos conjuntos de ferramentas de compilador mais recentes sempre que possível para maximizar o uso de 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 permitir 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.
BinSkim é uma ferramenta de código aberto e gera arquivos de saída que usam o formato Static Analysis Results Interchange Format (SARIF). BinSkim substitui a antiga ferramenta BinScope.
Para obter mais informações sobre BinSkim, consulte Utilize o BinSkim para verificar binários e o Guia do Usuário BinSkim.
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 aproveita os testes no Windows Hardware Lab Kit (HLK). Os testes HLK Device Fundamentals podem ser usados na linha de comando para exercitar o código do driver e investigar pontos fracos. Para obter informações gerais sobre os testes de fundamentos 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 para alguns comportamentos associados a vulnerabilidades de código:
DF - Teste IOCTL aleatório Fuzz (Confiabilidade)
DF - Teste de subabertura difusa (Confiabilidade)
DF - Teste FSCTL de buffer de comprimento zero Fuzz (Confiabilidade)
DF - Teste FSCTL aleatório Fuzz (Confiabilidade)
DF - Teste Fuzz Misc API (Confiabilidade)
Você também pode usar o difusor de atraso de sincronização do kernel
Os testes CHAOS (Concurrent Hardware and Operating System) executam vários testes de driver PnP, testes de difusão de driver de dispositivo e testes de sistema de energia simultaneamente. Para obter mais informações, consulte CHAOS Tests (Device Fundamentals).
Os testes de penetração de fundamentos de 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 em interfaces de software. Para obter mais informações, consulte Testes de Intrusão (Fundamentos de Dispositivos).
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 recursos de desenvolvimento não relacionados familiarizados com o tipo específico de driver que está sendo desenvolvido e uma ou mais pessoas familiarizadas com análise e prevenção de invasões de segurança.
Entenda como os drivers são relatados usando o Centro de Relatórios de Drivers Vulneráveis e Mal-Intencionados da Microsoft
Item #18 da lista de verificação de segurança: Entenda como os drivers são relatados usando o Centro de Relatórios de Drivers Vulneráveis e Mal-Intencionados da Microsoft
Qualquer pessoa pode enviar um driver questionável usando o Centro de Relatório de Driver Vulnerável e Mal-Intencionado da Microsoft. 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
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 drivers vulneráveis são confirmados, uma notificação apropriada ocorre, eles são adicionados à lista de bloqueio 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 protegido pelo hipervisor) e ao Windows 10 no modo S.
Revise os recursos de codificação seguros
Item #19 da lista de verificação de segurança:Revise esses recursos para expandir sua compreensão das práticas recomendadas de codificação segura aplicáveis aos desenvolvedores de drivers.
Diretrizes para codificação de drivers em modo kernel de forma segura
Criação de drivers de Kernel-Mode confiáveis
Organizações de codificação seguras
Universidade Carnegie Mellon SEI CERT
Carnegie Mellon University SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (Edição de 2016).
MITRE - pontos fracos abordados pelo CERT C Secure Coding Standard
Segurança Predial no Modelo de Maturidade (BSIMM) - https://www.bsimm.com/
SAFECode - https://safecode.org/
OSR
OSR fornece treinamento de desenvolvimento de motoristas e serviços de consultoria. Os artigos do boletim informativo da OSR destacam problemas de segurança dos drivers.
Você tem que usar a proteção -- Inside Driver & Device Security
Locking Down Drivers - Um Levantamento de Técnicas
Meltdown e Spectre: E os motoristas?
Estudo de caso
Livros
24 pecados mortais 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
Programação do Microsoft Windows Driver Model (2ª Edição), Walter Oney
Desenvolvendo drivers com o Windows Driver Foundation (Referência do desenvolvedor), Penny Orwick e Guy Smith
Formação
O treinamento em sala de aula de drivers do Windows está disponível em fornecedores como os seguintes:
O treinamento on-line de codificação segura está disponível em uma variedade de fontes. Por exemplo, este curso está disponível no Coursera.
Identificação de vulnerabilidades de segurança node programação C/C++.
O SAFECode também oferece formação gratuita:
Certificação Profissional
A CERT oferece uma certificação Secure Coding Professional.
Resumo das principais conclusões
A segurança dos motoristas é uma tarefa complexa que contém muitos elementos, mas aqui estão alguns pontos-chave a considerar:
Os drivers vivem 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 ao design com a segurança em mente.
Aplicar o princípio do menor privilégio:
um. Use uma cadeia de caracteres SDDL estrita para restringir o acesso ao driver
b. Restringir ainda mais as IOCTL 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 incorporados que estão a ser passados do modo utilizador. Eles precisam ser sondados e acessados num bloco try except, e são propensos a problemas de tempo de verificação de tempo de uso (ToCToU), a menos que o valor do buffer seja capturado e comparado.
Se não tiveres certeza, usa METHOD_BUFFERED como método de buffer de IOCTL.
Use utilitários de verificação 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ê pode ter perdido.
Use verificadores de controladores e teste o seu controlador com várias entradas, incluindo casos limite.