Modelo de segurança do Windows para desenvolvedores de drivers
O modelo de segurança do Windows se baseia em objetos que podem ser protegidos. Cada componente do sistema operacional deve garantir a segurança dos objetos pelos quais é responsável. Os drivers, portanto, devem proteger a segurança de seus dispositivos e objetos de dispositivo.
Este tópico resume como o modelo de segurança do Windows se aplica aos drivers do modo kernel.
Modelo de segurança do Windows
O modelo de segurança do Windows se baseia principalmente em direitos por objeto, com um pequeno número de privilégios em todo o sistema. Os objetos que podem ser protegidos incluem, sem limitação, processos, threads, eventos e outros objetos de sincronização, bem como arquivos, diretórios e dispositivos.
Para cada tipo de objeto, os direitos genéricos de leitura, gravação e execução são mapeados em direitos específicos de objetos detalhados. Por exemplo, para arquivos e diretórios, os direitos possíveis incluem o direito de ler ou gravar o arquivo ou diretório, de ler ou gravar atributos de arquivos estendidos, de percorrer um diretório e de escrever o descritor de segurança de um objeto.
O modelo de segurança envolve os seguintes conceitos:
- Identificadores de segurança (SIDs)
- Tokens de acesso
- Descritores de segurança
- ACLs (Listas de Controle de Acesso)
- Privilégios
Identificadores de segurança (SIDs)
Um identificador de segurança (SID, também chamado de entidade) identifica um usuário, um grupo ou uma sessão de logon. Cada usuário tem um SID exclusivo, que é recuperado pelo sistema operacional no momento do logon.
Os SIDs são emitidos por uma autoridade, como o sistema operacional ou um servidor de domínio. Alguns SIDs são bem conhecidos e têm nomes, bem como identificadores. Por exemplo, o SID S-1-1-0 identifica Todos (ou Mundo).
Tokens de acesso
Todo processo tem um token de acesso. O token de acesso descreve o contexto de segurança completo do processo. Ele contém o SID do usuário, o SID dos grupos aos quais o usuário pertence e o SID da sessão de logon, bem como uma lista dos privilégios de todo o sistema concedidos ao usuário.
Por padrão, o sistema usa o token de acesso primário para um processo sempre que um thread do processo interage com um objeto que pode ser protegido. No entanto, um thread pode representar uma conta de cliente. Quando um thread se representa, ele tem um token de representação além de seu próprio token primário. O token de representação descreve o contexto de segurança da conta de usuário que o thread está representando. A representação é especialmente comum no tratamento de RPC (Chamada de Procedimento Remoto).
Um token de acesso que descreve um contexto de segurança restrito para um thread ou processo é chamado de token restrito. Os SIDs em um token restrito podem ser definidos apenas para negar acesso, não para permitir acesso, a objetos que podem ser protegidos. Além disso, o token pode descrever um conjunto limitado de privilégios em todo o sistema. O SID e a identidade do usuário permanecem os mesmos, mas os direitos de acesso do usuário são limitados enquanto o processo está usando o token restrito. A função CreateRestrictedToken cria um token restrito.
Descritores de segurança
Cada objeto nomeado do Windows tem um descritor de segurança; alguns objetos não nomeados também. O descritor de segurança descreve os SIDs de proprietário e grupo do objeto junto com suas ACLs.
O descritor de segurança de um objeto geralmente é criado pela função que cria o objeto. Quando um driver chama rotina IoCreateDevice ou IoCreateDeviceSecure para criar um objeto de dispositivo, o sistema aplica um descritor de segurança ao objeto de dispositivo criado e define ACLs para o objeto. Para a maioria dos dispositivos, as ACLs são especificadas no arquivo de informações do dispositivo (INF).
Para obter mais informações, consulte Descritores de segurança na documentação do driver do kernel.
Listas de Controle de Acesso
As ACLs (Listas de Controle de Acesso) permitem um controle detalhado do acesso a objetos. Uma ACL faz parte do descritor de segurança de cada objeto.
Cada ACL contém zero ou mais ACEs (Entradas de Controle de Acesso). Cada ACE, por sua vez, contém um único SID que identifica um usuário, grupo ou computador e uma lista de direitos que são negados ou permitidos para esse SID.
ACLs de objetos de dispositivo
A ACL de um objeto de dispositivo pode ser definida de três maneiras:
- No descritor de segurança padrão de seu tipo de dispositivo.
- Criada programaticamente pela função RtlCreateSecurityDescriptor e definida pela função RtlSetDaclSecurityDescriptor.
- Especificada em Linguagem de Definição do Descritor de Segurança (SDDL) no arquivo INF do dispositivo ou em uma chamada para a rotina IoCreateDeviceSecure.
Todos os drivers devem usar SDDL no arquivo INF para especificar ACLs para seus objetos de dispositivo.
SDDL é uma linguagem de descrição extensível que permite que os componentes criem ACLs em um formato de cadeia de caracteres. A SDDL é usada tanto pelo código de modo de usuário quanto pelo modo kernel. A figura a seguir mostra o formato de cadeias de caracteres SDDL para objetos de dispositivo.
O valor Access especifica o tipo de acesso permitido. O valor do SID especifica um identificador de segurança que determina a quem o valor de Access se aplica (por exemplo, um usuário ou grupo).
Por exemplo, a seguinte cadeia de caracteres SDDL permite o acesso do sistema (SY) a tudo e permite que todos os outros (WD) tenham somente acesso de leitura:
“D:P(A;;GA;;;SY)(A;;GR;;;WD)”
O arquivo de cabeçalho wdmsec.h também inclui um conjunto de cadeias de caracteres SDDL predefinidas que são adequadas para objetos de dispositivo. Por exemplo, o arquivo de cabeçalho define SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX assim:
"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)"
O primeiro segmento dessa cadeia de caracteres permite que o kernel e o sistema operacional (SY) tenham controle completo sobre o dispositivo. Com o segundo segmento, qualquer pessoa no grupo interno de administradores (BA) pode acessar o dispositivo inteiro, mas não pode alterar a ACL. O terceiro segmento permite que todos (WD) leiam ou gravem no dispositivo, e o quarto segmento concede os mesmos direitos a códigos não confiáveis (RC). Os drivers podem usar as cadeias de caracteres predefinidas como estão ou como modelos para cadeias de caracteres específicas do objeto de dispositivo.
Todos os objetos de dispositivo em uma pilha devem ter as mesmas ACLs. Alterar as ACLs em um objeto de dispositivo na pilha altera as ACLs em toda a pilha de dispositivos.
No entanto, adicionar um novo objeto de dispositivo à pilha não altera nenhuma ACL, seja a do novo objeto de dispositivo (se ele tiver ACLs) ou as de qualquer objeto de dispositivo existente na pilha. Quando um driver cria um novo objeto de dispositivo e o anexa à parte superior da pilha, o driver deve copiar as ACLs da pilha para o novo objeto de dispositivo copiando o campo DeviceObject.Characteristics do próximo driver inferior.
A rotina IoCreateDeviceSecure oferece suporte a um subconjunto de cadeias de caracteres SDDL que usam SIDs predefinidos, como WD e SY. APIs de modo de usuário e arquivos INF dão suporte à sintaxe SDDL completa.
Verificações de segurança usando ACLs
Quando um processo solicita acesso a um objeto, as verificações de segurança comparam as ACLs do objeto com os SIDs no token de acesso do autor da chamada.
O sistema compara os ACEs em uma ordem rigorosa de cima para baixo e é interrompido na primeira correspondência relevante. Portanto, ao criar uma ACL, você sempre deve colocar ACEs de negação acima das ACEs de concessão correspondentes. Os exemplos a seguir mostram como a comparação procede.
Exemplo 1: Comparar uma ACL a um token de acesso
O Exemplo 1 mostra como o sistema compara uma ACL ao token de acesso do processo de um autor de chamada. Imagine que o autor da chamada quer abrir um arquivo que tem a ACL mostrada na tabela a seguir.
Exemplo de ACL de arquivo
Permissão | SID | Access |
---|---|---|
Allow | Contabilidade | Gravação, exclusão |
Allow | Vendas | Acrescentar |
Negar | Ofício | Acrescentar, gravar, excluir |
Allow | Todos | Ler |
Essa ACL tem quatro ACEs, que se aplicam especificamente aos grupos Contabilidade, Vendas, Jurídico e Todos.
Em seguida, imagine que o token de acesso do processo de solicitação contém SIDs para um usuário e três grupos, na seguinte ordem:
Usuário Jim (S-1-5-21...)
Grupo Contabilidade (S-1-5-22...)
Grupo Jurídico (S-1-5-23...)
Grupo Todos (S-1-1-0)
Ao comparar uma ACL de arquivo com um token de acesso, o sistema primeiro procura uma ACE para o usuário Jim na ACL do arquivo. Nenhuma é exibida, portanto, em seguida ele procura uma ACE para o grupo Contabilidade. Como mostrado na tabela anterior, uma ACE para o grupo Contabilidade aparece como a primeira entrada na ACL do arquivo, portanto, o processo de Jim recebe o direito de gravar ou excluir o arquivo, e a comparação é interrompida. Se, em vez disso, a ACE do grupo Jurídico precedesse a ACE do grupo Contabilidade na ACL, o processo teria o acesso de gravação, acréscimo e exclusão ao arquivo negado.
Exemplo 2: Comparar uma ACL a um token restrito
O sistema compara uma ACL a um token restrito da mesma maneira que compara as que estão em um token não restrito. No entanto, um SID de negação em um token restrito pode corresponder apenas a uma ACE de negação em uma ACL.
O Exemplo 2 mostra como o sistema compara a ACL de um arquivo com um token restrito. Imagine que o arquivo tem a mesma ACL mostrada na tabela anterior. Neste exemplo, no entanto, o processo tem um token restrito que contém os seguintes SIDs:
Usuário Jim (S-1-5-21...) Negar
Grupo de Contabilidade (S-1-5-22...) Negar
Grupo Jurídico (S-1-5-23...) Negar
Grupo Todos (S-1-1-0)
A ACL do arquivo não lista o SID de Jim, portanto, o sistema prossegue para o SID do grupo Contabilidade. Embora a ACL do arquivo tenha uma ACE para o grupo Contabilidade, essa ACE permite acesso. Portanto, ela não corresponde ao SID no token restrito do processo, que nega acesso. Como resultado, o sistema prossegue para o SID do grupo Jurídico. A ACL do arquivo contém uma ACE para o grupo Jurídico que nega acesso. Portanto, o processo não pode gravar, anexar ou excluir o arquivo.
Privilégios
Um privilégio é o direito de um usuário executar uma operação relacionada ao sistema no computador local, como carregar um driver, alterar a hora ou desligar o sistema.
Os privilégios são diferentes dos direitos de acesso porque se aplicam a tarefas e recursos relacionados ao sistema, em vez de objetos, e porque são atribuídos a um usuário ou grupo por um administrador do sistema, e não pelo sistema operacional.
O token de acesso de cada processo contém uma lista dos privilégios concedidos ao processo. Os privilégios devem ser habilitados especificamente antes do uso. Para obter mais informações sobre privilégios, consulte Privilégios na documentação do driver do kernel.
Cenário do modelo de segurança do Windows: Criar um arquivo
O sistema usa as construções de segurança descritas no modelo de segurança do Windows sempre que um processo cria um identificador para um arquivo ou objeto.
O diagrama a seguir mostra as ações relacionadas à segurança que são acionadas quando um processo de modo de usuário tenta criar um arquivo.
O diagrama anterior mostra como o sistema responde quando um aplicativo de modo de usuário chama a função CreateFile. As notas a seguir referem-se aos números circulares na figura:
- Um aplicativo de modo de usuário chama a função CreateFile, passando um nome de arquivo válido do Microsoft Win32.
- O modo de usuário Kernel32.dll passa a solicitação para Ntdll.dll, que converte o nome Win32 em um nome de arquivo do Microsoft Windows NT.
- Ntdll.dll chama a função NtCreateFile com o nome de arquivo do Windows. Dentro de Ntoskrnl.exe, o Gerenciador de E/S manipula NtCreateFile.
- O Gerenciador de E/S reempacota a solicitação em uma chamada do Gerenciador de Objetos.
- O Gerenciador de Objetos resolve links simbólicos e garante que o usuário tenha direitos de travessia para o caminho no qual o arquivo será criado. Para obter mais informações, consulte Verificações de segurança no Gerenciador de Objetos.
- O Gerenciador de Objetos chama o componente do sistema que possui o tipo de objeto subjacente associado à solicitação. Para uma solicitação de criação de arquivo, esse componente é o Gerenciador de E/S, que possui objetos de dispositivo.
- O Gerenciador de E/S verifica o descritor de segurança do objeto de dispositivo em relação ao token de acesso do processo do usuário para garantir que o usuário tenha o acesso necessário ao dispositivo. Para obter mais informações, consulte Verificações de segurança no Gerenciador de E/S.
- Se o processo do usuário tiver o acesso necessário, o Gerenciador de E/S criará um identificador e enviará uma solicitação de IRP_MJ_CREATE ao driver do dispositivo ou sistema de arquivos.
- O driver executa verificações de segurança adicionais conforme necessário. Por exemplo, se a solicitação especificar um objeto no namespace do dispositivo, o driver deverá garantir que o autor da chamada tenha os direitos de acesso necessários. Para obter mais informações, consulte Verificações de segurança no driver.
Verificações de segurança no Gerenciador de Objetos
A responsabilidade pela verificação dos direitos de acesso pertence ao componente de nível mais alto que pode executar tais verificações. Se o Gerenciador de Objetos puder verificar os direitos de acesso do autor da chamada, ele fará isso. Caso contrário, o Gerenciador de Objetos passa a solicitação para o componente responsável pelo tipo de objeto subjacente. Esse componente, por sua vez, verifica o acesso, se puder. Caso contrário, ele passa a solicitação para um componente ainda mais baixo, como um driver.
O Gerenciador de Objetos verifica as ACLs em busca de tipos de objetos simples, como eventos e bloqueios mutex. Para objetos que têm um namespace, o proprietário do tipo executa verificações de segurança. Por exemplo, o Gerenciador de E/S é considerado o proprietário do tipo de objetos de dispositivo e de arquivo. Se o Gerenciador de Objetos localizar o nome de um objeto de dispositivo ou de arquivo ao analisar um nome, ele entregará o nome ao Gerenciador de E/S, como no cenário de criação de arquivo apresentado acima. Em seguida, o Gerenciador de E/S verifica os direitos de acesso, se possível. Se o nome especificar um objeto dentro de um namespace de dispositivo, o Gerenciador de E/S, por sua vez, entregará o nome ao driver do dispositivo (ou do sistema de arquivos) e esse driver será responsável por validar o acesso solicitado.
Verificações de segurança no Gerenciador de E/S
Quando o Gerenciador de E/S cria um identificador, ele verifica os direitos do objeto em relação ao token de acesso do processo e, em seguida, armazena os direitos concedidos ao usuário junto com o identificador. Quando as solicitações de E/S posteriores chegam, o Gerenciador de E/S verifica os direitos associados ao identificador para garantir que o processo tenha o direito de executar a operação de E/S solicitada. Por exemplo, se o processo solicitar posteriormente uma operação de gravação, o Gerenciador de E/S verificará os direitos associados ao identificador para garantir que o autor da chamada tenha acesso de gravação ao objeto.
Se o identificador estiver duplicado, os direitos poderão ser removidos da cópia, mas não adicionados a ela.
Quando o Gerenciador de E/S cria um objeto, ele converte modos de acesso Win32 genéricos em direitos específicos do objeto. Por exemplo, os seguintes direitos se aplicam a arquivos e diretórios:
modo de acesso Win32 | Direitos específicos do objeto |
---|---|
GENERIC_READ | ReadData |
GENERIC_WRITE | WriteData |
GENERIC_EXECUTE | ReadAttributes |
GENERIC_ALL | Tudo |
Para criar um arquivo, um processo deve ter direitos de travessia para os diretórios pai no caminho de destino. Por exemplo, para criar \Device\CDROM0\Directory\File.txt, um processo deve ter o direito de atravessar \Device, \Device\CDROM0 e \Device\CDROM0\Directory. O Gerenciador de E/S verifica apenas os direitos de travessia para esses diretórios.
O Gerenciador de E/S verifica os direitos de travessia quando analisa o nome do arquivo. Se o nome do arquivo for um link simbólico, o Gerenciador de E/S o resolverá para um caminho completo e, em seguida, verificará os direitos de travessia, começando pela raiz. Por exemplo, imagine que o link simbólico \DosDevices\D mapeia para o nome de dispositivo do Windows NT \Device\CDROM0. O processo deve ter direitos de travessia para o diretório \Device.
Para obter mais informações, consulte Identificadores de objeto e Segurança de objeto.
Verificações de segurança no driver
O kernel do sistema operacional trata cada driver, na verdade, como um sistema de arquivos com seu próprio namespace. Consequentemente, quando um autor de chamada tenta criar um objeto no namespace do dispositivo, o Gerenciador de E/S verifica se o processo tem direitos de travessia para os diretórios no caminho.
Com os drivers WDM, o Gerenciador de E/S não executa verificações de segurança no namespace, a menos que o objeto de dispositivo tenha sido criado especificando FILE_DEVICE_SECURE_OPEN. Quando FILE_DEVICE_SECURE_OPEN não está definido, o driver é responsável por garantir a segurança de seu namespace. Para obter mais informações, consulte Controlar o acesso ao namespace do dispositivo e Proteger objetos de dispositivo.
Para drivers WDF, o sinalizador FILE_DEVICE_SECURE_OPEN é sempre definido para que haja uma verificação do descritor de segurança do dispositivo antes de permitir que um aplicativo acesse quaisquer nomes dentro do namespace do dispositivo. Para obter mais informações, confira Controlar o acesso ao dispositivo em drivers KMDF.
Limites de segurança do Windows
Os drivers que se comunicam entre si e com os autores de chamada do modo de usuário de diferentes níveis de privilégio podem ser considerados como ultrapassando um limite de confiança. Um limite de confiança é qualquer caminho de execução de código que cruza de um processo com privilégios mais baixos para um processo com privilégios mais altos.
Quanto maior a disparidade nos níveis de privilégio, mais interessante é o limite para invasores que desejam executar ataques, como um ataque de escalonamento de privilégios em relação ao driver ou processo de destino.
Parte do processo de criação de um modelo de ameaça é analisar os limites de segurança e procurar caminhos imprevistos. Para obter mais informações, consulte Modelagem de ameaças para drivers.
Todos os dados que cruzam um limite de confiança não são confiáveis e devem ser validados.
Este diagrama mostra três drivers de kernel e dois aplicativos, um em um contêiner de aplicativo e um aplicativo executado com direitos de administrador. As linhas vermelhas indicam exemplos de limites de confiança.
Como o contêiner do aplicativo pode fornecer restrições adicionais e não está sendo executado no nível de administrador, o caminho (1) é de maior risco para um ataque de escalonamento, já que o limite de confiança é entre um contêiner de aplicativo (um processo de privilégios muito baixos) e um driver de kernel.
O caminho (2) é de menor risco, pois o aplicativo está sendo executado com direitos de administrador e está chamando diretamente para o driver do kernel. Administrador já é um privilégio bastante alto no sistema. Então, a superfície de ataque do administrador para o kernel é um alvo menos interessante para os invasores, mas ainda assim um limite de confiança notável.
O caminho (3) é um exemplo de caminho de execução de código que cruza vários limites de confiança que poderão ser perdidos se um modelo de ameaça não for criado. Neste exemplo, há um limite de confiança entre o driver 1 e o driver 3, pois o driver 1 recebe a entrada do aplicativo de modo de usuário e a passa diretamente para o driver 3.
Todas as entradas que chegam ao driver do modo de usuário não são confiáveis e devem ser validadas. Entradas vindas de outros drivers também podem não ser confiáveis, dependendo de o driver anterior ser apenas uma simples passagem (por exemplo, os dados foram recebidos pelo driver 1 do aplicativo 1, o driver 1 não fez nenhuma validação nos dados e apenas os passou para o driver 3). Não se esqueça de identificar todas as superfícies de ataque e limites de confiança e validar todos os dados que os cruzam, criando um modelo de ameaça completo.
Recomendações do Modelo de Segurança do Windows
- Defina ACLs padrão fortes em chamadas para a rotina IoCreateDeviceSecure.
- Especifique ACLs no arquivo INF para cada dispositivo. Essas ACLs podem afrouxar as ACLs padrão rígidas, se necessário.
- Defina a característica FILE_DEVICE_SECURE_OPEN para aplicar as configurações de segurança do objeto de dispositivo ao namespace do dispositivo.
- Não defina IOCTLs que permitam FILE_ANY_ACCESS, a menos que esse acesso não possa ser explorado maliciosamente.
- Use a rotina IoValidateDeviceIoControlAccess para reforçar a segurança em IOCTLS existentes que permitem FILE_ANY_ACCESS.
- Crie um modelo de ameaça para analisar os limites de segurança e procurar caminhos imprevistos. Para obter mais informações, consulte Modelagem de ameaças para drivers.
- Consulte Lista de verificação de segurança do driver para obter recomendações adicionais de segurança do driver.