Lista de comprobación de seguridad del controlador
En este artículo se proporciona una lista de comprobación de seguridad de controladores para que los desarrolladores de controladores ayuden a reducir el riesgo de que los controladores se vean comprometidos.
Introducción a la seguridad del controlador
Un error de seguridad es cualquier error que permite a un atacante provocar que un controlador funcione mal de tal manera que provoque que el sistema se bloquee o se vuelva inutilizable. Además, las vulnerabilidades en el código del controlador pueden permitir que un atacante obtenga acceso al kernel, creando la posibilidad de comprometer todo el sistema operativo. Cuando la mayoría de los desarrolladores trabajan en sus controladores, su atención se centra en logar que el controlador funcione correctamente y no en si un atacante malintencionado intentará aprovechar vulnerabilidades dentro de su código.
Sin embargo, una vez que se lanza un controlador, los atacantes pueden intentar investigar e identificar errores de seguridad. Los desarrolladores deben tener en cuenta estas cuestiones durante la fase de diseño e implementación para minimizar la probabilidad de que se produzcan dichas vulnerabilidades. El objetivo es eliminar todos los errores de seguridad conocidos antes de que se lance el controlador.
La creación de controladores más seguros requiere la cooperación del arquitecto del sistema (pensando conscientemente en posibles amenazas al controlador), del desarrollador que implementa el código (codificando de forma defensiva las operaciones comunes que pueden ser fuente de vulnerabilidades de seguridad) y del equipo de prueba (intentando encontrar de forma proactiva puntos débiles y vulnerabilidades). Al coordinar adecuadamente todas estas actividades, la seguridad del controlador se mejora considerablemente.
Además de evitar los problemas asociados con un controlador atacado, muchos de los pasos descritos, como el uso más preciso de la memoria del kernel, aumentarán la confiabilidad del controlador. Esto reducirá los costos de soporte técnico y aumentará la satisfacción del cliente con su producto. Completar las tareas de la lista de comprobación siguiente le ayudará a lograr todos estos objetivos.
Lista de comprobación de seguridad: complete la tarea de seguridad descrita en cada uno de los temas siguientes.
Confirmar que se requiere un controlador de kernel
Usar los marcos del controlador
Controlar el acceso solo a los controladores del software
No firmar el código del controlador de prueba de producción
Seguir las directrices de codificación segura del controlador
Implementar código compatible con HVCI
Seguir procedimientos recomendados de código específicos de la tecnología
Realizar una revisión del código del mismo nivel
Administrar el control de acceso del controlador
Mejorar la seguridad de la instalación de dispositivos
Ejecutar la firma adecuada del controlador de versión
Utilizar CodeQL para comprobar el código del controlador
Agregar anotaciones SAL al código del controlador
Utilizar el comprobador de controladores para comprobar vulnerabilidades
Comprobar el código con el analizador binario BinSkim
Comprobar el código con las pruebas del programa de compatibilidad de hardware
Revisar las técnicas y extensiones del depurador
Revisar los recursos de codificación segura
Revisar el resumen de conclusiones clave
Confirmar que se requiere un controlador de kernel
Elemento de la lista de comprobación de seguridad n.º 1: confirme que se requiere un controlador de kernel y que un enfoque de menor riesgo, como un servicio o una aplicación de Windows, no es una mejor opción.
Los controladores residen en el kernel de Windows y si surge un problema al ejecutarlos en el kernel, se expone todo el sistema operativo. Si hay alguna otra opción disponible, es probable que sea más económica y tenga menos riesgos asociados que crear un nuevo controlador de kernel. Para obtener más información sobre el uso de los controladores de Windows integrados, consulte ¿Es necesario escribir un controlador?.
Para obtener información sobre el uso de tareas en segundo plano, consulte Compatibilidad de la aplicación con tareas en segundo plano.
Para obtener información sobre el uso de los servicios de Windows, consulte Servicios.
Usar los marcos del controlador
Elemento de la lista de comprobación de seguridad n.º 2: use los marcos de controladores para reducir el tamaño del código y aumentar su confiabilidad y seguridad.
Use Windows Driver Frameworks para reducir el tamaño del código y aumentar su confiabilidad y seguridad. Para empezar, revise Uso de WDF para desarrollar un controlador. Para obtener información sobre el uso del marco del controlador en modo de usuario de menor riesgo (UMDF), consulte Elección de un modelo de controlador.
Escribir un controlador Windows Driver Model (WDM) antiguo requiere más tiempo, es más costoso y casi siempre implica volver a crear el código que esté disponible en los marcos del controlador.
El código fuente de Windows Driver Framework es de código abierto y está disponible en GitHub. Este es el mismo código fuente a partir del cual se compila la biblioteca en tiempo de ejecución de WDF que se incluye en Windows 10. Puede depurar el controlador de forma más efectiva si puede seguir las interacciones entre el controlador y WDF. Descárguelo desde https://github.com/Microsoft/Windows-Driver-Frameworks.
Controlar el acceso solo a los controladores del software
Elemento de la lista de comprobación de seguridad n.º 3: si se va a crear un controlador de solo software, se debe implementar un control de acceso adicional.
Los controladores de kernel de solo software no usan plug-and-play (PnP) para asociarse con identificadores de hardware específicos y pueden ejecutarse en cualquier equipo. Un controlador de este tipo podría usarse para fines distintos a los previstos originalmente, creando un vector de ataque.
Debido a que los controladores de kernel de solo software contienen riesgos adicionales, se debe limitar su ejecución en hardware específico (por ejemplo, utilizando un identificador de PnP único para habilitar la creación de un controlador PnP o comprobando la tabla SMBIOS para detectar la presencia de hardware específico).
Por ejemplo, imaginemos que OEM Fabrikam desea distribuir un controlador que habilite una utilidad de overclocking para sus sistemas. Si este controlador de solo software se ejecutara en un sistema desde un OEM diferente, podría producirse inestabilidad o daños en el sistema. Los sistemas de Fabrikam deben incluir un identificador de PnP único para habilitar la creación de un controlador PnP que también se pueda actualizar a través de Windows Update. Si esto no es posible y Fabrikam crea un controlador heredado, ese controlador debe encontrar otro método para comprobar que se esté ejecutando en un sistema Fabrikam (por ejemplo, examinando la tabla SMBIOS antes de habilitar cualquier funcionalidad).
No firmar el código del controlador de prueba de producción
Elemento de la lista de comprobación de seguridad n.º 4: no firme el código de producción del controlador del kernel para el desarrollo, pruebas y fabricación.
El código del controlador del kernel que se usa para el desarrollo, las pruebas o la fabricación podría incluir funcionalidades peligrosas que supongan un riesgo de seguridad. Este código peligroso nunca debe firmarse con un certificado en el que Windows confíe. El mecanismo correcto para ejecutar código de controlador peligroso es deshabilitar el arranque seguro UEFI, habilitar el BCD "TESTSIGNING" y firmar el código de desarrollo, prueba y fabricación mediante un certificado que no es de confianza (por ejemplo, uno generado por makecert.exe).
El código firmado por un certificado de editores de software (SPC) de confianza o una firma de Windows Hardware Quality Labs (WHQL) no debe facilitar la omisión de las tecnologías de seguridad e integridad de código de Windows. Antes de firmar el código con una firma SPC o WHQL confiable, primero asegúrese de que cumpla con las pautas de Creación de controladores en modo kernel confiables. Además, el código no debe contener ningún comportamiento peligroso, descrito a continuación. Para obtener más información sobre la firma del controlador, consulte Ejecución de la firma adecuada del controlador de versión más adelante en este artículo.
Entre los ejemplos de comportamiento peligroso se incluyen los siguientes:
- Proporcionar la capacidad de asignar memoria arbitraria del kernel, física o del dispositivo al modo de usuario.
- Proporcionar la capacidad de leer o escribir memoria arbitraria del kernel, física o del dispositivo, incluida la entrada y salida del puerto (E/S).
- Proporcionar acceso al almacenamiento que omite el control de acceso de Windows.
- Proporcionar la capacidad de modificar el hardware o el firmware para cuya administración el controlador no fue diseñado.
Realizar análisis de amenazas
Elemento de la lista de comprobación de seguridad n.º 5: modifique un modelo de amenazas de controlador existente o cree un modelo de amenazas personalizado para el controlador.
Al considerar la seguridad, una metodología común consiste en crear modelos de amenazas específicos que intenten describir los tipos de ataques que son posibles. Esta técnica es útil al diseñar un controlador porque obliga al desarrollador a considerar de antemano los posibles vectores de ataque contra un controlador de antemano. Una vez identificadas las posibles amenazas, un desarrollador de controladores puede considerar medios de defensa contra estas amenazas a fin de reforzar la seguridad general del componente de controlador.
En este artículo se proporcionan instrucciones específicas del controlador para crear un modelo de amenazas ligero: Modelado de amenazas para controladores. El artículo proporciona un diagrama de modelo de amenazas del controlador que se puede usar como punto de partida para su controlador.
IHV y OEM pueden utilizar los procedimientos recomendados del ciclo de vida de desarrollo de seguridad (SDL) y las herramientas asociadas para mejorar la seguridad de sus productos. Para obtener más información, consulte Recomendaciones de SDL para OEM.
Seguir las directrices de codificación segura del controlador
Elemento de la lista de comprobación de seguridad n.º 6: revise el código y quite las vulnerabilidades de código conocidas.
La actividad principal de la creación de controladores seguros es identificar áreas en el código que deben cambiarse para evitar vulnerabilidades de software conocidas. Muchas de estas vulnerabilidades de software conocidas tienen que ver con el mantenimiento de un seguimiento estricto del uso de la memoria para evitar problemas con terceros que sobrescriban o comprometan de otro modo las ubicaciones de memoria que utiliza su controlador.
Las herramientas de análisis de código, como CodeQL y pruebas específicas del controlador, se pueden usar para ayudar a localizar, algunas, pero no todas, de estas vulnerabilidades. Estas herramientas y pruebas se describen más adelante en este tema.
Búferes de memoria
Compruebe siempre los tamaños de los búferes de entrada y salida para asegurarse de que los búferes pueden contener todos los datos solicitados. Para obtener más información, consulte Error al comprobar el tamaño de los búferes.
Inicie correctamente todos los búferes de salida con ceros antes de devolverlos al autor de la llamada. Para obtener más información, consulte Error al iniciar búferes de salida
Validar búferes de longitud variable. Para obtener más información, consulte Error al validar búferes de longitud variable Para obtener más información sobre cómo trabajar con búferes y usar ProbeForRead y ProbeForWrite para validar la dirección de un búfer, consulte Control de búferes.
Uso del método adecuado para acceder a los búferes de datos con IOCTL
Una de las principales responsabilidades de un controlador de Windows es transferir datos entre las aplicaciones en modo de usuario y los dispositivos de un sistema. Los tres métodos para acceder a los búferes de datos se muestran en la tabla siguiente.
Tipo de búfer IOCTL | Resumen | Para más información |
---|---|---|
METHOD_BUFFERED | Recomendado para la mayoría de situaciones | Uso de E/S almacenada en búfer |
METHOD_IN_DIRECT o METHOD_OUT_DIRECT | Se usa en algunas E/S de HW de alta velocidad | Uso de E/S directa |
METHOD_NEITHER | Evitar si es posible | Sin uso de búfer ni E/S directa |
En general, se recomienda la E/S almacenada en búfer, ya que proporciona los métodos de almacenamiento en búfer más seguros. Pero incluso cuando se usa la E/S almacenada en búfer existen riesgos, como punteros integrados, que deben mitigarse.
Para obtener más información sobre cómo trabajar con búferes en IOCTL, consulte Métodos para acceder a a búferes de datos.
Errores en el uso de E/S con búfer IOCTL
Compruebe el tamaño de los búferes relacionados con IOCTL. Para obtener más información, consulte Error al comprobar el tamaño de los búferes.
Inicialice correctamente los búferes de salida. Para obtener más información, consulte Error al iniciar búferes de salida
Valide correctamente los búferes de longitud variable. Para obtener más información, consulte Error al validar búferes de longitud variable
Al usar E/S almacenada en búfer, asegúrese de devolver la longitud adecuada para OutputBuffer en el campo Información de estructura IO_STATUS_BLOCK. No solo devuelva directamente la longitud directamente desde una solicitud READ. Por ejemplo, considere una situación en la que los datos devueltos desde el espacio de usuario indican que hay un búfer de 4K. Si el controlador en realidad solo debería devolver 200 bytes, pero en lugar de eso devuelve 4K en el campo Información, se ha producido una vulnerabilidad de divulgación de información. Este problema se produce porque en versiones anteriores de Windows, el búfer que utiliza el Administrador de E/S para la E/S almacenada en búfer no está puesto a cero. Por lo tanto, la aplicación de usuario recupera los 200 bytes de datos originales más 4K-200 bytes de lo que había en el búfer (contenido del grupo no paginado). Este escenario puede producirse con todos los usos de E/S almacenados en búfer y no solo con IOCTL.
Errores en la E/S directa de IOCTL
Maneje correctamente los búferes de longitud cero. Para obtener más información, consulte Errores en E/S directa.
Errores al hacer referencia a direcciones de espacio de usuario
Valide punteros insertados en solicitudes de E/S almacenadas en búfer. Para obtener más información, consulte Errores al hacer referencia a direcciones de espacio de usuario.
Valide cualquier dirección en el espacio de usuario antes de intentar usarla, mediante API como ProbeForRead y ProbeForWrite cuando corresponda.
Lecturas y escrituras de registros específicos del modelo MSR
Las funciones intrínsecas del compilador, como __readmsr y __writemsr se pueden usar para acceder a los registros específicos del modelo. Si se requiere este acceso, el controlador siempre debe comprobar que el registro para leer o escribir está restringido al índice o intervalo esperado.
Para obtener más información y ejemplos de código, consulte Cómo proporcionar la capacidad de leer y escribir MSR en Procedimientos recomendados de seguridad para desarrolladores de controladores de Windows.
Vulnerabilidades de TOCTOU
Existe una posible vulnerabilidad de tiempo de verificación a tiempo de uso (TOCTOU) cuando se usa E/S directa (para IOCTL o para lectura y escritura). Tenga en cuenta que aunque el controlador esté accediendo al búfer de datos de usuario, el usuario puede estar accediendo a él simultáneamente.
Para administrar este riesgo, copie todos los parámetros que se deban validar desde el búfer de datos de usuario a una memoria a la que solo se pueda acceder desde el modo kernel (como la pila o el grupo). Después, una vez que la aplicación de usuario no pueda acceder a los datos, valide y, a continuación, opere sobre los datos que se pasaron.
El código del controlador debe hacer un uso correcto de la memoria
Todas las asignaciones del grupo de controladores deben estar en un grupo que no sea ejecutable (NX). El uso de grupos de memoria NX es intrínsecamente más seguro que el uso de grupos ejecutables no paginados (NP) y proporciona una mejor protección contra ataques de desbordamiento.
Los controladores de dispositivo deben manejar adecuadamente varias solicitudes de E/S de modo de usuario, así como de kernel a kernel.
Para permitir que los controladores admitan la virtualización de HVCI, existen requisitos de memoria adicionales. Para obtener más información, consulte Implementar código compatible con HVCI más adelante en este artículo.
Asas
- Valide los identificadores pasados entre la memoria en modo usuario y en modo kernel. Para obtener más información, consulte Administración de identificadores y Error al validar identificadores de objeto.
Objetos de dispositivo
Proteja los objetos de dispositivo. Para obtener más información, consulte Protección de objetos de dispositivo.
Valide los objetos de dispositivo. Para obtener más información, consulte Error al validar identificadores de objeto
IRP
WDF e IRP
Una ventaja de usar WDF es que los controladores WDF normalmente no acceden directamente a los IRP. Por ejemplo, el marco convierte los IRP de WDM que representan operaciones de control de E/S de lectura, escritura y dispositivo a objetos de solicitud de marco que KMDF/UMDF reciben en colas de E/S.
Si va a escribir un controlador WDM, revise las instrucciones siguientes.
Administración correcta de los búferes de E/S de IRP
En los artículos siguientes se proporciona información sobre cómo validar los valores de entrada de IRP:
DispatchReadWrite mediante E/S almacenadas en búfer
Errores en E/S almacenada en búfer
DispatchReadWrite mediante E/S directa
Problemas de seguridad para códigos de control de E/S
Considere la posibilidad de validar los valores asociados a un IRP, como las direcciones y longitudes del búfer.
Si decide utilizar Ninguna E/S, tenga cuenta que, a diferencia de Lectura y escritura, y a diferencia de E/S con búfer y E/S directa, cuando se usa Ninguna E/S IOCTL el Administrador de E/S no valida los punteros y longitudes del búfer.
Control correcto de las operaciones de finalización de IRP
Un controlador nunca debe completar un IRP con un valor de estado de STATUS_SUCCESS a menos que realmente admita y procese el IRP. Para obtener información sobre las formas correctas de manejar las operaciones de finalización de IRP, consulte Finalización de IRP.
Administración del estado pendiente del IRP del controlador
El controlador debe marcar el IRP pendiente antes de guardarlo y debe considerar la posibilidad de incluir tanto la llamada a IoMarkIrpPending como la asignación en una secuencia entrelazada. Para obtener más información, consulte Error al comprobar el estado de un controlador y Retener los IRP entrantes cuando un dispositivo está en pausa.
Control correcto de las operaciones de cancelación de IRP
Las operaciones de cancelación pueden ser difíciles de codificar correctamente porque normalmente se ejecutan de forma asincrónica. Los problemas en el código que controla las operaciones de cancelación pueden pasar desapercibidos durante mucho tiempo, ya que este código normalmente no se ejecuta con frecuencia en un sistema en ejecución. Asegúrese de leer y comprender toda la información proporcionada en Cancelación de IRP. Preste especial atención a la sincronización de la cancelación de IRP y a los puntos a tener en cuenta al cancelar IRP.
Una forma recomendada de minimizar los problemas de sincronización asociados con las operaciones de cancelación es implementar una cola IRP segura para la cancelación.
Control correcto de las operaciones de limpieza y cierre de IRP
Asegúrese de comprender la diferencia entre las solicitudes IRP_MJ_CLEANUP e IRP_MJ_CLOSE. Las solicitudes de limpieza llegan después de que una aplicación cierre todos los identificadores de un objeto de archivo, pero a veces antes de que se hayan completado todas las solicitudes de E/S. Las solicitudes de cierre llegan después de que se hayan completado o cancelado todas las solicitudes de E/S para el objeto de archivo. Vea los siguientes artículos para más información:
Rutinas DispatchCreate, DispatchClose y DispatchCreateClose
Errores en el control de las operaciones de limpieza y cierre
Para obtener más información sobre cómo controlar IRP correctamente, consulte Errores adicionales en el control de IRP.
Otros problemas de seguridad
Use un bloqueo o una secuencia entrelazada para evitar condiciones de carrera. Para obtener más información, consulte Errores en un entorno de varios procesadores.
Asegúrese de que los controladores de dispositivo controlan correctamente varios modos de usuario, así como el kernel a las solicitudes de E/S del kernel.
Asegúrese de que el controlador o los paquetes de software asociados no instalen filtros TDI o LSP durante la instalación o el uso.
Uso de funciones seguras
Use funciones de cadena segura Para obtener más información, consulte Uso de funciones de cadena segura.
Use de funciones aritméticas seguras Para obtener más información, consulte Rutinas de biblioteca de enteros seguras.
Use funciones de conversión seguras
Vulnerabilidades adicionales de código
Además de las posibles vulnerabilidades que se describen aquí, en este artículo se proporciona información adicional sobre cómo mejorar la seguridad del código del controlador del modo kernel: Creación de controladores en modo kernel confiables.
Para obtener más información sobre la codificación segura de C y C++, consulte Revisar los recursos de codificación segura al final de este artículo.
Administrar el control de acceso del controlador
Elemento de la lista de comprobación de seguridad n.º 7: revise el controlador para asegurarse de que controla el acceso correctamente.
Administración del control de acceso del controlador: WDF
Los controladores deben funcionar para evitar que los usuarios accedan de forma inapropiada a los dispositivos y archivos de un equipo. Para evitar el acceso no autorizado a dispositivos y archivos, debe:
Asignar un nombre a los objetos de dispositivo solo cuando sea necesario. Los objetos de dispositivo con nombre solo son necesarios por motivos heredados, por ejemplo, si tiene una aplicación que espera abrir el dispositivo con un nombre determinado o si usa un dispositivo de control o un dispositivo que no sea PNP. Tenga en cuenta que los controladores WDF no necesitan asignar un nombre FDO al dispositivo PnP para crear un vínculo simbólico mediante WdfDeviceCreateSymbolicLink.
Proteja el acceso a los objetos e interfaces del dispositivo.
Para permitir que las aplicaciones u otros controladores WDF accedan al PDO del dispositivo PnP, debe usar interfaces de dispositivo. Para obtener más información, consulte Uso de interfaces de dispositivo. Una interfaz de dispositivo actúa como un vínculo simbólico al PDO de la pila de dispositivos.
Una de las mejores formas de controlar el acceso al PDO es especificando una cadena SDDL en el INF. Si la cadena SDDL no está en el archivo INF, Windows aplicará un descriptor de seguridad predeterminado. Para obtener más información, consulte Protección de objetos de dispositivo y SDDL para objetos de dispositivo.
Para obtener más información sobre el control del acceso, consulte los siguientes artículos:
Control de acceso a dispositivos en controladores KMDF
Nombres, descriptores de seguridad y clases de dispositivo: cómo hacer que los objetos de dispositivo sean accesibles... and SAFE desde enero, febrero 2017 The NT Insider Newsletter publicado por OSR.
Administración del control de acceso del controlador: WDM
Si está trabajando con un controlador WDM y utilizó un objeto de dispositivo con nombre, puede usar IoCreateDeviceSecure y especificar un SDDL para protegerlo. Al implementar IoCreateDeviceSecure especifique siempre un GUID de clase personalizado para DeviceClassGuid. No debe especificar aquí un GUID de clase existente. Hacerlo, podría interrumpir la configuración de seguridad o la compatibilidad con otros dispositivos que pertenecen a esa clase. Para obtener más información, consulte WdmlibIoCreateDeviceSecure.
Vea los siguientes artículos para más información:
Control del acceso al dispositivo
Control del acceso al espacio de nombres del dispositivo
Modelo de seguridad de Windows para desarrolladores de controladores
Jerarquía de riesgos de identificadores de seguridad (SID)
En la sección siguiente se describe la jerarquía de riesgos de los SID comunes que se usan en el código de controlador. Para obtener información general sobre SDDL, consulte SDDL para objetos de dispositivo, Cadenas SID y la sintaxis de cadenas SDDL.
Es importante saber que si se permite que autores de llamada con menos privilegios accedan al kernel, el riesgo del código aumenta. En este diagrama de resumen, el riesgo aumenta a medida que se permite el acceso de los SID con privilegios inferiores a la funcionalidad del controlador.
SY (System)
\/
BA (Built-in Administrators)
\/
LS (Local Service)
\/
BU (Built-in User)
\/
AC (Application Container)
Siguiendo el principio general de seguridad de mínimo privilegio, configure solo el nivel mínimo de acceso requerido para que su controlador funcione.
Control de seguridad IOCTL pormenorizado de WDM
Para administrar aún más la seguridad cuando los autores de llamadas en modo de usuario envían IOCTL, el código de controlador puede incluir la función IoValidateDeviceIoControlAccess. Esta función permite a un controlador comprobar los derechos de acceso. Al recibir un IOCTL, un controlador puede llamar a IoValidateDeviceIoControlAccess, especificando FILE_READ_ACCESS, FILE_WRITE_ACCESS o ambos.
La implementación de un control de seguridad IOCTL pormenorizado no reemplaza la necesidad de administrar el acceso de los controladores mediante las técnicas descritas anteriormente.
Vea los siguientes artículos para más información:
Definición de códigos de control de E/S
Implementar código compatible con HVCI
Elemento de la lista de comprobación de seguridad n.º 8: compruebe que el controlador usa memoria para que sea compatible con HVCI.
Uso de memoria y compatibilidad con HVCI
HVCI usa tecnología de hardware y virtualización para aislar la función de toma de decisiones de integridad de código (CI) del resto del sistema operativo. Al utilizar seguridad basada en virtualización para aislar CI, la única forma en que la memoria del kernel puede convertirse en ejecutable es a través de una comprobación de CI. Esto significa que las páginas de memoria del kernel nunca pueden ser regrabables y ejecutables (W+X) y el código ejecutable no se puede modificar directamente.
Para implementar código compatible con HVCI, asegúrese de que el código de controlador haga lo siguiente:
- Participa en NX de forma predeterminada
- Usa las API o marcas de NX para la asignación de memoria (NonPagedPoolNx)
- No usa secciones que sean regrabables y ejecutables
- No intenta modificar directamente la memoria del sistema ejecutable
- No usa código dinámico en kernel
- No carga archivos de datos como ejecutables
- La alineación de sección es un múltiplo de 0x1000 (PAGE_SIZE). Por ejemplo, DRIVER_ALIGNMENT=0x1000
Para obtener más información sobre el uso de la herramienta y una lista de llamadas de memoria incompatibles, consulte Implementación de código compatible con HVCI.
Para obtener más información sobre la prueba de seguridad de los aspectos básicos del sistema relacionados, consulte Prueba de preparación de integridad de código de HyperVisor e Integridad del código protegido por hipervisor (HVCI).
Seguir procedimientos recomendados de código específicos de la tecnología
Elemento de la lista de comprobación de seguridad n.º 9: revise la siguiente guía tecnológica específica para el controlador.
Sistemas de archivos
Para obtener más información, acerca de la seguridad del controlador del sistema de archivos, consulte los artículos siguientes:
Introducción a la seguridad de los sistemas de archivos
Problemas de seguridad del sistema de archivos
Características de seguridad para sistemas de archivos
Coexistencia con otros controladores de filtro del sistema de archivos
NDIS - Redes
Para obtener información sobre la seguridad del controlador NDIS, consulte Problemas de seguridad de los controladores de red.
Mostrar
Para obtener información sobre la seguridad del controlador de visualización, consulte <Contenido pendiente>.
Impresoras
Para obtener información relacionada con la seguridad del controlador de impresora, consulte Consideraciones de seguridad del controlador de impresora V4.
Problemas de seguridad de los controladores de adquisición de imágenes de Windows (WIA)
Para obtener información sobre la seguridad de WIA, consulte Problemas de seguridad de los controladores WIA.
Mejorar la seguridad de la instalación de dispositivos
Elemento de la lista de comprobación de seguridad n.º 10: revise las instrucciones de creación e instalación del controlador para asegurarse de que sigue los procedimientos recomendados.
Al crear el código que instala el controlador, debe asegurarse de que la instalación del dispositivo siempre se realizará de forma segura. Una instalación segura de dispositivos es aquella que hace lo siguiente:
- Limita el acceso al dispositivo y a sus clases de interfaz de dispositivo.
- Limita el acceso a los servicios de controlador que se crearon para el dispositivo.
- Protege los archivos de controlador de modificaciones o eliminaciones.
- Limita el acceso a las entradas del registro del dispositivo.
- Limita el acceso a las clases WMI del dispositivo.
- Usa las funciones setupAPI correctamente.
Vea los siguientes artículos para más información:
Creación de instalaciones de dispositivos seguros
Directrices para usar SetupAPI
Uso de funciones de instalación de dispositivos
Temas avanzados de instalación de dispositivos y controladores
Realizar una revisión del código del mismo nivel
Elemento de la lista de comprobación de seguridad n.º 11: realice una revisión del código del mismo nivel para buscar problemas que no hayan sido detectados por las otras herramientas y procesos
Busque revisores de código expertos para buscar problemas que se puedan haber pasado por alto. Una segunda revisión a menudo detectará problemas que quizás usted haya pasado por alto.
Si no cuenta con personal adecuado para revisar el código internamente, considere contratar ayuda externa para este propósito.
Ejecutar la firma adecuada del controlador de versión
Elemento de la lista de comprobación de seguridad n.º 12: use el portal de asociados de Windows para firmar correctamente el controlador para su distribución.
Antes de liberar un paquete de controladores al público, se recomienda enviar el paquete para su certificación. Para obtener más información, consulte Prueba de rendimiento y compatibilidad, Introducción al programa de hardware, Servicios de panel de hardware y Firma de atestación de un controlador de kernel para la versión.
Utilizar CodeQL para comprobar el código del controlador
Elemento de la lista de comprobación de seguridad n.º 13: use CodeQL para comprobar si hay vulnerabilidades en el código de controlador.
CodeQL, de GitHub, es un motor de análisis de código semántico y la combinación de un amplio conjunto de consultas de seguridad junto con una plataforma sólida lo convierten en una herramienta inestimable para proteger el código de controlador. Para obtener más información, consulte CodeQL y la prueba del logotipo de herramientas estáticas.
Agregar anotaciones SAL al código del controlador
Elemento de la lista de comprobación de seguridad n.º 14: agregue anotaciones SAL a en el código de controlador.
El lenguaje de anotación de código fuente (SAL) proporciona un conjunto de anotaciones que puede usar para describir cómo una función usa sus parámetros, las suposiciones que hace sobre ellos y las garantías que ofrece cuando finaliza. Las anotaciones se definen en el archivo de encabezado sal.h
. Code Analysis de Visual Studio para C++ usa anotaciones SAL para modificar su análisis de funciones. Para obtener más información sobre el desarrollo de controladores SAL 2.0 para Windows, consulte Anotaciones de SAL 2.0 para controladores de Windows y Utilizar anotaciones SAL para reducir defectos de código de C/C++.
Para obtener información general sobre SAL, consulte este artículo disponible en OSR. https://www.osr.com/blog/2015/02/23/sal-annotations-dont-hate-im-beautiful/
Utilizar el comprobador de controladores para comprobar vulnerabilidades
Elemento de la lista de comprobación de seguridad n.º 15: use el comprobador de controladores para comprobar vulnerabilidades en el código de controlador.
El comprobador de controladores usa un conjunto de reglas de interfaz y un modelo del sistema operativo para determinar si el controlador interactúa correctamente con el sistema operativo Windows. DV puede encontrar defectos en el código de controlador que podrían apuntar a posibles errores en los controladores.
El comprobador de controladores permite realizar pruebas del controlador de por vida. El Comprobador de controladores supervisa los controladores del modo kernel de Windows y los controladores gráficos para detectar llamadas o acciones de función no válidas que podrían dañar el sistema. Además, puede someter los controladores de Windows a varias pruebas y tensiones para detectar comportamientos incorrectos. Para obtener más información, consulte Comprobador de controladores.
Tenga en cuenta que solo ciertos tipos de controladores son compatibles con DV. Para obtener más información sobre los controladores que DV puede comprobar, consulte Controladores admitidos. Consulte las páginas siguientes para obtener información sobre las pruebas del DV disponibles para el tipo de controlador con el que esté trabajando.
- Reglas para controladores WDM
- Reglas para controladores KMDF
- Reglas para controladores NDIS
- Reglas para controladores Storport
- Reglas para controladores de audio
- Reglas para controladores AVStream
Para familiarizarse con DV, puede usar uno de los controladores de ejemplo (por ejemplo, el ejemplo de toaster destacado: https://github.com/Microsoft/Windows-driver-samples/tree/main/general/toaster/toastDrv/kmdf/func/featured).
Comprobar el código con el analizador binario BinSkim
Elemento de la lista de comprobación de seguridad n.º 16: siga estos pasos para usar BinSkim y poder comprobar que las opciones de compilación y creación estén configuradas para minimizar problemas de seguridad conocidos.
Use BinSkim para examinar los archivos binarios e identificar prácticas de codificación y creación que puedan hacer que el binario se vulnerable.
BinSkim comprueba si:
- Se usan conjuntos de herramientas del compilador obsoletos: los archivos binarios deben compilarse con los conjuntos de herramientas de compilación más recientes siempre que sea posible para maximizar el uso de las mitigaciones de seguridad proporcionadas por el sistema operativo y el nivel de compilador actual.
- Las configuraciones de compilaciones no son seguras: los archivos binarios deben compilarse con las configuraciones más seguras posibles para habilitar las mitigaciones de seguridad proporcionadas por el sistema operativo, maximizar los errores del compilador y los informes de advertencias procesables, entre otras cosas.
- Hay problemas de firma: los archivos binarios firmados deben firmarse con algoritmos criptográficamente seguros.
BinSkim es una herramienta de código abierto y genera archivos de salida que usan el formato de intercambio de resultados de análisis estáticos (SARIF). BinSkim reemplaza a la antigua herramienta BinScope.
Para obtener más información sobre BinSkim, consulte la Guía del usuario de BinSkim.
Siga estos pasos para validar que las opciones de compilación de seguridad estén configuradas correctamente en el código que va a enviar.
Descargue e instale el SDK multiplataforma .NET Core.
Confirme que Visual Studio esté instalado. Para obtener información sobre cómo descargar e instalar Visual Studio, consulte Instalar Visual Studio.
Hay varias opciones para descargar BinSkim, como un paquete NuGet. En este ejemplo usaremos la opción git clone para descargarlo desde aquí: https://github.com/microsoft/binskim e instalarlo en un equipo con Windows de 64 bit.
Abra una ventana del símbolo del sistema para desarrolladores de Visual Studio y cree un directorio, por ejemplo
C:\binskim-master
.C:\> Md \binskim-master
Vaya a ese directorio que acaba de crear.
C:\> Cd \binskim-master
Use el comando git clone para descargar todos los archivos necesarios.
C:\binskim-master> git clone --recurse-submodules https://github.com/microsoft/binskim.git
Vaya al nuevo directorio
binskim
que creó el comando clone.C:\> Cd \binskim-master\binskim
Ejecute BuildAndTest.cmd para garantizar que la compilación de la versión se realice correctamente y que se superen todas las pruebas.
C:\binskim-master\binskim> BuildAndTest.cmd Welcome to .NET Core 3.1! --------------------- SDK Version: 3.1.101 ... C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64\BinSkim.Sdk.dll 1 File(s) copied C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\linux-x64\BinSkim.Sdk.dll 1 File(s) copied ...
El proceso de compilación crea un conjunto de directorios con los ejecutables BinSkim. Vaya al directorio de salida de la compilación win-x64.
C:\binskim-master\binskim> Cd \binskim-master\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64>
Mostrar la ayuda para la opción de análisis
C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim help analyze
BinSkim PE/MSIL Analysis Driver 1.6.0.0
--sympath Symbols path value, e.g., SRV*http://msdl.microsoft.com/download/symbols or Cache*d:\symbols;Srv*http://symweb. See
https://learn.microsoft.com/windows-hardware/drivers/debugger/advanced-symsrv-use for syntax information. Note that BinSkim will clear the
_NT_SYMBOL_PATH environment variable at runtime. Use this argument for symbol information instead.
--local-symbol-directories A set of semicolon-delimited local directory paths that will be examined when attempting to locate PDBs.
-o, --output File path to which analysis output will be written.
--verbose Emit verbose output. The resulting comprehensive report is designed to provide appropriate evidence for compliance scenarios.
...
Configuración de la ruta de acceso del símbolo
Si va a compilar todo el código que está analizando en la misma máquina en la que ejecuta BinSkim, normalmente no necesitará establecer la ruta de acceso del símbolo. Esto se debe a que los archivos de símbolo están disponibles en el cuadro local donde los compiló. Si utiliza un sistema de compilación más complejo o redirige los símbolos a una ubicación diferente (no junto con el binario compilado), use --local-symbol-directories
para agregar estas ubicaciones a la búsqueda de archivos de símbolo.
Si el código hace referencia a un binario compilado que no forma parte del código, se puede usar el depurador de Windows sympath para recuperar símbolos con el fin de comprobar la seguridad de estas dependencias de código. Si encuentra problemas en estas dependencias, es posible que no pueda corregirlos. Pero puede ser útil tener en cuenta cualquier riesgo de seguridad posible que esté aceptando al asumir esas dependencias.
Sugerencia
Al agregar una ruta de acceso de símbolo (que hace referencia a un servidor de símbolos en red), agregue una ubicación de caché local para especificar una ruta de acceso local y almacenar en caché los símbolos. No hacerlo puede comprometer considerablemente el rendimiento de BinSkim. En el ejemplo siguiente, se especifica una caché local en d:\symbols.
--sympath Cache*d:\symbols;Srv*http://symweb
Para obtener más información sobre sympath, consulte Ruta de acceso a símbolos para depuradores de Windows.
Ejecute el siguiente comando para analizar un archivo binario del controlador compilado. Actualice la ruta de acceso de destino para que apunte al archivo .sys del controlador compatible.
C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\echo.sys"
Para obtener información adicional, agregue una opción verbose como esta.
C:\binskim-master\binskim\bld\bin\AnyCPU_Release\Publish\netcoreapp2.0\win-x64> BinSkim analyze "C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys" --verbose
Nota:
La opción verbose generará resultados de paso o error explícitos en cada comprobación. Si no proporciona la opción verbose, solo verá los defectos que detecta BinSkim. Normalmente, la opción verbose no se recomienda para sistemas de automatización reales debido al mayor tamaño de los archivos de registro y porque hace que sea más difícil detectar errores individuales cuando ocurren, ya que estarán incrustadas en medio de una gran cantidad de resultados "aprobados".
Revise la salida del comando para buscar posibles problemas. En esta salida de ejemplo se muestran tres pruebas que se han superado. Puede encontrar información adicional sobre las reglas, como BA2002, en la Guía del usuario de BinSkim.
Analyzing... Analyzing 'osrusbfx2.sys'... ... C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys\Debug\osrusbfx2.sys: pass BA2002: 'osrusbfx2.sys' does not incorporate any known vulnerable dependencies, as configured by current policy. C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: pass BA2005: 'osrusbfx2.sys' is not known to be an obsolete binary that is vulnerable to one or more security problems. C:\Samples\KMDF_Echo_Driver\osrusbfx2.sys: pass BA2006: All linked modules of 'osrusbfx2.sys' generated by the Microsoft front-end satisfy configured policy (compiler minimum version 17.0.65501.17013).
Esta salida muestra que la prueba BA3001 no se ejecuta porque la herramienta indica que el controlador no es un binario ELF.
... C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: notapplicable BA3001: 'osrusbfx2.sys' was not evaluated for check 'EnablePositionIndependentExecutable' as the analysis is not relevant based on observed metadata: image is not an ELF binary.
Esta salida muestra un error para la prueba BA2007.
... C:\Samples\KMDF_Echo_Driver\Debug\osrusbfx2.sys: error BA2007: 'osrusbfx2.sys' disables compiler warning(s) which are required by policy. A compiler warning is typically required if it has a high likelihood of flagging memory corruption, information disclosure, or double-free vulnerabilities. To resolve this issue, enable the indicated warning(s) by removing /Wxxxx switches (where xxxx is a warning id indicated here) from your command line, and resolve any warnings subsequently raised during compilation.
Para habilitar estas advertencias en Visual Studio, en C/C++ en las páginas de propiedades del proyecto, quite los valores que no desea excluir en Deshabilitar advertencias específicas.
Las opciones de compilación predeterminadas de Visual Studio para proyectos de controladores pueden deshabilitar advertencias como las siguientes. BinSkim notificará estas advertencias.
C4627 - 'description': omitido al buscar el uso del encabezado precompilado
C4986 - 'declaration': la especificación de excepción no coincide con la declaración anterior
Para obtener más información sobre las advertencias del compilador, consulte Advertencias del compilador por versión del compilador.
Comprobar el código con las pruebas del programa de compatibilidad de hardware
Elemento de la lista de comprobación de seguridad n.º 17: use las pruebas del programa de compatibilidad de hardware relacionadas con la seguridad para comprobar si hay problemas de seguridad.
El programa de compatibilidad de hardware incluye pruebas relacionadas con la seguridad se pueden utilizar para buscar vulnerabilidades en el código. El Programa de compatibilidad de hardware con Windows aprovecha las pruebas de Windows Hardware Lab Kit (HLK). Las pruebas de aspectos básicos del dispositivo HLK se pueden usar en la línea de comandos para ejercitar el código del controlador y detectar puntos débiles. Para obtener información general sobre las pruebas fundamentales del dispositivo y el programa de compatibilidad de hardware, consulte Windows Hardware Lab Kit.
Las siguientes pruebas son ejemplos de pruebas que pueden resultar útiles para comprobar el código de controlador en busca de algunos comportamientos asociados con vulnerabilidades de código:
DF: Prueba aproximada de IOCTL aleatoria (confiabilidad)
DF: Prueba aproximada de aperturas relativas (confiabilidad)
DF: Prueba aproximada FSCTL de búfer de longitud cero (confiabilidad)
DF: Prueba aproximada de FSCTL aleatoria (confiabilidad)
DF: Prueba aproximada de Misc API (confiabilidad)
También puede usar la pruebas de vulnerabilidad ante datos aleatorios o inesperados de demora en la sincronización del kernel que se incluye con el comprobador de controladores.
Las pruebas CHAOS (hardware simultáneo y sistema operativo) ejecutan varias pruebas de controladores PnP, pruebas aproximadas del controlador de dispositivo y pruebas del sistema de energía simultáneamente. Para obtener más información, consulte Pruebas CHAOS (aspectos básicos del dispositivo).
Las pruebas de penetración de los aspectos básicos del dispositivo realizan varias formas de ataques de entrada, que son un componente crítico de las pruebas de seguridad. Las pruebas de ataque y penetración pueden ayudar a identificar vulnerabilidades en las interfaces de software. Para obtener más información, consulte Pruebas de penetración (aspectos básicos del dispositivo).
Use Device Guard: prueba de cumplimiento, junto con las otras herramientas descritas en este artículo, para confirmar que el controlador es compatible con HVCI.
Herramientas de prueba personalizadas y específicas del dominio
Considere el desarrollo de pruebas de seguridad personalizadas y específicas para cada dominio. Para desarrollar pruebas adicionales, recopile información de los diseñadores originales del software, así como de los recursos de desarrollo no relacionados que estén familiarizados con el tipo específico de controlador que se esté desarrollando y una o más personas familiarizadas con el análisis y la prevención de intrusiones de seguridad.
Revisar las técnicas y extensiones del depurador
Elemento de la lista de comprobación de seguridad n.º 18: revise estas herramientas de depuración y considere su uso en el flujo de trabajo de depuración de desarrollo.
Comandos del depurador relacionados con la seguridad
La extensión !acl da formato y muestran el contenido de una lista de control de acceso (ACL). Para obtener más información, consulte Determinar la ACL de un objeto y !acl.
La extensión !token muestra una vista con formato de un objeto de token de seguridad. Para obtener más información, consulte !token.
La extensión !tokenfields muestra los nombres y desplazamientos de los campos dentro del objeto de token de acceso (la estructura TOKEN). Para obtener más información, consulte !tokenfields.
La extensión !sid muestra el identificador de seguridad (SID) en la dirección especificada. Para obtener más información, consulte !sid.
La extensión !sd muestra el descriptor de seguridad en la dirección especificada. Para obtener más información, consulte !sd.
Centro de informes de controladores vulnerables y malintencionados de Microsoft
Cualquiera puede enviar un controlador cuestionable mediante el Centro de informes de controladores vulnerables y malintencionados de Microsoft. Consulte esta entrada de blog para obtener información sobre cómo se envían los controladores para su análisis: cómo mejorar la seguridad del kernel con el nuevo Centro de informes de controladores vulnerables y malintencionados de Microsoft
El Centro de informes puede examinar y analizar controladores de Windows creados para arquitecturas x86 y x64. Los controladores analizados vulnerables y malintencionados se marcan para su análisis e investigación por parte del equipo de controladores vulnerables de Microsoft. Una vez confirmados los controladores vulnerables, se produce una notificación correspondiente y se agregan a la lista de bloqueados del controlador vulnerable. Para obtener más información sobre esto, consulte Reglas de bloqueo de controladores recomendadas por Microsoft. Estas reglas se aplican de forma predeterminada a los dispositivos habilitados para integridad de código protegido por hipervisor (HVCI) y Windows 10 en modo S.
Revisar los recursos de codificación segura
Elemento de la lista de comprobación de seguridad n.º 19: revise estos recursos para ampliar la comprensión de los procedimientos recomendados de codificación segura aplicables a los desarrolladores de controladores.
Revise estos recursos para obtener más información sobre la seguridad de los controladores
Directrices de codificación de controladores en modo kernel seguro
Creación de controladores en modo kernel confiables
Organizaciones de codificación segura
Carnegie Mellon University SEI CERT
Estándar de codificación SEI CERT C de la Universidad Carnegie Mellon: reglas para desarrollar sistemas seguros, confiables y protegidos (edición 2016).
MITRE: puntos débiles abordados por el estándar de codificación segura CERT C
Creación de seguridad en el modelo de madurez (BSIMM): https://www.bsimm.com/
SAFECode: https://safecode.org/
OSR
OSR proporciona servicios de consultoría y aprendizaje para el desarrollo de controladores. Estos artículos del boletín OSR destacan los problemas de seguridad de los controladores.
Debe usar la protección: seguridad interna del controlador y del dispositivo
Bloqueo de controladores: una encuesta sobre técnicas
Meltdown y Spectre: ¿qué pasa con los controladores?
Caso práctico
Libros
24 deadly sins of software security : programming flaws and how to fix them (24 pecados capitales de la seguridad del software: fallos de programación y cómo solucionarlos) por Michael Howard, David LeBlanc y John Viega
The art of software security assessment : identifying and preventing software vulnerabilities (El arte de evaluar la seguridad del software: identificar y prevenir vulnerabilidades del software), Mark Dowd, John McDonald y Justin Schuh
Writing Secure Software, Second Edition (Cómo escribir software seguro, segunda edición), Michael Howard y David LeBlanc
The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities (El arte de evaluar la seguridad del software: identificar y prevenir vulnerabilidades del software), Mark Dowd y John McDonald
Secure Coding in C and C++ (SEI Series in Software Engineering) 2nd Edition [Codificación segura en C y C++ (Serie SEI en ingeniería de software) 2.ª edición)], Robert C. Seacord
Programming the Microsoft Windows Driver Model (2nd Edition) [Programación del modelo de controlador de Microsoft Windows (segunda edición)], Walter Oney
Developing Drivers with the Windows Driver Foundation (Developer Reference) [Desarrollo de controladores con Windows Driver Foundation (referencia para desarrolladores)], Penny Orwick y Guy Smith
Cursos
La formación en aula para controladores de Windows está disponible a través de proveedores como los siguientes:
La formación en línea sobre codificación segura está disponible en una variedad de fuentes. Por ejemplo, este curso está disponible en:
Identificación de vulnerabilidades de seguridad en programación C/C++.
SAFECode también ofrece formación gratuita:
Certificación profesional
CERT ofrece una Certificación profesional de codificación segura.
Revisar el resumen de conclusiones clave
La seguridad del controlador es una tarea compleja que contiene muchos elementos, pero estos son algunos puntos clave a tener en cuenta:
Los controladores residen en el kernel de Windows y si surge un problema al ejecutarlos en el kernel, se expone todo el sistema operativo. Por este motivo, preste mucha atención a la seguridad y al diseño de los controladores teniendo en cuenta la seguridad.
Aplique el principio de mínimo privilegio:
a. Usar una cadena SDDL estricta para restringir el acceso al controlador
b. Restringir aún más los IOCTL individuales
Cree un modelo de amenaza para identificar vectores de ataque y considere si algo se puede restringir aún más.
Tenga cuidado con los punteros incrustados que se pasan desde el modo de usuario. Es necesario probarlos, acceder a ellos dentro de try except y son propensos a vulnerabilidad de tiempo de verificación a tiempo de uso (ToCToU) a menos que se capture y compare el valor del búfer.
Si no está seguro, use METHOD_BUFFERED como método de almacenamiento en búfer IOCTL.
Use utilidades de análisis de código para buscar vulnerabilidades de código conocidas y solucionar cualquier problema identificado.
Busque revisores de código expertos para buscar problemas que se puedan haber pasado por alto.
Use comprobadores de controladores y pruebe el controlador con varias entradas, incluidos casos extremos.