Protección de flujo de control para la seguridad de la plataforma

¿Qué es Control Flow Guard?

Control Flow Guard (CFG) es una característica de seguridad de plataforma altamente optimizada que se creó para combatir las vulnerabilidades de daños en la memoria. Al colocar restricciones estrictas en el lugar desde el que una aplicación puede ejecutar código, hace que sea mucho más difícil que las vulnerabilidades de seguridad ejecuten código arbitrario a través de vulnerabilidades como desbordamientos de búfer. CFG amplía las tecnologías de mitigación de vulnerabilidades anteriores, como /GS, DEP y ASLR.

  • Evitar daños en la memoria y ataques de ransomware.
  • Restrinja las funcionalidades del servidor a lo que sea necesario en un momento dado para reducir la superficie expuesta a ataques.
  • Hacer que sea más difícil aprovechar el código arbitrario a través de vulnerabilidades como desbordamientos de búfer.

Esta característica está disponible en Microsoft Visual Studio 2015, y se ejecuta en las versiones "CFG-Aware" de Windows: las versiones x86 y x64 para escritorio y servidor de Windows 10 y Windows 8.1 Update (KB3000850).

Recomendamos encarecidamente a los desarrolladores que habiliten CFG para sus aplicaciones. No tiene que habilitar CFG para cada parte de su código, ya que una mezcla de código habilitado para CFG y no habilitado para CFG se ejecutará sin problemas. Pero no habilitar CFG para todo el código puede abrir brechas en la protección. Además, el código habilitado para CFG funciona correctamente en las versiones "no compatibles con CFG" de Windows y, por lo tanto, es totalmente compatible con ellas.

¿Cómo puedo habilitar CFG?

En la mayoría de los casos, no es necesario cambiar el código fuente. Lo único que tiene que hacer es agregar una opción al proyecto de Visual Studio 2015 y el compilador y vinculador habilitarán CFG.

El método más sencillo es navegar a Project | Propiedades | Propiedades de configuración | C/C++ | Generación de código y elija Sí (/guard:cf) para Control Flow Guard.

propiedad cfg en Visual Studio

Como alternativa, agregue /guard:cf a Project | Propiedades | Propiedades de configuración | C/C++ | Línea de comandos | Opciones adicionales (para el compilador) y /guard:cf para Project | Propiedades | Propiedades de configuración | Enlazador | Línea de comandos | Opciones adicionales (para el enlazador).

propiedad cfg para lapropiedad cfg del compilador para el enlazador

Consulte /guard (Habilitar protección de flujo de control) para obtener información adicional.

Si va a compilar el proyecto desde la línea de comandos, puede agregar las mismas opciones. Por ejemplo, si va a compilar un proyecto denominado test.cpp, use cl /guard:cf test.cpp /link /guard:cf.

También tiene la opción de controlar dinámicamente el conjunto de direcciones de destino de icall que CFG considera válidas mediante SetProcessValidCallTargets de memory Management API. La misma API se puede usar para especificar si las páginas no son válidas o son destinos válidos para CFG. Las funciones VirtualProtect y VirtualAlloc tratarán de forma predeterminada una región especificada de páginas ejecutables y confirmadas como destinos de llamada indirectos válidos. Es posible invalidar este comportamiento, como al implementar un compilador Just-In-Time, especificando PAGE_TARGETS_INVALID al llamar a VirtualAlloc o PAGE_TARGETS_NO_UPDATE al llamar a VirtualProtect como se detalla en Constantes de protección de memoria.

¿Cómo puedo saber que un binario está bajo la protección de flujo de control?

Ejecute la herramienta dumpbin (incluida en la instalación de Visual Studio 2015) desde el símbolo del sistema de Visual Studio con las opciones /headers y /loadconfig : dumpbin /headers /loadconfig test.exe. La salida de un binario en CFG debe mostrar que los valores de encabezado incluyen "Guard" y que los valores de configuración de carga incluyen "CF Instrumented" y "TABLA FID presente".

salida de dumpbin /headers

salida de dumpbin /loadconfig

¿Cómo funciona CFG?

Las vulnerabilidades de software suelen aprovecharse al proporcionar datos poco probables, inusuales o extremos a un programa en ejecución. Por ejemplo, un atacante puede aprovechar una vulnerabilidad de desbordamiento de búfer proporcionando más entrada a un programa de lo esperado, lo que supera la ejecución del área reservada por el programa para contener una respuesta. Esto podría dañar la memoria adyacente que puede contener un puntero de función. Cuando el programa llama a través de esta función, puede saltar a una ubicación no deseada especificada por el atacante.

Sin embargo, una combinación potente de compatibilidad en tiempo de compilación y tiempo de ejecución de CFG implementa la integridad del flujo de control que restringe estrechamente dónde se pueden ejecutar las instrucciones de llamada indirectas.

El compilador hace lo siguiente:

  1. Agrega comprobaciones de seguridad ligeras al código compilado.
  2. Identifica el conjunto de funciones de la aplicación que son destinos válidos para llamadas indirectas.

Compatibilidad con tiempo de ejecución, proporcionada por el kernel de Windows:

  1. Mantiene eficazmente el estado que identifica destinos de llamada indirectos válidos.
  2. Implementa la lógica que comprueba que un destino de llamada indirecto es válido.

Para ilustrar:

cfg pseudocódigo

Cuando se produce un error en una comprobación de CFG en tiempo de ejecución, Windows finaliza inmediatamente el programa, lo que interrumpe cualquier vulnerabilidad de seguridad que intente llamar indirectamente a una dirección no válida.