Controllare Flow Guard per la sicurezza della piattaforma

Che cos'è Control Flow Guard?

Il controllo Flow Guard (CFG) è una funzionalità di sicurezza della piattaforma altamente ottimizzata creata per combattere le vulnerabilità di danneggiamento della memoria. Inserendo restrizioni rigide sulla posizione da cui un'applicazione può eseguire codice, rende molto più difficile per gli exploit eseguire codice arbitrario tramite vulnerabilità come gli overflow del buffer. CFG estende le tecnologie di mitigazione degli exploit precedenti, ad esempio /GS, DEP e ASLR.

  • Prevenire il danneggiamento della memoria e gli attacchi ransomware.
  • Limitare le funzionalità del server a qualsiasi elemento necessario in un determinato momento per ridurre la superficie di attacco.
  • Rendere più difficile sfruttare il codice arbitrario tramite vulnerabilità, ad esempio overflow del buffer.

Questa funzionalità è disponibile in Microsoft Visual Studio 2015 ed è in esecuzione nelle versioni "compatibile con CFG" di Windows, ovvero le versioni x86 e x64 per Desktop e Server di Windows 10 e Windows 8.1 Update (KB3000850).

Si consiglia vivamente agli sviluppatori di abilitare CFG per le proprie applicazioni. Non è necessario abilitare cfg per ogni parte del codice, poiché una combinazione di codice abilitato per cfg e codice non abilitato per CFG verrà eseguito correttamente. Tuttavia, non è possibile abilitare cfg per tutto il codice può aprire gap nella protezione. Inoltre, il codice abilitato per CFG funziona correttamente nelle versioni "CFG-Unaware" di Windows ed è quindi completamente compatibile con essi.

Come è possibile abilitare CFG?

Nella maggior parte dei casi, non è necessario modificare il codice sorgente. Tutto quello che devi fare è aggiungere un'opzione al progetto Visual Studio 2015 e il compilatore e il linker abiliteranno CFG.

Il metodo più semplice consiste nel passare a Project | Proprietà | Proprietà di configurazione | | C/C++ Generazione di codice e scegliere Sì (/guard:cf) per Controllo Flow Guard.

cfg property in visual studio

In alternativa, aggiungere /guard:cf a Project | Proprietà | Proprietà di configurazione | | C/C++ | della riga di comando Opzioni aggiuntive (per il compilatore) e /guard:cf per Project | Proprietà | Proprietà di configurazione | | del linker | della riga di comando Opzioni aggiuntive (per il linker).

cfg property for compilercfg property for linker

Per altre informazioni, vedi /guard (Abilita controllo Flow Guard).

Se si compila il progetto dalla riga di comando, è possibile aggiungere le stesse opzioni. Ad esempio, se si compila un progetto denominato test.cpp, usare cl /guard:cf test.cpp /link /guard:cf.

È anche possibile controllare dinamicamente il set di indirizzi di destinazione icall considerati validi da CFG usando SetProcessValidCallTargets dall'API Gestione memoria. La stessa API può essere usata per specificare se le pagine non sono valide o sono destinazioni valide per CFG. Le funzioni VirtualProtect e VirtualAlloc considerano per impostazione predefinita un'area specificata di pagine eseguibili e di cui è stato eseguito il commit come destinazioni di chiamata indirette valide. È possibile eseguire l'override di questo comportamento, ad esempio quando si implementa un compilatore JIT, specificando PAGE_TARGETS_INVALID quando si chiama VirtualAlloc o PAGE_TARGETS_NO_UPDATE quando si chiama VirtualProtect come descritto in Costanti di protezione della memoria.

Come posso dire che un file binario è sotto controllo Flow Guard?

Eseguire lo strumento dumpbin (incluso nell'installazione di Visual Studio 2015) dal prompt dei comandi Visual Studio con le opzioni /headers e /loadconfig: dumpbin /headers /loadconfig test.exe. L'output per un file binario in CFG deve indicare che i valori di intestazione includono "Guard" e che i valori di configurazione del carico includono "CF Instrumented" e "FID table present".

output from dumpbin /headers

output from dumpbin /loadconfig

Come funziona realmente CFG?

Le vulnerabilità software vengono spesso sfruttate fornendo dati improbabili, insoliti o estremi a un programma in esecuzione. Ad esempio, un utente malintenzionato può sfruttare una vulnerabilità di overflow del buffer fornendo più input a un programma del previsto, eseguendo in tal modo l'area riservata dal programma per contenere una risposta. Ciò potrebbe danneggiare la memoria adiacente che può contenere un puntatore a funzione. Quando il programma chiama tramite questa funzione, può passare a una posizione non intenzionale specificata dall'utente malintenzionato.

Tuttavia, una combinazione potente di supporto per la compilazione e la fase di esecuzione di CFG implementa l'integrità del flusso di controllo che limita strettamente la posizione in cui è possibile eseguire le istruzioni di chiamata indirette.

Il compilatore esegue le operazioni seguenti:

  1. Aggiunge controlli di sicurezza leggeri al codice compilato.
  2. Identifica il set di funzioni nell'applicazione che sono destinazioni valide per le chiamate indirette.

Il supporto del runtime, fornito dal kernel Windows:

  1. Mantiene in modo efficiente lo stato che identifica destinazioni di chiamata indirette valide.
  2. Implementa la logica che verifica che una destinazione di chiamata indiretta sia valida.

Per illustrare:

cfg pseudocode

Quando un controllo cfg ha esito negativo in fase di esecuzione, Windows termina immediatamente il programma, interrompendo così qualsiasi exploit che tenta di chiamare indirettamente un indirizzo non valido.