Modelo de seguridad de Windows para desarrolladores de controladores

El modelo de seguridad de Windows se basa en objetos protegibles. Cada componente del sistema operativo debe garantizar la seguridad de los objetos para los que es responsable. Por lo tanto, los controladores deben proteger la seguridad de sus dispositivos y objetos de dispositivo.

En este tema se resume cómo se aplica el modelo de seguridad de Windows a los controladores en modo kernel.

Modelo de seguridad de Windows

El modelo de seguridad de Windows se basa principalmente en derechos por objeto, con un pequeño número de privilegios para todo el sistema. Los objetos que se pueden proteger incluyen, entre otros, procesos, subprocesos, eventos y otros objetos de sincronización, así como archivos, directorios y dispositivos.

Para cada tipo de objeto, los derechos genéricos de lectura, escritura y ejecución se asignan a derechos detallados específicos del objeto. Por ejemplo, para archivos y directorios, los posibles derechos incluyen el derecho a leer o escribir el archivo o directorio, el derecho a leer o escribir atributos de archivo extendidos, el derecho a recorrer un directorio y el derecho a escribir el descriptor de seguridad de un objeto.

El modelo de seguridad implica los siguientes conceptos:

  • Identificadores de seguridad (SID)
  • Tokens de acceso
  • Descriptores de seguridad
  • Listas de control de acceso (ACL)
  • Privilegios

Identificadores de seguridad (SID)

Un identificador de seguridad (SID, también denominado entidad de seguridad) identifica un usuario, un grupo o una sesión de inicio de sesión. Cada usuario tiene un SID único, que el sistema operativo recupera en el inicio de sesión.

Los SID son emitidos por una autoridad como el sistema operativo o un servidor de dominio. Algunos SID son conocidos y tienen nombres, así como identificadores. Por ejemplo, el SID S-1-1-0 identifica a todos (o mundo).

Tokens de acceso

Cada proceso tiene un token de acceso. El token de acceso describe el contexto de seguridad completo del proceso. Contiene el SID del usuario, el SID de los grupos a los que pertenece el usuario y el SID de la sesión de inicio de sesión, así como una lista de los privilegios de todo el sistema concedidos al usuario.

De forma predeterminada, el sistema usa el token de acceso principal para un proceso cada vez que un subproceso del proceso interactúa con un objeto protegible. Sin embargo, un subproceso puede suplantar una cuenta de cliente. Cuando un subproceso suplanta, tiene un token de suplantación además de su propio token principal. El token de suplantación describe el contexto de seguridad de la cuenta de usuario que el subproceso suplanta. La suplantación es especialmente común en el control de llamadas a procedimiento remoto (RPC).

Un token de acceso que describe un contexto de seguridad restringido para un subproceso o proceso se denomina token restringido. Los SID de un token restringido solo se pueden establecer para denegar el acceso, no permitir el acceso, a objetos protegibles. Además, el token puede describir un conjunto limitado de privilegios para todo el sistema. El SID y la identidad del usuario siguen siendo los mismos, pero los derechos de acceso del usuario están limitados mientras el proceso usa el token restringido. La función CreateRestrictedToken crea un token restringido.

Descriptores de seguridad

Cada objeto de Windows con nombre tiene un descriptor de seguridad; algunos objetos sin nombre también lo hacen. El descriptor de seguridad describe los SID de propietario y grupo del objeto junto con sus ACL.

Normalmente, la función que crea el objeto crea el descriptor de seguridad de un objeto. Cuando un controlador llama a la rutina IoCreateDevice o IoCreateDeviceSecure para crear un objeto de dispositivo, el sistema aplica un descriptor de seguridad al objeto de dispositivo creado y establece las ACL para el objeto. Para la mayoría de los dispositivos, las ACL se especifican en el archivo de información del dispositivo (INF).

Para obtener más información sobre los descriptores de seguridad en la documentación del controlador de kernel.

Listas de control de acceso

Access Control Listas (ACL) permiten un control específico sobre el acceso a los objetos. Una ACL forma parte del descriptor de seguridad de cada objeto.

Cada ACL contiene cero o más Access Control entradas (ACE). Cada ACE, a su vez, contiene un único SID que identifica un usuario, un grupo o un equipo y una lista de derechos denegados o permitidos para ese SID.

ACL para objetos de dispositivo

La ACL de un objeto de dispositivo se puede establecer de tres maneras:

  • Establezca en el descriptor de seguridad predeterminado para su tipo de dispositivo.
  • Creado mediante programación por la función RtlCreateSecurityDescriptor y establecido por la función RtlSetDaclSecurityDescriptor .
  • Se especifica en Security Descriptor Definition Language (SDDL) en el archivo INF del dispositivo o en una llamada a la rutina IoCreateDeviceSecure .

Todos los controladores deben usar SDDL en el archivo INF para especificar ACL para sus objetos de dispositivo.

SDDL es un lenguaje de descripción extensible que permite a los componentes crear ACL en un formato de cadena. SDDL lo usa el código en modo de usuario y en modo kernel. En la ilustración siguiente se muestra el formato de cadenas SDDL para objetos de dispositivo.

Diagrama que muestra el formato de las cadenas SDDL para los objetos de dispositivo.

El valor de Access especifica el tipo de acceso permitido. El valor de SID especifica un identificador de seguridad que determina a quién se aplica el valor de Access (por ejemplo, un usuario o grupo).

Por ejemplo, la siguiente cadena SDDL permite el acceso del sistema (SY) a todo y permite que todos los demás (WD) solo tengan acceso de lectura:

“D:P(A;;GA;;;SY)(A;;GR;;;WD)”

El archivo de encabezado wdmsec.h también incluye un conjunto de cadenas SDDL predefinidas que son adecuadas para objetos de dispositivo. Por ejemplo, el archivo de encabezado define SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX de la siguiente manera:

"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)"

El primer segmento de esta cadena permite que el kernel y el sistema operativo (SY) control total sobre el dispositivo. El segundo segmento permite a cualquier persona del grupo de administradores integrado (BA) acceder a todo el dispositivo, pero no cambiar la ACL. El tercer segmento permite a todos los usuarios (WD) leer o escribir en el dispositivo, y el cuarto segmento concede los mismos derechos al código que no es de confianza (RC). Los controladores pueden usar las cadenas predefinidas tal cual o como modelos para cadenas específicas del objeto del dispositivo.

Todos los objetos de dispositivo de una pila deben tener las mismas ACL. Al cambiar las ACL en un objeto de dispositivo de la pila, se cambian las ACL en toda la pila de dispositivos.

Sin embargo, agregar un nuevo objeto de dispositivo a la pila no cambia ninguna ACL, ya sea las del nuevo objeto de dispositivo (si tiene ACL) o las de cualquier objeto de dispositivo existente en la pila. Cuando un controlador crea un nuevo objeto de dispositivo y lo adjunta a la parte superior de la pila, el controlador debe copiar las ACL de la pila en el nuevo objeto de dispositivo copiando el campo DeviceObject.Characteristics desde el controlador inferior siguiente.

La rutina IoCreateDeviceSecure admite un subconjunto de cadenas SDDL que usan SID predefinidos, como WD y SY. Las API en modo de usuario y los archivos INF admiten la sintaxis completa de SDDL.

Comprobaciones de seguridad mediante ACL

Cuando un proceso solicita acceso a un objeto, las comprobaciones de seguridad comparan las ACL del objeto con los SID del token de acceso del autor de la llamada.

El sistema compara los ASE en un orden estricto de arriba abajo y se detiene en la primera coincidencia pertinente. Por lo tanto, al crear una ACL, siempre debe colocar los ACA de denegación por encima de las ACE de concesión correspondientes. En los ejemplos siguientes se muestra cómo continúa la comparación.

Ejemplo 1: Comparación de una ACL con un token de acceso

En el ejemplo 1 se muestra cómo el sistema compara una ACL con el token de acceso para el proceso de un llamador. Supongamos que el autor de la llamada quiere abrir un archivo que tenga la ACL que se muestra en la tabla siguiente.

ACL de archivo de ejemplo

Permiso SID Access
Allow Control Escritura, eliminación
Allow Sales Anexar
Denegar Información legal Anexar, escribir, eliminar
Allow Todos Leer

Esta ACL tiene cuatro ACE, que se aplican específicamente a los grupos Contabilidad, Ventas, Legal y Todos.

A continuación, supongamos que el token de acceso para el proceso de solicitud contiene SID para un usuario y tres grupos, en el orden siguiente:

Usuario Jim (S-1-5-21...)

Contabilidad de grupo (S-1-5-22...)

Grupo Legal (S-1-5-23...)

Agrupar a todos (S-1-1-0)

Al comparar una ACL de archivo con un token de acceso, el sistema busca primero una ACE para el usuario Jim en la ACL del archivo. Ninguno aparece, por lo que a continuación busca una ACE para el grupo Contabilidad. Como se muestra en la tabla anterior, una ACE para el grupo Contabilidad aparece como la primera entrada en la ACL del archivo, por lo que el proceso de Jim se concede el derecho de escribir o eliminar el archivo y la comparación se detiene. Si la ACE del grupo Legal precedió a la ACE para el grupo Contabilidad en la ACL, el proceso se denegaría el acceso de escritura, anexión y eliminación del archivo.

Ejemplo 2: Comparación de una ACL con un token restringido

El sistema compara una ACL con un token restringido de la misma manera que compara los de un token que no está restringido. Sin embargo, un SID de denegación en un token restringido solo puede coincidir con una ACE de denegación en una ACL.

En el ejemplo 2 se muestra cómo el sistema compara la ACL de un archivo con un token restringido. Supongamos que el archivo tiene la misma ACL que se muestra en la tabla anterior. Sin embargo, en este ejemplo, el proceso tiene un token restringido que contiene los siguientes SID:

Usuario Jim (S-1-5-21...) Negar

Contabilidad de grupo (S-1-5-22...) Negar

Grupo Legal (S-1-5-23...) Negar

Agrupar a todos (S-1-1-0)

La ACL del archivo no enumera el SID de Jim, por lo que el sistema procede al SID del grupo contabilidad. Aunque la ACL del archivo tiene una ACE para el grupo Contabilidad, esta ACE permite el acceso; por lo tanto, no coincide con el SID en el token restringido del proceso, que deniega el acceso. Como resultado, el sistema procede al SID del grupo legal. La ACL del archivo contiene una ACE para el grupo Legal que deniega el acceso, por lo que el proceso no puede escribir, anexar o eliminar el archivo.

Privilegios

Un privilegio es el derecho para que un usuario realice una operación relacionada con el sistema en el equipo local, como cargar un controlador, cambiar la hora o apagar el sistema.

Los privilegios son diferentes de los derechos de acceso porque se aplican a las tareas y recursos relacionados con el sistema en lugar de a los objetos, y porque un administrador del sistema les asigna a un usuario o grupo, en lugar de hacerlo por el sistema operativo.

El token de acceso de cada proceso contiene una lista de los privilegios concedidos al proceso. Los privilegios deben habilitarse específicamente antes de su uso. Para obtener más información sobre los privilegios, consulte Privilegios en la documentación del controlador de kernel.

Escenario del modelo de seguridad de Windows: Creación de un archivo

El sistema usa las construcciones de seguridad descritas en el modelo de seguridad de Windows cada vez que un proceso crea un identificador para un archivo u objeto.

En el diagrama siguiente se muestran las acciones relacionadas con la seguridad que se desencadenan cuando un proceso en modo de usuario intenta crear un archivo.

Diagrama de flujo que ilustra las acciones relacionadas con la seguridad cuando un proceso en modo de usuario intenta crear un archivo.

En el diagrama anterior se muestra cómo responde el sistema cuando una aplicación en modo de usuario llama a la función CreateFile . Las notas siguientes hacen referencia a los números en círculo de la ilustración:

  1. Una aplicación en modo de usuario llama a la función CreateFile y pasa un nombre de archivo de Microsoft Win32 válido.
  2. El modo de usuario Kernel32.dll pasa la solicitud a Ntdll.dll, que convierte el nombre de Win32 en un nombre de archivo nt de Microsoft Windows.
  3. Ntdll.dll llama a la función NtCreateFile con el nombre de archivo de Windows. Dentro de Ntoskrnl.exe, el Administrador de E/S controla NtCreateFile.
  4. El Administrador de E/S vuelve a empaquetar la solicitud en una llamada al Administrador de objetos.
  5. El Administrador de objetos resuelve vínculos simbólicos y garantiza que el usuario tenga derechos de recorrido para la ruta de acceso en la que se creará el archivo. Para obtener más información, vea Comprobaciones de seguridad en el Administrador de objetos.
  6. El Administrador de objetos llama al componente del sistema que posee el tipo de objeto subyacente asociado a la solicitud. Para una solicitud de creación de archivos, este componente es el Administrador de E/S, que posee objetos de dispositivo.
  7. El Administrador de E/S comprueba el descriptor de seguridad del objeto de dispositivo en el token de acceso del proceso del usuario para asegurarse de que el usuario tiene el acceso necesario al dispositivo. Para obtener más información, consulte Comprobaciones de seguridad en el Administrador de E/S.
  8. Si el proceso de usuario tiene el acceso necesario, el Administrador de E/S crea un identificador y envía una solicitud IRP_MJ_CREATE al controlador para el dispositivo o sistema de archivos.
  9. El controlador realiza comprobaciones de seguridad adicionales según sea necesario. Por ejemplo, si la solicitud especifica un objeto en el espacio de nombres del dispositivo, el controlador debe asegurarse de que el autor de la llamada tenga los derechos de acceso necesarios. Para obtener más información, consulte Comprobaciones de seguridad en el controlador.

Comprobaciones de seguridad en el Administrador de objetos

La responsabilidad de comprobar los derechos de acceso pertenece al componente de nivel más alto que puede realizar dichas comprobaciones. Si el Administrador de objetos puede comprobar los derechos de acceso del autor de la llamada, lo hace. Si no es así, el Administrador de objetos pasa la solicitud al componente responsable del tipo de objeto subyacente. Ese componente, a su vez, comprueba el acceso, si puede; si no es posible, pasa la solicitud a un componente todavía inferior, como un controlador.

El Administrador de objetos comprueba las ACL en busca de tipos de objeto simples, como eventos y bloqueos de exclusión mutua. En el caso de los objetos que tienen un espacio de nombres, el propietario del tipo realiza comprobaciones de seguridad. Por ejemplo, el Administrador de E/S se considera el propietario del tipo para objetos de dispositivo y objetos de archivo. Si el Administrador de objetos encuentra el nombre de un objeto de dispositivo o un objeto de archivo al analizar un nombre, entrega el nombre al Administrador de E/S, como en el escenario de creación de archivos presentado anteriormente. A continuación, el Administrador de E/S comprueba los derechos de acceso si es posible. Si el nombre especifica un objeto dentro de un espacio de nombres de dispositivo, el Administrador de E/S a su vez entrega el nombre al controlador del dispositivo (o sistema de archivos) y ese controlador es responsable de validar el acceso solicitado.

Comprobaciones de seguridad en el Administrador de E/S

Cuando el Administrador de E/S crea un identificador, comprueba los derechos del objeto en relación con el token de acceso del proceso y, a continuación, almacena los derechos concedidos al usuario junto con el identificador. Cuando lleguen solicitudes de E/S posteriores, el Administrador de E/S comprueba los derechos asociados al identificador para asegurarse de que el proceso tiene derecho a realizar la operación de E/S solicitada. Por ejemplo, si el proceso solicita posteriormente una operación de escritura, el Administrador de E/S comprueba los derechos asociados al identificador para asegurarse de que el autor de la llamada tiene acceso de escritura al objeto.

Si el identificador está duplicado, los derechos se pueden quitar de la copia, pero no se pueden agregar a él.

Cuando el Administrador de E/S crea un objeto, convierte los modos de acceso genéricos de Win32 a derechos específicos del objeto. Por ejemplo, los siguientes derechos se aplican a los archivos y directorios:

Modo de acceso Win32 Derechos específicos del objeto
GENERIC_READ ReadData
GENERIC_WRITE WriteData
GENERIC_EXECUTE ReadAttributes
GENERIC_ALL Todo

Para crear un archivo, un proceso debe tener derechos de recorrido a los directorios primarios en la ruta de acceso de destino. Por ejemplo, para crear \Device\CDROM0\Directory\File.txt, un proceso debe tener derecho a atravesar \Device, \Device\CDROM0 y \Device\CDROM0\Directory. El Administrador de E/S comprueba solo los derechos transversales de estos directorios.

El Administrador de E/S comprueba los derechos transversales cuando analiza el nombre de archivo. Si el nombre de archivo es un vínculo simbólico, el Administrador de E/S lo resuelve en una ruta de acceso completa y, a continuación, comprueba los derechos transversales, empezando por la raíz. Por ejemplo, suponga que el vínculo simbólico \DosDevices\D se asigna al nombre del dispositivo Windows NT \Device\CDROM0. El proceso debe tener derechos transversales en el directorio \Device.

Para obtener más información, vea Identificadores de objetos y Seguridad de objetos.

Comprobaciones de seguridad en el controlador

El kernel del sistema operativo trata todos los controladores, en efecto, como un sistema de archivos con su propio espacio de nombres. Por lo tanto, cuando un autor de la llamada intenta crear un objeto en el espacio de nombres del dispositivo, el Administrador de E/S comprueba que el proceso tiene derechos de recorrido a los directorios de la ruta de acceso.

Con los controladores WDM, el Administrador de E/S no realiza comprobaciones de seguridad en el espacio de nombres, a menos que se haya creado el objeto device que especifique FILE_DEVICE_SECURE_OPEN. Cuando no se establece FILE_DEVICE_SECURE_OPEN, el controlador es responsable de garantizar la seguridad de su espacio de nombres. Para obtener más información, consulte Control del acceso al espacio de nombres del dispositivo y protección de objetos de dispositivo.

En el caso de los controladores WDF, la marca de FILE_DEVICE_SECURE_OPEN siempre se establece, de modo que habrá una comprobación del descriptor de seguridad del dispositivo antes de permitir que una aplicación acceda a cualquier nombre dentro del espacio de nombres del dispositivo. Para obtener más información, vea Controlar el acceso a dispositivos en controladores KMDF.

Límites de seguridad de Windows

Los controladores que se comunican entre sí y con los autores de llamadas en modo de usuario de distintos niveles de privilegios se pueden considerar que cruzan un límite de confianza. Un límite de confianza es cualquier ruta de acceso de ejecución de código que cruza desde un proceso con privilegios inferiores en un proceso con privilegios más elevados.

Cuanto mayor sea la disparidad en los niveles de privilegios, más interesante será el límite para los atacantes que quieran realizar ataques, como un ataque de escalación de privilegios contra el controlador o proceso de destino.

Parte del proceso de creación de un modelo de amenazas es examinar los límites de seguridad y buscar rutas de acceso imprevistas. Para obtener más información, consulte Modelado de amenazas para controladores.

Los datos que cruzan un límite de confianza no son de confianza y se deben validar.

En este diagrama se muestran tres controladores de kernel y dos aplicaciones, una en un contenedor de aplicaciones y una aplicación que se ejecuta con derechos de administrador. Las líneas rojas indican límites de confianza de ejemplo.

Diagrama que muestra la superficie expuesta a ataques de controladores con tres controladores de kernel, una aplicación en un contenedor de aplicaciones y una aplicación con derechos de administrador.

Como el contenedor de aplicaciones puede proporcionar restricciones adicionales y no se ejecuta en el nivel de administrador, la ruta de acceso (1) es una ruta de acceso de riesgo mayor para un ataque de escalación, ya que el límite de confianza está entre un contenedor de aplicaciones (un proceso de privilegios muy bajo) y un controlador de kernel.

La ruta de acceso (2) es una ruta de acceso de menor riesgo, ya que la aplicación se ejecuta con derechos de administrador y llama directamente al controlador del kernel. Administración ya es un privilegio bastante alto en el sistema, por lo que la superficie expuesta a ataques del administrador al kernel es menos interesante para los atacantes, pero sigue siendo un límite de confianza notable.

Ruta de acceso (3) es un ejemplo de una ruta de acceso de ejecución de código que cruza varios límites de confianza que podrían perderse si no se crea un modelo de amenazas. En este ejemplo, hay un límite de confianza entre el controlador 1 y el controlador 3, ya que el controlador 1 toma la entrada de la aplicación en modo de usuario y la pasa directamente al controlador 3.

Todas las entradas que entran en el controlador desde el modo de usuario no son de confianza y deben validarse. Las entradas procedentes de otros controladores también pueden no ser de confianza en función de si el controlador anterior era simplemente un paso a través simple (por ejemplo, el controlador 1 recibió datos del controlador 1 de la aplicación 1 , el controlador 1 no realizó ninguna validación en los datos y acaba de pasarlo al controlador 3). Asegúrese de identificar todas las superficies de ataque y los límites de confianza y validar todos los datos que los cruzan, mediante la creación de un modelo de amenazas completo.

Recomendaciones del modelo de Seguridad de Windows

  • Establezca ACL predeterminadas seguras en las llamadas a la rutina IoCreateDeviceSecure .
  • Especifique las ACL en el archivo INF para cada dispositivo. Estas ACL pueden aflojar ACL predeterminadas ajustadas si es necesario.
  • Establezca la característica FILE_DEVICE_SECURE_OPEN para aplicar la configuración de seguridad de objetos de dispositivo al espacio de nombres del dispositivo.
  • No defina las ICTL que permitan FILE_ANY_ACCESS a menos que dicho acceso no se pueda aprovechar de forma malintencionada.
  • Use la rutina IoValidateDeviceIoControlAccess para reforzar la seguridad de los IOCTLS existentes que permiten FILE_ANY_ACCESS.
  • Cree un modelo de amenazas para examinar los límites de seguridad y busque rutas de acceso imprevistas. Para obtener más información, consulte Modelado de amenazas para controladores.
  • Consulte Lista de comprobación de seguridad del controlador para obtener recomendaciones de seguridad de controladores adicionales.

Consulte también

Protección de objetos de dispositivo

Lista de comprobación de seguridad del controlador