Modèle de sécurité de Windows pour les développeurs de pilotes
Le modèle de sécurité de Windows est basé sur des objets sécurisables. Chaque composant du système d’exploitation doit garantir la sécurité des objets dont il est responsable. Les pilotes doivent donc protéger la sécurité de leurs appareils et de leurs objets d’appareil.
Cette rubrique résume la façon dont le modèle de sécurité de Windows s’applique aux pilotes en mode noyau.
Modèle de sécurité de Windows
Le modèle de sécurité de Windows est principalement basé sur les droits par objet, avec un petit nombre de privilèges à l’échelle du système. Les objets qui peuvent être sécurisés incluent, sans s’y limiter, les processus, les threads, les événements et d’autres objets de synchronisation, ainsi que les fichiers, les répertoires et les appareils.
Pour chaque type d’objet, les droits génériques de lecture, d’écriture et d’exécution se traduisent par des droits détaillés spécifiques à l’objet. Par exemple, pour les fichiers et les répertoires, les droits potentiels incluent le droit de lire ou d’écrire le fichier ou le répertoire, le droit de lire ou d’écrire des attributs de fichier étendus, le droit de parcourir un répertoire et le droit d’écrire le descripteur de sécurité d’un objet.
Le modèle de sécurité comprend les concepts suivants :
- Identificateurs de sécurité (SID)
- Jetons d’accès
- Descripteurs de sécurité
- Listes de contrôle d’accès (ACL)
- Privilèges
Identificateurs de sécurité (SID)
Un identificateur de sécurité (SID, également appelé principal) identifie un utilisateur, un groupe ou une ouverture de session. Chaque utilisateur possède un SID unique, qui est récupéré par le système d’exploitation lors de l’ouverture de session.
Les SID sont émis par une autorité comme le système d’exploitation ou un serveur de domaine. Certains SID sont connus et ont des noms ainsi que des identificateurs. Par exemple, le SID S-1-1-0 identifie Tout le monde (ou Monde).
Jetons d’accès
Chaque processus a un jeton d’accès. Le jeton d’accès décrit le contexte de sécurité complet du processus. Il contient le SID de l’utilisateur, le SID des groupes auxquels appartient l’utilisateur et le SID de l’ouverture de session, ainsi qu’une liste des privilèges à l’échelle du système accordés à l’utilisateur.
Par défaut, le système utilise le jeton d’accès principal pour un processus chaque fois qu’un thread de ce dernier interagit avec un objet sécurisable. Toutefois, un thread peut emprunter l’identité d’un compte client. Lorsqu’un thread emprunte l’identité, il possède un jeton d’emprunt d’identité en plus de son propre jeton principal. Le jeton d’emprunt d’identité décrit le contexte de sécurité du compte d’utilisateur dont le thread emprunte l’identité. L’emprunt d’identité est particulièrement courant dans la gestion des appels de procédure distante (RPC).
On appelle jeton restreint, un jeton d’accès qui décrit un contexte de sécurité restreint pour un thread ou un processus. Les SID d’un jeton restreint peuvent être définis uniquement pour refuser l’accès, et non pour l’autoriser, aux objets sécurisables. En outre, le jeton peut décrire un ensemble limité de privilèges à l’échelle du système. Le SID et l’identité de l’utilisateur ne changent pas, mais les droits d’accès de l’utilisateur sont limités pendant que le processus utilise le jeton restreint. La fonction CreateRestrictedToken crée un jeton restreint.
Descripteurs de sécurité
Chaque objet Windows nommé possède un descripteur de sécurité ; certains objets non nommés aussi. Le descripteur de sécurité décrit les SID du propriétaire et du groupe de l’objet, ainsi que ses listes de contrôle d’accès.
Le descripteur de sécurité d’un objet est généralement créé par la fonction qui crée l’objet. Lorsqu’un pilote appelle la routine IoCreateDevice ou IoCreateDeviceSecure pour créer un objet d’appareil, le système applique un descripteur de sécurité à ce dernier et définit des listes de contrôle d’accès pour l’objet. Pour la plupart des appareils, les listes de contrôle d’accès sont spécifiées dans le fichier d’informations sur l’appareil (INF).
Pour en savoir plus sur les descripteurs de sécurité, consultez la documentation du pilote du noyau.
Listes de contrôle d’accès
Les listes de contrôle d’accès (ACL) permettent un contrôle précis sur l’accès aux objets. Une liste de contrôle d’accès fait partie du descripteur de sécurité pour chaque objet.
Chaque liste de contrôle d’accès contient zéro ou plusieurs entrées de contrôle d’accès (ACE). Chaque ACE contient à son tour un SEUL SID qui identifie un utilisateur, un groupe ou un ordinateur et une liste de droits refusés ou accordés pour ce SID.
Listes de contrôle d’accès pour les objets d’appareil
La liste de contrôle d’accès d’un objet d’appareil peut être définie de trois façons :
- Définie dans le descripteur de sécurité par défaut pour son type d’appareil.
- Créée par programmation par la fonction RtlCreateSecurityDescriptor et définie par la fonction RtlSetDaclSecurityDescriptor.
- Spécifiée dans le langage SDDL (Security Descriptor Definition Language) dans le fichier INF de l’appareil ou dans un appel à la routine IoCreateDeviceSecure.
Tous les pilotes doivent utiliser le langage SDDL dans le fichier INF pour spécifier des listes de contrôle d’accès pour leurs objets d’appareil.
Le SDDL est un langage de description extensible qui permet aux composants de créer des listes de contrôle d’accès au format chaîne. Le SDDL est utilisé par le code en mode utilisateur et en mode noyau. Le schéma suivant montre le format des chaînes SDDL pour les objets d’appareil.
La valeur d’accès spécifie le type d’accès autorisé. La valeur SID spécifie un identifiant de sécurité qui détermine à qui s’applique la valeur d’accès (par exemple, un utilisateur ou un groupe).
Par exemple, la chaîne SDDL suivante permet au système (SY) d’accéder à tout et autorise tout le monde (WD) à accéder uniquement en lecture :
“D:P(A;;GA;;;SY)(A;;GR;;;WD)”
Le fichier d’en-tête wdmsec.h inclut également un ensemble de chaînes SDDL prédéfinies qui conviennent aux objets d’appareil. Par exemple, le fichier d’en-tête définit SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX comme suit :
"D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGWGX;;;RC)"
Le premier segment de cette chaîne permet au noyau et au système d’exploitation (SY) de contrôler entièrement l’appareil. Le second segment permet à toute personne appartenant au groupe Administrateurs intégré (BA) d’accéder à la totalité de l’appareil, mais pas de modifier la liste de contrôle d’accès. Le troisième segment permet à tout le monde (WD) de lire ou d’écrire sur l’appareil, et le quatrième segment accorde les mêmes droits au code non approuvé (RC). Les pilotes peuvent utiliser les chaînes prédéfinies telles qu’elles ou comme modèles pour les chaînes spécifiques à l’objet d’appareil.
Tous les objets d’appareil d’une pile doivent avoir les mêmes listes de contrôle d’accès. La modification des listes de contrôle d’accès d’un objet d’appareil de la pile modifie celles de toute la pile d’appareils.
Cependant, l’ajout d’un nouvel objet d’équipement à la pile ne modifie aucune liste de contrôle d’accès, que ce soit celles du nouvel objet d’appareil (s’il en possède) ou celles des objets d’appareil existants dans la pile. Lorsqu’un pilote crée un objet d’appareil et l’attache au haut de la pile, il doit copier les listes de contrôle d’accès de la pile sur le nouvel objet d’appareil en copiant le champ DeviceObject.Characteristics du pilote immédiatement inférieur.
La routine IoCreateDeviceSecure prend en charge un sous-ensemble de chaînes SDDL qui utilisent des SID prédéfinis tels que WD et SY. Les API en mode utilisateur et les fichiers INF prennent en charge la syntaxe SDDL complète.
Vérifications de sécurité à l’aide des listes de contrôle d’accès
Lorsqu’un processus demande l’accès à un objet, les vérifications de sécurité comparent les listes de contrôle d’accès de l’objet aux SID dans le jeton d’accès de l’appelant.
Le système compare les ACE dans un ordre descendant strict et s’arrête sur la première correspondance pertinente. Par conséquent, lors de la création d’une liste de contrôle d’accès, il faut toujours placer les ACE de refus au-dessus des ACE d’octroi correspondantes. Les exemples suivants montrent comment se déroule la comparaison.
Exemple 1 : comparaison entre une liste de contrôle d’accès et un jeton d’accès
L’exemple 1 montre comment le système compare une liste de contrôle d’accès au jeton d’accès du processus de l’appelant. Supposons que l’appelant souhaite ouvrir un fichier dont la liste de contrôle d’accès est affichée dans le tableau suivant.
Exemple de fichier de liste de contrôle d’accès
Autorisation | SID | Access |
---|---|---|
Autoriser | Comptabilité | Écriture, suppression |
Autoriser | Vente | Ajouter |
Deny | Informations juridiques | Ajout, écriture, suppression |
Autoriser | Tout le monde | Lire |
Cette liste de contrôle d’accès comprend quatre ACL, qui s’appliquent spécifiquement aux groupes Comptabilité, Vente, Informations juridiques et Tout le monde.
Supposons ensuite que le jeton d’accès pour le processus demandeur contient des SID pour un utilisateur et trois groupes, dans l’ordre suivant :
Utilisateur Jim (S-1-5-21...)
Groupe Comptabilité (S-1-5-22...)
Groupe Informations juridiques (S-1-5-23...)
Groupe Tout le monde (S-1-1-0)
Lorsqu’il compare une liste de contrôle d’accès à un fichier avec un jeton d’accès, le système recherche d’abord une ACE pour l’utilisateur Jim dans la liste de contrôle d’accès du fichier. Aucune n’apparaît. Par conséquent, il recherche une ACE pour le groupe Comptabilité. Comme indiqué dans le tableau précédent, une ACE pour le groupe Comptabilité apparaît comme la première entrée dans la liste de contrôle d’accès du fichier. Le processus de Jim est donc autorisé à écrire ou à supprimer le fichier et la comparaison s’arrête. Si l’ACE du groupe Informations juridiques précède l’ACE du groupe Comptabilité dans la liste de contrôle d’accès, le processus se verra refuser l’accès en écriture, en ajout et en suppression au fichier.
Exemple 2 : Comparaison entre une liste de contrôle d’accès et un jeton restreint
Le système compare une liste de contrôle d’accès à un jeton restreint de la même façon qu’il compare celles d’un jeton qui n’est pas restreint. Toutefois, un SID de refus dans un jeton restreint ne peut correspondre qu’à une ACE de refus dans une liste de contrôle d’accès.
L’exemple 2 montre comment le système compare une liste de contrôle d’accès d’un fichier à un jeton restreint. Supposons que le fichier a la même liste de contrôle d’accès affichée dans le tableau précédent. Dans cet exemple, toutefois, le processus a un jeton restreint qui contient les SID suivants :
Utilisateur Jim (S-1-5-21...) Refus
Groupe Comptabilité (S-1-5-22...) Refus
Groupe Informations juridiques (S-1-5-23...) Refus
Groupe Tout le monde (S-1-1-0)
La liste de contrôle d’accès du fichier ne répertorie pas le SID de Jim. Par conséquent, le système passe au SID du groupe Comptabilité. Bien que la liste de contrôle d’accès du fichier dispose d’une ACE pour le groupe Comptabilité, cette ACE autorise l’accès ; par conséquent, elle ne correspond pas au SID dans le jeton restreint du processus, qui refuse l’accès. Le système passe donc au SID du groupe Informations juridiques. La liste de contrôle d’accès du fichier contient une ACE pour le groupe Informations juridiques qui refuse l’accès, de sorte que le processus ne peut pas écrire, ajouter ou supprimer le fichier.
Privilèges
Un privilège est le droit pour un utilisateur d’effectuer une opération liée au système sur l’ordinateur local, par exemple charger un pilote, modifier l’heure ou arrêter le système.
Les privilèges sont différents des droits d’accès, car ils s’appliquent aux tâches et ressources liées au système plutôt qu’aux objets, et parce qu’ils sont affectés à un utilisateur ou à un groupe par un administrateur système, plutôt que par le système d’exploitation.
Le jeton d’accès pour chaque processus contient une liste des privilèges accordés au processus. Les privilèges doivent être spécifiquement activés avant l’utilisation. Pour en savoir plus sur les privilèges, consultez les Privilèges dans la documentation du pilote du noyau.
Scénario de modèle de sécurité de Windows : création d’un fichier
Le système utilise les constructions de sécurité décrites dans le modèle de sécurité de Windows chaque fois qu’un processus crée un handle dans un fichier ou un objet.
Le schéma suivant présente les actions liées à la sécurité qui sont déclenchées lorsqu’un processus en mode utilisateur tente de créer un fichier.
Le schéma précédent montre la réponse du système quand une application en mode utilisateur appelle la fonction CreateFile. Les notes suivantes se rapportent aux chiffres dans un cercle dans la figure :
- Une application en mode utilisateur appelle la fonction CreateFile , en transmettant un nom de fichier Microsoft Win32 valide.
- Le mode utilisateur Kernel32.dll transmet la requête à Ntdll.dll, qui convertit le nom Win32 en nom de fichier Microsoft Windows NT.
- Ntdll.dll appelle la fonction NtCreateFile avec le nom de fichier Windows. Dans Ntoskrnl.exe, le gestionnaire d’E/S gère NtCreateFile.
- Le gestionnaire d’E/S replace dans un package la requête dans un appel du gestionnaire d’objets.
- Le gestionnaire d’objets résout les liens symboliques et garantit que l’utilisateur dispose de droits de traversée pour le chemin d’accès dans lequel le fichier sera créé. Pour en savoir plus, consultez Vérifications de sécurité dans le gestionnaire d’objets.
- Le gestionnaire d’objets appelle le composant système propriétaire du type d’objet sous-jacent associé à la requête. Pour une demande de création de fichier, ce composant est le gestionnaire d’E/S, qui possède des objets d’appareil.
- Le gestionnaire d’E/S vérifie le descripteur de sécurité de l’objet d’appareil par rapport au jeton d’accès du processus de l’utilisateur afin de s’assurer que ce dernier dispose de l’accès requis à l’appareil. Pour en savoir plus, consultez Vérifications de sécurité dans le gestionnaire d’E/S.
- Si le processus utilisateur dispose de l’accès requis, le gestionnaire d’E/S crée un handle et envoie une demande IRP_MJ_CREATE au pilote pour l’appareil ou le système de fichiers.
- Le pilote effectue des vérifications de sécurité supplémentaires en fonction des besoins. Par exemple, si la demande spécifie un objet dans l’espace de noms de l’appareil, le pilote doit s’assurer que l’appelant dispose des droits d’accès requis. Pour en savoir plus, consultez Vérifications de sécurité dans le pilote.
Vérifications de sécurité dans le gestionnaire d’objets
La responsabilité de la vérification des droits d’accès incombe au composant de plus haut niveau qui peut effectuer ces vérifications. Si le gestionnaire d’objets peut vérifier les droits d’accès de l’appelant, il le fait. Dans le cas contraire, il transmet la demande au composant responsable du type d’objet sous-jacent. Ce composant vérifie à son tour l’accès, s’il le peut. Dans le cas contraire, il transmet la requête à un composant encore inférieur, tel qu’un pilote.
Le gestionnaire d’objets vérifie les listes de contrôle d’accès pour les types d’objets simples, notamment les événements et les verrous mutex. Pour les objets qui possèdent un espace de noms, le propriétaire du type effectue des vérifications de sécurité. Par exemple, le gestionnaire d’E/S est considéré comme le propriétaire de type pour les objets d’appareil et les objets de fichier. S’il trouve le nom d’un objet d’appareil ou d’un objet de fichier lors de l’analyse d’un nom, il remet le nom au gestionnaire d’E/S, comme dans le scénario de création de fichier présenté ci-dessus. Le gestionnaire d’E/S vérifie ensuite les droits d’accès s’il le peut. Si le nom spécifie un objet au sein d’un espace de noms d’appareil, le gestionnaire d’E/S transmet le nom du pilote d’appareil (ou de système de fichiers), lequel est chargé de valider l’accès demandé.
Vérifications de sécurité dans le gestionnaire d’E/S
Lorsque le gestionnaire d’E/S crée un handle, il vérifie les droits de l’objet par rapport au jeton d’accès de processus, puis stocke les droits accordés à l’utilisateur ainsi que le handle. Lorsque des demandes d’E/S ultérieures arrivent, le gestionnaire d’E/S vérifie les droits associés au handle pour s’assurer que le processus a le droit d’effectuer l’opération d’E/S demandée. Par exemple, si le processus demande ultérieurement une opération d’écriture, le gestionnaire d’E/S vérifie les droits associés au handle pour s’assurer que l’appelant dispose de l’accès en écriture à l’objet.
Si la poignée est dupliquée, les droits peuvent être retirés de la copie, mais pas ajoutés.
Lorsque le gestionnaire d’E/S crée un objet, il convertit les modes d’accès Win32 génériques en droits spécifiques à l’objet. Par exemple, les droits suivants s’appliquent aux fichiers et aux répertoires :
Mode d’accès Win32 | Droits spécifiques à l’objet |
---|---|
GENERIC_READ | ReadData |
GENERIC_WRITE | WriteData |
GENERIC_EXECUTE | ReadAttributes |
GENERIC_ALL | Tous |
Pour créer un fichier, un processus doit disposer de droits de traversée vers les répertoires parents dans le chemin d’accès cible. Par exemple, pour créer \Device\CDROM0\Directory\File.txt, un processus doit avoir le droit de traverser \Device, \Device\CDROM0 et \Device\CDROM0\Directory. Le gestionnaire d’E/S vérifie uniquement les droits de traversée pour ces répertoires.
Le gestionnaire d’E/S vérifie les droits de traversée lorsqu’il analyse le nom du fichier. Si le nom de fichier est un lien symbolique, le gestionnaire d’E/S le résout en chemin d’accès complet, puis vérifie les droits de traversée, en commençant par la racine. Par exemple, supposons que le lien symbolique \DosDevices\D renvoie au nom de l’appareil Windows NT \Device\CDROM0. Le processus doit disposer de droits de traversée vers le répertoire \Device.
Pour en savoir plus, consultez Handles d’objets et Sécurité des objets.
Vérifications de sécurité dans le pilote
Dans les faits, le noyau du système d’exploitation traite chaque pilote comme un système de fichiers avec son propre espace de noms. Par conséquent, quand un appelant tente de créer un objet dans l’espace de noms de l’appareil, le gestionnaire d’E/S s’assure que le processus dispose des droits de traversée vers les répertoires dans le chemin d’accès.
Avec les pilotes WDM, le gestionnaire d’E/S n’effectue pas de vérifications de sécurité dans l’espace de noms, sauf si l’objet d’appareil a été créé en spécifiant FILE_DEVICE_SECURE_OPEN. Lorsque FILE_DEVICE_SECURE_OPEN n’est pas défini, le pilote est responsable de la sécurité de son espace de noms. Pour en savoir plus, consultez Contrôle de l’accès à l’espace de noms de l’appareil et Sécurisation des objets d’appareil.
Pour les pilotes WDF, l’indicateur FILE_DEVICE_SECURE_OPEN est toujours défini, de sorte qu’il y aura une vérification du descripteur de sécurité de l’appareil avant d’autoriser une application à accéder à tout nom dans l’espace de noms de l’appareil. Pour en savoir plus, consulter Contrôle de l’accès des appareils dans les pilotes KMDF.
Limites de la sécurité de Windows
Les pilotes qui communiquent entre eux et avec des appelants en mode utilisateur ayant des niveaux de privilège différents peuvent être considérés comme franchissant une limite de confiance. Une limite d’approbation est un chemin d’exécution de code qui passe d’un processus à privilèges inférieurs à un processus à privilèges supérieurs.
Plus la disparité des niveaux de privilèges est grande, plus la limite est intéressante pour les malfaiteurs qui souhaitent mener des attaques telles que l’escalade des privilèges contre le pilote ou le processus ciblé.
Une partie du processus de création d’un modèle de menace consiste à examiner les limites de sécurité et à rechercher des chemins imprévus. Pour en savoir plus, consultez Modélisation des menaces pour les pilotes.
Toutes les données qui franchissent une limite d’approbation ne sont pas fiables et doivent être validées.
Ce schéma montre trois pilotes de noyau et deux applications, une dans un conteneur d’applications et une autre qui s’exécute avec des droits d’administrateur. Les lignes rouges indiquent des exemples de limites d’approbation.
Comme le conteneur d’application peut fournir des contraintes supplémentaires et qu’il n’est pas exécuté au niveau administrateur, le chemin (1) est à plus haut risque pour une attaque d’escalade, puisque la limite d’approbation se situe entre un conteneur d’application (un processus à très faible privilège) et un pilote de noyau.
Le chemin d’accès (2) présente moins de risques, car l’application s’exécute avec des droits d’administrateur et appelle directement dans le pilote du noyau. L’administrateur dispose déjà d’un privilège assez élevé sur le système, de sorte que la surface d’attaque entre l’administrateur et le noyau constitue une cible moins intéressante pour les attaquants, mais reste une limite d’approbation digne d’intérêt.
Le chemin d’accès (3) est un exemple de chemin d’exécution de code qui franchit plusieurs limites d’approbation, qui peuvent être manquées si un modèle de menace n’est pas créé. Dans cet exemple, il existe une limite d’approbation entre le pilote 1 et le pilote 3, car le pilote 1 utilise les entrées de l’application en mode utilisateur et les transmet directement au pilote 3.
Toutes les entrées provenant du mode utilisateur dans le pilote ne sont pas fiables et doivent être validées. Les entrées provenant d’autres pilotes peuvent également ne pas être fiables, dans la mesure où le pilote précédent n’était qu’un simple pass-through (par exemple, le pilote 1 a reçu des données de l’application 1, le pilote 1 n’a procédé à aucune validation des données et les a simplement transmises au pilote 3). Veillez à identifier toutes les surfaces d’attaque et les limites d’approbation, et à valider toutes les données qui les traversent, en créant un modèle de menace complet.
Recommandations pour le modèle de sécurité de Windows
- Définissez des listes de contrôle d’accès par défaut robustes dans les appels à la routine IoCreateDeviceSecure .
- Spécifiez des listes de contrôle d’accès dans le fichier INF pour chaque appareil. Ces listes de contrôle d’accès peuvent assouplir celles par défaut si nécessaire.
- Définissez la caractéristique FILE_DEVICE_SECURE_OPEN pour appliquer les paramètres de sécurité des objets d’appareil à l’espace de noms d’appareil.
- Ne définissez pas d’IOCTL autorisant FILE_ANY_ACCESS à moins que cet accès ne puisse être exploité de manière malveillante.
- Utilisez la routine IoValidateDeviceIoControlAccess pour renforcer la sécurité sur les IOCTLS existants qui autorisent FILE_ANY_ACCESS.
- Créez un modèle de menace pour examiner les limites de sécurité et rechercher des chemins imprévus. Pour en savoir plus, consultez Modélisation des menaces pour les pilotes.
- Consultez la Liste de vérification de sécurité des pilotes pour obtenir des recommandations supplémentaires sur la sécurité des pilotes.