Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Pour obtenir une discussion générale sur l’écriture de pilotes sécurisés, consultez Création de pilotes fiables Kernel-Mode.
Au-delà des pratiques de codage sécurisées et des conseils généraux sur le pilote de périphérique, les pilotes réseau doivent effectuer les opérations suivantes pour améliorer la sécurité :
- Tous les pilotes réseau doivent valider les valeurs qu’ils lisent à partir du Registre. Plus précisément, l’appelant de NdisReadConfiguration ou NdisReadNetworkAddress ne doit effectuer aucune hypothèse sur les valeurs lues à partir du Registre et doit valider chaque valeur de Registre qu’elle lit. Si l’appelant de NdisReadConfiguration détermine qu’une valeur est hors limites, elle doit utiliser une valeur par défaut à la place. Si l’appelant de NdisReadNetworkAddress détermine qu’une valeur est hors limites, elle doit utiliser l’adresse MAC (Permanent Medium Access Control) ou une adresse par défaut.
Problèmes spécifiques à l’OID
Un pilote miniport, dans ses fonctions MiniportOidRequest ou MiniportCoOidRequest , doit valider toute valeur d’identificateur d’objet (OID) demandée par le pilote. Si le pilote détermine que la valeur à définir est en-dehors des limites, il doit rejeter la demande de configuration. Pour plus d’informations sur les identificateurs d’objet, consultez Obtention et définition des informations sur le pilote Miniport et prise en charge de NDIS pour WMI.
Si la fonction MiniportOidRequest d’un pilote intermédiaire ne passe pas d’opération définie à un pilote miniport sous-jacent, la fonction doit valider la valeur OID. Pour plus d’informations, consultez Les opérations de requête et de définition de pilotes intermédiaires.
Instructions de sécurité de l’OID de requête
La plupart des OID de requête peuvent être émis par n’importe quelle application usermode sur le système. Suivez ces instructions spécifiques pour les OID de requête.
Vérifiez toujours que la taille de la mémoire tampon est suffisamment grande pour la sortie. Tout gestionnaire OID de requête sans vérification de taille de mémoire tampon de sortie a un bogue de sécurité.
if (oid->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) { oid->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG); return NDIS_STATUS_INVALID_LENGTH; }Écrivez toujours une valeur correcte et minimale dans BytesWritten. Il s’agit d’un signal d'alarme d'attribuer
oid->BytesWritten = oid->InformationBufferLengthcomme dans l’exemple suivant.// ALWAYS WRONG oid->DATA.QUERY_INFORMATION.BytesWritten = DATA.QUERY_INFORMATION.InformationBufferLength;Le système d'exploitation copie les octets spécifiés par BytesWritten vers et depuis une application en mode utilisateur. Si BytesWritten est supérieur au nombre d’octets que le pilote a réellement écrits, le système d’exploitation peut finir par copier la mémoire du noyau non initialisée vers le modèle utilisateur, ce qui serait une vulnérabilité de divulgation d’informations. Utilisez plutôt du code similaire à ceci :
oid->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);Ne lisez jamais les valeurs de la mémoire tampon. Dans certains cas, la mémoire tampon de sortie d’un OID est directement mappée dans un processus de mode utilisateur hostile. Le processus hostile peut modifier votre mémoire tampon de sortie une fois que vous l’avez écrite. Par exemple, le code ci-dessous peut être attaqué, car un attaquant peut modifier numElements une fois qu’il a été écrit :
output->NumElements = 4; for (i = 0 ; i < output->NumElements ; i++) { output->Element[i] = . . .; }Pour éviter de lire à partir de la mémoire tampon, conservez une copie locale. Par exemple, pour corriger l’exemple ci-dessus, introduisez une nouvelle variable de pile :
ULONG num = 4; output->NumElements = num; for (i = 0 ; i < num; i++) { output->Element[i] = . . .; }Avec cette approche, la boucle for lit à partir de la variable
numde pile du pilote et non à partir de sa mémoire tampon de sortie. Le pilote doit également marquer la mémoire tampon de sortie avec levolatilemot clé pour empêcher le compilateur d’annuler silencieusement ce correctif.
Définir des instructions de sécurité OID
La plupart des OID Set peuvent être émis par une application en mode utilisateur s’exécutant dans les groupes de sécurité Administrateurs ou Système. Bien que ces applications soient généralement approuvées, le pilote miniport ne doit toujours pas autoriser l’altération de la mémoire ou l’injection de code du noyau. Suivez ces règles spécifiques pour définir des OID :
Vérifiez toujours que l’entrée est suffisamment grande. Tout gestionnaire défini par OID sans vérification de la taille de la mémoire tampon d’entrée présente une vulnérabilité de sécurité.
if (oid->DATA.SET_INFORMATION.InformationBufferLength < sizeof(ULONG)) { return NDIS_STATUS_INVALID_LENGTH; }Chaque fois que vous validez un OID avec un décalage incorporé, vous devez vérifier que la mémoire tampon incorporée se trouve dans la charge utile OID. Cela nécessite plusieurs vérifications. Par exemple, OID_PM_ADD_WOL_PATTERN peut fournir un modèle incorporé, qui doit être vérifié. La validation correcte nécessite la vérification :
InformationBufferSize >= sizeof(NDIS_PM_PACKET_PATTERN)
PmPattern = (PNDIS_PM_PACKET_PATTERN) InformationBuffer; if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) { Status = NDIS_STATUS_BUFFER_TOO_SHORT; *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); break; }Pattern->PatternOffset + Pattern->PatternSize ne déborde pas
ULONG TotalSize = 0; if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternSize, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }Ces deux vérifications peuvent être combinées à l’aide de code comme dans l’exemple suivant :
ULONG TotalSize = 0; if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN) || !NT_SUCCESS(RtlUlongAdd(Pattern->PatternSize, Pattern->PatternOffset, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }InformationBuffer + Pattern->PatternOffset + Pattern->PatternLength ne provoque pas de dépassement
ULONG TotalSize = 0; if (!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) || (!NT_SUCCESS(RtlUlongAdd(TotalSize, InformationBuffer, &TotalSize) || TotalSize > InformationBufferLength) { return NDIS_STATUS_INVALID_LENGTH; }Pattern-PatternOffset> + Pattern-PatternLength <>= InformationBufferSize
ULONG TotalSize = 0; if(!NT_SUCCESS(RtlUlongAdd(Pattern->PatternOffset, Pattern->PatternLength, &TotalSize) || TotalSize > InformationBufferLength)) { return NDIS_STATUS_INVALID_LENGTH; }
Instructions de sécurité de la méthode OID
Les OID de méthode peuvent être émis par une application usermode s’exécutant dans les groupes de sécurité administrateurs ou système. Il s'agit d'une combinaison d'un ensemble et d'une requête, les deux listes précédentes de directives s'appliquent également aux OID de méthode.
Autres problèmes de sécurité de pilote réseau
De nombreux pilotes de miniport NDIS utilisent NdisRegisterDeviceEx pour exposer un périphérique de contrôle. Ceux qui le font doivent auditer leurs gestionnaires IOCTL, avec toutes les mêmes règles de sécurité qu’un pilote WDM. Pour plus d’informations, consultez Problèmes de sécurité pour les codes de contrôle d’E/S.
Les pilotes miniport NDIS bien conçus ne doivent pas s'appuyer sur un appel dans un contexte de processus particulier, ni interagir très étroitement avec le mode utilisateur, à l'exception des IOCTLs et des OID. Il s’agirait d’un indicateur rouge pour voir un miniport qui a ouvert des poignées en mode utilisateur, effectué des attentes de mode utilisateur ou alloué de la mémoire par rapport au quota de mode utilisateur. Ce code doit être examiné.
La plupart des pilotes miniport NDIS ne doivent pas être impliqués dans l’analyse des charges utiles de paquets. Dans certains cas, cependant, il peut être nécessaire. Dans ce cas, ce code doit être audité très soigneusement, car le pilote analyse les données d’une source non approuvée.
Comme c’est le cas lors de l’allocation de mémoire en mode noyau, les pilotes NDIS doivent utiliser les mécanismes de pool NX Opt-In appropriés. Dans WDK 8 et versions ultérieures, la
NdisAllocate*famille de fonctions est correctement choisie.