Chargement sécurisé des bibliothèques pour empêcher les attaques de préchargement de DLL

Le support de Windows Vista Service Pack 1 (SP1) a pris fin le 12 juillet 2011. Pour continuer à recevoir des mises à jour de sécurité pour Windows, assurez-vous que vous exécutez Windows Vista avec le Service Pack 2 (SP2). Pour plus d’informations, reportez-vous à la page web suivante de Microsoft : Le support de Windows a pris fin.

Quand une application charge dynamiquement une bibliothèque de liens dynamiques (DLL) sans spécifier de chemin d’accès complet, Windows tente de localiser la DLL en recherchant un ensemble bien défini de répertoires. Si un attaquant prend le contrôle de l’un des répertoires, il peut forcer l’application à charger une copie malveillante de la DLL au lieu de la DLL qu’elle attendait. Ces attaques sont appelées « attaques de préchargement de DLL » et sont communes à tous les systèmes d’exploitation qui prennent en charge le chargement dynamique de bibliothèques de DLL partagées. L’effet de ces attaques pourrait être qu’un attaquant puisse exécuter du code dans le contexte de l’utilisateur qui exécute l’application. Lorsque l’application est exécutée en tant qu’administrateur, cela peut entraîner une élévation de privilèges locale. Nous connaissons un regain d’intérêt pour ces attaques. Pour limiter l’effet de ce problème sur nos clients communs, nous publions ce document à la communauté des développeurs pour nous assurer qu’ils sont informés de ce problème et qu’ils disposent des outils nécessaires pour résoudre le problème dans leurs applications.

Résumé

Description des attaques par préchargement de DLL

Attaques basées sur LoadLibrary

Lorsqu’une application charge dynamiquement une DLL sans spécifier un chemin d’accès complet, Windows tente de localiser cette DLL en effectuant des recherches linéaires dans un ensemble bien défini de répertoires, appelé « Ordre de recherche des DLL ». Si Windows localise la DLL dans l’ordre de recherche des DLL, il charge cette DLL. Toutefois, si Windows ne trouve la DLL dans aucun des répertoires dans l’ordre de recherche des DLL, il renvoie l’opération de chargement de DLL en échec. Voici l’ordre de recherche des DLL pour les fonctions LoadLibrary et LoadLibraryEx , qui sont utilisées pour charger dynamiquement des DLL :

  1. Répertoire à partir duquel l’application s’est chargée
  2. Répertoire système
  3. Répertoire système 16 bits
  4. Répertoire Windows
  5. Le répertoire de travail actuel (CWD)
  6. Répertoires répertoriés dans la variable d’environnement PATH

                
Prenons le scénario suivant :

  • Une application charge une DLL sans spécifier un chemin complet qu’elle s’attend à trouver dans le CWD de l’application.
  • L’application est parfaitement préparée pour traiter le cas lorsqu’elle ne trouve pas la DLL.
  • L’attaquant connaît ces informations sur l’application et contrôle le CWD.
  • L’attaquant copie sa propre version spécialement conçue de la DLL dans le CWD. Cela suppose que l’agresseur est autorisé à le faire.
  • Windows effectue une recherche dans les répertoires dans l’ordre de recherche des DLL et trouve la DLL dans le CWD de l’application.

Dans ce scénario, la DLL spécialement conçue s’exécute dans l’application et gagne les privilèges de l’utilisateur actuel.

Recommandation

Pour éviter cette attaque, les applications peuvent supprimer le répertoire de travail actuel (CWD) du chemin de recherche DLL en appelant l’API SetDllDirectory à l’aide d’une chaîne vide («  »). Si une application dépend du chargement d’une DLL à partir du répertoire courant, procurez-vous le répertoire de travail actuel et utilisez-le pour passer dans un chemin complet de LoadLibrary.

Nous sommes également conscients que certains développeurs utilisent LoadLibrary pour valider si une DLL spécifique est présente afin de déterminer quelle version de Windows est exécutée par l’utilisateur. Vous devez savoir que cela peut rendre l’application vulnérable. Si la bibliothèque affectée n’existe effectivement pas sur la version Windows sur laquelle l’application est exécutée, un attaquant pourrait introduire une bibliothèque du même nom dans CWD. Nous vous déconseillons fortement d’utiliser cette technique. Utilisez plutôt les techniques recommandées décrites dans l’article MSDN « Obtention de la version système ».

Une application qui charge des plug-ins tiers et qui ne peut pas forcer les plug-ins à utiliser un chemin qualifié pour ses appels LoadLibrary doit appeler SetDllDirectory(«  ») pour supprimer CWD, puis appeler SetDllDirectory(« emplacement d’installation du plug-in ») pour ajouter le répertoire d’installation du plug-in au chemin de recherche des DLL.

Attaques basées sur SearchPath

Une attaque similaire existe lorsqu’une application utilise l’API SearchPath pour localiser une DLL et charger dynamiquement le chemin renvoyé par SearchPath. Voici l’ordre de recherche par défaut pour l’API SearchPath :

  • Répertoire à partir duquel l’application s’est chargée
  • Le répertoire de travail actuel (CWD)
  • Répertoire système
  • Répertoire système 16 bits
  • Répertoire Windows
  • Répertoires répertoriés dans la variable d’environnement PATH

Nous déconseillons ce modèle, car il n’est pas sécurisé. Nous déconseillons l’utilisation de la fonction SearchPath comme méthode de localisation d’un fichier .dll si l’utilisation prévue de la sortie est dans un appel à la fonction LoadLibrary. Cela peut entraîner la localisation d’un fichier .dll erroné, car l’ordre de recherche de la fonction SearchPath diffère de l’ordre de recherche utilisé par la fonction LoadLibrary. Si vous devez localiser et charger un fichier .dll, utilisez la fonction LoadLibrary.

ShellExecute et CreateProcess

Des variantes de ces problèmes peuvent également exister lorsque les développeurs appellent des fonctions similaires, telles que ShellExecute et CreateProcess pour charger des exécutables externes. Nous recommandons aux développeurs d’être prudents lorsqu’ils chargent des fichiers binaires et de spécifier le chemin d’accès complet. Cela devrait poser moins de complexité lorsque vous chargez un binaire au lieu d’une bibliothèque.

Nous recommandons aux développeurs d’effectuer les opérations suivantes :

  • Validez leurs applications pour les instances de chargements de bibliothèques non sécurisés (des exemples de chacune sont donnés plus loin dans cet article). En voici quelques exemples :

    • Utilisation de SearchPath pour identifier l’emplacement d’une bibliothèque ou d’un composant.
    • L’utilisation de LoadLibrary pour identifier la version du système d’exploitation.
  • Utilisez des chemins d’accès complets pour tous les appels à LoadLibrary, CreateProcess et ShellExecute, lorsque vous le pouvez.

  • Implémentez les appels à SetDllDirectory avec une chaîne vide («  ») pour supprimer le répertoire de travail actuel de l’ordre de recherche DLL par défaut lorsque cela est nécessaire. Sachez que SetDllDirectory affecte l’ensemble du processus. Par conséquent, vous devez effectuer cette opération une seule fois au début de l’initialisation du processus, et non avant et après les appels à LoadLibrary. Étant donné que SetDllDirectory affecte l’ensemble du processus, plusieurs threads appelant SetDllDirectory avec des valeurs différentes peuvent entraîner un comportement indéfini. En outre, si le processus est conçu pour charger des DLL tierces, des tests seront nécessaires pour déterminer si la création d’un paramètre à l’échelle du processus entraînera des incompatibilités. Un problème connu est que, lorsqu’une application dépend de Visual Basic pour Applications, un paramètre à l’échelle du processus peut provoquer des incompatibilités.

  • Utilisez la fonction SetSearchPathMode pour activer le mode de recherche de processus sécurisé pour le processus. Cela déplace le répertoire de travail actuel au dernier emplacement de la liste de recherche SearchPath pour la durée de vie du processus.

  • Évitez d’utiliser SearchPath pour case activée l’existence d’une DLL sans spécifier un chemin d’accès complet, même si le mode de recherche sans échec est activé, car cela peut toujours entraîner des attaques de préchargement de DLL.

Conseils sur l’identification des chargements de bibliothèques non sécurisés

Dans le code source, voici des exemples de chargements de bibliothèque non sécurisés :

  • Dans l’exemple de code suivant, l’application recherche « schannel.dll » en utilisant le chemin de recherche le moins sécurisé. Si un attaquant peut placer schannel.dll dans CWD, il se chargera avant même que l’application ne recherche la bibliothèque appropriée dans les répertoires Windows.

    DWORD retval = SearchPath(NULL, "schannel", ".dll", err, result, NULL); 
    HMODULE handle = LoadLibrary(result);
    
  • Dans l’exemple de code suivant, l’application tente de charger la bibliothèque à partir des différents emplacements de l’application et du système d’exploitation décrits au début de ce document pour l’appel LoadLibrary(). S’il existe un risque que le fichier ne soit pas présent, l’application peut essayer de charger le fichier à partir du répertoire de travail actuel. Ce scénario est légèrement moins dangereux que l’exemple précédent. Toutefois, elle expose toujours l’utilisateur de l’application à un risque si l’environnement n’est pas entièrement prévisible.

    HMODULE handle = LoadLibrary("schannel.dll");
    

                
                
Voici des exemples de chargements de bibliothèque plus performants et plus sécurisés :

  • Dans l’exemple de code suivant, la bibliothèque est chargée directement à l’aide d’un chemin complet. Il n’y a aucun risque que l’attaquant introduise du code malveillant à moins qu’il ne dispose déjà d’autorisations d’écriture sur le répertoire cible de l’application.

    HMODULE handle = LoadLibrary("c:\\windows\\system32\\schannel.dll");
    

    Remarque Pour plus d’informations sur la façon de déterminer le répertoire système, consultez les ressources suivantes :

    GetSystemDirectory
    http://msdn.microsoft.com/en-us/library/ms724373%28VS.85%29.aspx SHGetKnownFolderPath
    http://msdn.microsoft.com/en-us/library/bb762188%28v=VS.85%29.aspx

  • Dans l’exemple de code suivant, le répertoire de travail actuel est supprimé du chemin de recherche avant d’appeler LoadLibrary. Cela réduit considérablement le risque, car l’attaquant devrait contrôler soit le répertoire de l’application, le répertoire Windows ou tout répertoire spécifié dans le chemin de l’utilisateur afin d’utiliser une attaque par préchargement de DLL.

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
  • Sur tous les systèmes qui ont installé la mise à jour de sécurité 963027 (décrits dans MS09-014), le code suivant déplacerait définitivement CWD à la toute dernière place dans l’ordre de recherche. Tout appel ultérieur à la fonction SetSearchPathMode à partir de ce processus qui tente de modifier le mode de recherche échouera.

    SetDllDirectory ("");
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
  • Dans l’exemple de code suivant, le répertoire de travail actuel est supprimé du chemin de recherche avant d’appeler LoadLibrary. Cela réduit considérablement le risque, car l’attaquant devrait contrôler soit le répertoire de l’application, le répertoire Windows ou tout répertoire spécifié dans le chemin de l’utilisateur afin d’utiliser une attaque de préchargement de DLL.

    SetSearchPathMode (BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT );
    HMODULE handle = LoadLibrary("schannel.dll");
    
    
    

Utilisation du Moniteur de traitement pour détecter dynamiquement les charges non sécurisées

Microsoft publie un outil nommé Process Monitor. Cet outil permet aux développeurs et aux administrateurs de suivre de près le comportement d’un processus en cours d’exécution. Process Monitor peut être utilisé pour détecter dynamiquement si l’une de vos applications peut être vulnérable à ce type de problème.

  • Pour télécharger le Moniteur de processus, reportez-vous à la page web de Microsoft suivante :
    http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

  • Essayez de démarrer votre application en utilisant CWD défini sur un répertoire spécifique. Par exemple, double-cliquez sur un fichier avec une extension dont le gestionnaire de fichiers est affecté à votre application.

  • Configurez Process Monitor avec les filtres suivants :

    Boîte de dialogue Filtre du moniteur de processus affichant les filtres d’activité des fichiers et des processus

  • Si un chemin vulnérable est touché, vous verrez quelque chose qui ressemble à ce qui suit : Résultats du moniteur de processus mettant en évidence une opération CreateFile sous un chemin de sécurité

     L’appel au partage de fichiers distant pour charger une DLL indique qu’il s’agit d’un programme vulnérable.

Informations supplémentaires

Pour plus d’informations, reportez-vous aux pages web de Microsoft suivantes :

Ordre de recherche de bibliothèques de liens dynamiques

http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx Documentation MSDN relative à la fonction SearchPath

http://msdn.microsoft.com/en-us/library/aa365527(VS.85).aspx Documentation MSDN relative à la fonction LoadLibrary

http://msdn.microsoft.com/en-us/library/ms684175(VS.85).aspx Documentation MSDN relative à la fonction SetDllDirectory

http://msdn.microsoft.com/en-us/library/ms686203(VS.85).aspx Documentation MSDN sur la fonction SetSearchPathMode

http://msdn.microsoft.com/en-us/library/dd266735 (VS.85).aspx Article de blog par David Leblanc, ingénieur principal en sécurité chez Microsoft Office

http://blogs.msdn.com/b/david_leblanc/archive/2008/02/20/dll-preloading-attacks.aspx Article de blog d’Andrew Roths, équipe d’ingénierie du MSRC sur les attaques de préchargement de DLL

http://blogs.technet.com/b/srd/archive/2009/04/14/ms09-014-addressing-the-safari-carpet-bomb-vulnerability.aspx

Ressources supplémentaires