Proteção de Fluxo de Controle para segurança da plataforma
O que é a Proteção de Fluxo de Controle?
O CFG (Proteção de Fluxo de Controle) é um recurso de segurança de plataforma altamente otimizado que foi criado para combater as vulnerabilidades de corrupção da memória. Ao impor restrições rígidas sobre o local de onde um aplicativo pode executar o código, fica muito mais difícil para as explorações executarem códigos arbitrários por meio de vulnerabilidades, como estouro de buffer. A CFG estende as tecnologias anteriores de mitigação de exploração, como /GS (Verificação de Segurança do Buffer), Prevenção de Execução de Dados (DEP) e Randomização de Layout de Espaço de Endereço (ASLR).
O uso da CFG pode ajudar a:
- Impedir a corrupção de memória e os ataques de ransomware.
- Restringir os recursos do servidor apenas ao que é necessário em um determinado momento para reduzir a superfície de ataque.
- Dificultar a exploração de código arbitrário por meio de vulnerabilidades, como estouros de buffer.
Esse recurso está disponível no Microsoft Visual Studio e é executado em versões com Reconhecimento de CFG do Windows; Windows 10 e Windows 11 no cliente e Windows Server 2019 e posterior no lado do servidor.
Os desenvolvedores são altamente incentivados a habilitar a CFG para seus aplicativos. Você não precisa habilitar o CFG para cada parte do código, pois uma combinação de código habilitado e não habilitado para CFG será executada corretamente. No entanto, não habilitar CFG para todo o código pode abrir lacunas na proteção. Além disso, o código habilitado para CFG funciona bem em versões sem reconhecimento de CFG do Windows e, portanto, é totalmente compatível com elas.
Como posso habilitar a CFG?
Na maioria dos casos, não há necessidade de alterar o código-fonte. Tudo o que você precisa fazer é adicionar uma opção ao seu projeto do Visual Studio, e o compilador e o vinculador habilitarão a CFG.
O método mais simples é ir para Projeto | Propriedades | Propriedades de Configuração | C/C++ | Geração de Código e escolher Sim (/guard:cf) para Proteção de Fluxo de Controle.
Como alternativa, adicione /guard:cf a Projeto | Propriedades | Propriedades de Configuração | C/C++ | Linha de Comando | Opções Adicionais (para o compilador) e /guard:cf a Projeto | Propriedades | Propriedades de Configuração | Vinculador | Linha de Comando | Opções Adicionais (para o vinculador).
Confira /guard (Habilitar Proteção de Fluxo de Controle) para obter informações adicionais.
Se estiver criando seu projeto a partir da linha de comando, você poderá adicionar as mesmas opções. Por exemplo, se você estiver compilando um projeto chamado test.cpp, use cl /guard:cf test.cpp /link /guard:cf.
Você também tem a opção de controlar dinamicamente o conjunto de endereços de destino icall que são considerados válidos pela CFG usando SetProcessValidCallTargets da API de Gerenciamento de Memória. A mesma API pode ser usada para especificar se as páginas são destinos válidos ou inválidos para CFG. Por padrão, as funções VirtualProtect e VirtualAlloc tratarão uma região especificada de páginas executáveis e confirmadas como destinos de chamada indireta válidos. É possível substituir esse comportamento, como ao implementar um compilador Just-in-Time, especificando PAGE_TARGETS_INVALID ao chamar VirtualAlloc ou PAGE_TARGETS_NO_UPDATE ao chamar VirtualProtect conforme detalhado em Constantes de Proteção de Memória.
Como saber se um binário está sob Proteção de Fluxo de Controle?
Execute a ferramenta dumpbin (incluída na instalação do Visual Studio) no prompt de comando do Visual Studio com as opções /headers e /loadconfig: dumpbin /headers /loadconfig test.exe. A saída para um binário na CFG deve mostrar que os valores de cabeçalho incluem "Guard" e que os valores de load config incluem "CF Instrumented" e "FID table present".
Como a CFG realmente funciona?
As vulnerabilidades de software são frequentemente exploradas, fornecendo dados improváveis, incomuns ou extremos para um programa em execução. Por exemplo, um invasor pode explorar uma vulnerabilidade de estouro de buffer fornecendo mais entrada para um programa do que o esperado, invadindo a área reservada pelo programa para ter uma resposta. Isso poderá corromper a memória adjacente que pode conter um ponteiro de função. Quando o programa chama essa função, ela pode ir para um local não intencional especificado pelo invasor.
No entanto, uma combinação potente de suporte a compilação e runtime da CFG implementa a integridade do fluxo de controle que restringe fortemente onde as instruções de chamada indireta podem ser executadas.
O compilador faz o seguinte:
- Adiciona verificações de segurança leves ao código compilado.
- Identifica o conjunto de funções no aplicativo que são destinos válidos para chamadas indiretas.
O suporte de tempo de execução, fornecido pelo kernel do Windows:
- Mantém com eficiência o estado que identifica destinos de chamada indireta válidos.
- Implementa a lógica que verifica se um destino de chamada indireta é válido.
Para ilustrar:
Quando uma verificação de CFG falhar em runtime, o Windows encerrará imediatamente o programa, interrompendo qualquer exploração que tente chamar indiretamente um endereço inválido.