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 de 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 de lectura, escritura y ejecución genéricos 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 atravesar 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 a 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 los emite 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 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 para 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 para 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
Todos los objetos Windows con nombre tienen un descriptor de seguridad; algunos objetos sin nombre también lo tienen. 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 del 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, consulte Descriptores de seguridad en la documentación del controlador de kernel.
Listas de control de acceso
Las listas de control de acceso (ACL) permiten un control específico sobre el acceso a los objetos. Una ACL forma parte del descriptor de seguridad para cada objeto.
Cada ACL contiene cero o más entradas de control de acceso (ACE). Cada ACE, a su vez, contiene un único SID que identifica a un usuario, grupo o 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:
- Se puede establecer en el descriptor de seguridad predeterminado para su tipo de dispositivo.
- Se puede crear mediante programación con la función RtlCreateSecurityDescriptor y se puede establecer con la función RtlSetDaclSecurityDescriptor.
- Se puede especificar en el Lenguaje de definición de descriptores de seguridad (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 tanto el modo de usuario como el código en modo kernel. En la ilustración siguiente se muestra el formato de las cadenas SDDL para 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 al kernel y al sistema operativo (SY) un control total sobre el dispositivo. El segundo segmento permite a cualquier persona del grupo de administradores integrados (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 a código que no es de confianza (RC). Los controladores pueden utilizar las cadenas predefinidas tal cual o como modelos para cadenas específicas de objetos de dispositivo.
Todos los objetos de dispositivo de una pila deben tener las mismas ACL. Cambiar las ACL en un objeto de dispositivo de la pila cambia las ACL en toda la pila de dispositivos.
Sin embargo, agregar un nuevo objeto de dispositivo a la pila no cambia las ACL, ni las del nuevo objeto de dispositivo (si tiene ACL) ni las de cualquier objeto de dispositivo existente en la pila. Cuando un controlador crea un nuevo objeto de dispositivo y lo asocia 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 siguiente controlador inferior.
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 respecto a los SID del token de acceso del llamador.
El sistema compara las ACE 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 las ACE 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 llamador quiere abrir un archivo que tenga la ACL que se muestra en la tabla siguiente.
ACL de archivo de ejemplo
Permiso | SID | Access |
---|---|---|
Permitir | Control | Escritura, eliminación |
Permitir | Ventas | Anexar |
Denegar | Información legal | Anexión, escritura, eliminación |
Permitir | 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...)
Grupo Contabilidad (S-1-5-22...)
Grupo Legal (S-1-5-23...)
Grupo 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 al proceso de Jim se concede el derecho de escritura o eliminación para el archivo y la comparación se detiene. Si la ACE del grupo Legal precedió a la ACE del grupo Contabilidad en la ACL, se denegaría al proceso 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 las 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...) Denegar
Grupo Contabilidad (S-1-5-22...) Denegar
Grupo Legal (S-1-5-23...) Denegar
Grupo Todos (S-1-1-0)
La ACL del archivo no enumera el SID de Jim, por lo que el sistema continúa con el 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 continúa con el 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 ni eliminar el archivo.
Privilegios
Un privilegio es el derecho de un usuario a realizar 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 tareas y recursos relacionados con el sistema en lugar de a objetos, y porque un administrador del sistema los asigna a un usuario o grupo, en lugar de a un sistema operativo.
El token de acceso de cada proceso contiene una lista de los privilegios concedidos al proceso. Los privilegios deben estar habilitados 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 o 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.
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 se refieren a los números marcados con un círculo en la ilustración:
- Una aplicación en modo de usuario llama a la función CreateFile y pasa un nombre de archivo de Microsoft Win32 válido.
- El modo de usuario Kernel32.dll pasa la solicitud a Ntdll.dll, que convierte el nombre Win32 en un nombre de archivo de Microsoft Windows NT.
- 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.
- El Administrador de E/S vuelve a empaquetar la solicitud en una llamada del Administrador de objetos.
- El Administrador de objetos resuelve vínculos simbólicos y garantiza que el usuario tenga derechos transversales para la ruta de acceso en la que se creará el archivo. Para obtener más información, consulte Comprobaciones de seguridad en el Administrador de objetos.
- 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.
- 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.
- 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 el sistema de archivos.
- 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 llamador 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 superior que puede realizar dichas comprobaciones. Si el Administrador de objetos puede comprobar los derechos de acceso del llamador, 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 así, pasa la solicitud a un componente aún inferior, como un controlador.
El Administrador de objetos comprueba las ACL de tipos de objetos simples, como eventos y bloqueos de exclusión mutua. Para 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. Después, el Administrador de E/S comprueba los derechos de acceso, si puede. 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 llegan las 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 más adelante solicita una operación de escritura, el Administrador de E/S comprueba los derechos asociados al identificador para asegurarse de que el llamador 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 ella.
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 archivos y directorios:
Modo de acceso Win32 | Derechos específicos del objeto |
---|---|
GENERIC_READ | ReadData |
GENERIC_WRITE | WriteData |
GENERIC_EXECUTE | ReadAttributes |
GENERIC_ALL | All |
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 el derecho de atravesar \Device, \Device\CDROM0 y \Device\CDROM0\Directory. El Administrador de E/S solo comprueba los derechos de recorrido de estos directorios.
El Administrador de E/S comprueba los derechos de recorrido 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 de recorrido, empezando por la raíz. Por ejemplo, supongamos que el vínculo simbólico \DosDevices\D se asigna al nombre del dispositivo Windows NT \Device\CDROM0. El proceso debe tener derechos de recorrido en el directorio \Device.
Para obtener más información, consulte 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 llamador 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 de dispositivo 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 está establecida, 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 más información, consulte Control de acceso a dispositivos en controladores KMDF.
Límites de seguridad de Windows
Los controladores que se comunican entre sí y con los llamadores 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 ejecución de código que cruza de un proceso con privilegios inferiores a un proceso con privilegios superiores.
Cuanto mayor sea la disparidad en los niveles de privilegio, más interesante será el límite para los atacantes que quieran realizar ataques, como un ataque de escalada 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 deben validarse.
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.
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 escalada, ya que el límite de confianza está entre un contenedor de aplicaciones (un proceso con privilegios muy bajos) 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 de kernel. El privilegio de Administrador ya es 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 digno de mención.
la 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 lo 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 tránsito 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 lo acaba de pasar 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 relajar las 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 IOTCL que permitan FILE_ANY_ACCESS, a menos que este acceso no se pueda explotar malintencionadamente.
- Use la rutina IoValidateDeviceIoControlAccess para reforzar la seguridad de los IOCTL 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.