Proteção de Fluxo de Controle para segurança da plataforma

O que é o Control Flow Guard?

O CFG (Control Flow Guard) é um recurso de segurança de plataforma altamente otimizado que foi criado para combater vulnerabilidades de corrupção de memória. Ao impor restrições rígidas de onde um aplicativo pode executar código, isso torna muito mais difícil para explorações executar código arbitrário por meio de vulnerabilidades como estouros de buffer. O CFG estende tecnologias de mitigação de exploração anteriores, como /GS, DEP e ASLR.

  • Evitar a corrupção de memória e ataques de ransomware.
  • Restrinja os recursos do servidor ao que for 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 2015 e é executado em versões com reconhecimento de CFG do Windows — as versões x86 e x64 para Desktop e Server de Windows 10 e Windows 8.1 Update (KB3000850).

Incentivamos fortemente os desenvolvedores a habilitar o 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. Mas 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 o CFG?

Na maioria dos casos, não é necessário alterar o código-fonte. Basta adicionar uma opção ao projeto do Visual Studio 2015 e o compilador e o vinculador habilitarão o CFG.

O método mais simples é navegar até Project | Propriedades | Propriedades de configuração | C/C++ | Geração de código e escolha Sim (/guard:cf) para Control Flow Guard.

propriedade cfg no Visual Studio

Como alternativa, adicione /guard:cf ao Project | Propriedades | Propriedades de configuração | C/C++ | Linha de Comando | Opções adicionais (para o compilador) e /guard:cf para Project | Propriedades | Propriedades de configuração | Vinculador | Linha de Comando | Opções adicionais (para o vinculador).

propriedade cfg paraa propriedade cfg do compilador para o vinculador

Consulte /guard (Habilitar o Control Flow Guard) para obter informações adicionais.

Se você estiver criando seu projeto a partir da linha de comando, 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 considerados válidos pelo CFG usando o SetProcessValidCallTargets da API de Gerenciamento de Memória. A mesma API pode ser usada para especificar se as páginas são destinos inválidos ou válidos para CFG. As funções VirtualProtect e VirtualAlloc tratarão por padrão uma região especificada de páginas executáveis e confirmadas como destinos de chamada indireto 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 faço para dizer que um binário está sob a Proteção de Fluxo de Controle?

Execute a ferramenta dumpbin (incluída na instalação do Visual Studio 2015) no prompt de comando do Visual Studio com as opções /headers e /loadconfig : dumpbin /headers /loadconfig test.exe. A saída de um binário em CFG deve mostrar que os valores de cabeçalho incluem "Guard" e que os valores de configuração de carga incluem "CF Instrumentado" e "Tabela FID presente".

saída de dumpbin /headers

saída de dumpbin /loadconfig

Como o CFG realmente funciona?

As vulnerabilidades de software geralmente são 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, superando assim a área reservada pelo programa para manter uma resposta. Isso pode corromper a memória adjacente que pode conter um ponteiro de função. Quando o programa chama essa função, ele pode ir para um local não intencional especificado pelo invasor.

No entanto, uma combinação potente de suporte de compilação e tempo de execução do 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:

  1. Adiciona verificações de segurança leves ao código compilado.
  2. Identifica o conjunto de funções no aplicativo que são destinos válidos para chamadas indiretas.

O suporte ao runtime, fornecido pelo kernel do Windows:

  1. Mantém com eficiência o estado que identifica destinos de chamadas indiretos válidos.
  2. Implementa a lógica que verifica se um destino de chamada indireto é válido.

Para ilustrar:

pseudocódigo cfg

Quando um marcar CFG falha no runtime, o Windows encerra imediatamente o programa, quebrando qualquer exploração que tente chamar indiretamente um endereço inválido.