Partager via


Sécurité de Windows Vista pour les éditeurs de logiciels

 

Sécurité de Windows Vista, pour les éditeurs de logiciels

Michael Howard et Matt Thomlinson
Microsoft Corporation

Avril 2007

Introduction

Windows Vista propose plusieurs nouveaux mécanismes de sécurité pour lutter contre les logiciels malveillants. Toute application s'exécutant sur la plateforme devrait exploiter pleinement ces défenses : d'une part, elles sont gratuites et d'autre part, vous éviterez ainsi de transformer une erreur de codage en panne irrémédiable.

Avec Internet Explorer, certaines des défenses de Windows Vista n'entrent en jeu que lorsque tous les composants utilisés par le navigateur les prennent en charge. Dans la mesure où les navigateurs sont les logiciels les plus visés (et donc les plus sensibles), il est essentiel que les composants les plus courants exploitent pleinement ces nouveaux moyens de défense. Cet article présente ces mécanismes de défense en détail et explique aux éditeurs de logiciels comment en tirer parti pour renforcer la protection des clients.

Cet article est extrait du livre "Écrire du code sécurisé pour Windows Vista" de Howard et LeBlanc, et s'applique au code C et C++ non managé (non-.NET).

Défenses de Windows Vista contre les saturations de mémoires tampon

Windows Vista adopte plusieurs stratégies de défense pour aider les entreprises et les particuliers à contrecarrer les attaques. Certains de ces mécanismes sont intégrés au noyau du système d'exploitation, d'autres sont offerts par le compilateur Microsoft Visual C++. Il s'agit notamment de :

  • /GS (Détection du débordement de pile)
  • /SafeSEH (Protection des gestionnaires d'exceptions)
  • No eXecute (NX) / Data Execution Prevention (DEP) / eXecute Disable (XD)
  • Address Space Layout Randomization (ASLR) (chargement à des adresses aléatoires dans l'espace d'adressage)
  • Organisation aléatoire des tas (heap)
  • Organisation aléatoire des piles
  • Détection de la corruption du tas.

Dans ce document, nous présenterons brièvement chacun de ces moyens de défense et proposerons des conseils quant à leur déploiement et leur test.

/GS (Détection de débordement de pile)

Cette fonction a été introduite dans le compilateur C/C++ de Visual Studio .NET 2002 et a été mise à jour dans Visual Studio .NET 2003 et Visual Studio 2005. L'argument /GS permet au compilateur d'ajouter un code de démarrage, et un code de prologue et d'épilogue de fonction afin de générer et de vérifier un nombre aléatoire qui est placé dans la pile de la fonction. Si ce nombre change, une fonction du gestionnaire est appelée pour interrompre l'application et empêcher l'exécution d'un code shell qui tenterait de profiter de la saturation de la mémoire tampon.

Pour activer cette option, il suffit d'ajouter l'argument /GS sur la ligne de commande du compilateur. Dans Visual C++, cette option est activée par défaut même si l'argument /GS ne figure pas sur la ligne de commande.

Remarque   Il est difficile d'évaluer l'impact de l'argument /GS sur les performances puisqu'il dépend du style de codage. Plus le code fait appel à des arguments et à des tampons pour chaînes qui utilisent la pile, plus l'impact se fait sentir. Si le code n'en fait intervenir aucun, les performances resteront les mêmes. Une estimation grossière serait de l'ordre de 3 % mais, dans les versions les plus récentes du compilateur, ce chiffre est compensé par d'autres optimisations. Cela dit, le code potentiellement le plus vulnérable exécute des E/S réseau et disque qui globalement ralentissent davantage les performances que les contrôles /GS.

Notez que Visual C++ 2005 déplace également les données dans la pile pour en rendre la corruption plus difficile. Parmi les exemples, citons :

  • Déplacement des tampons vers une zone mémoire située au-dessus des autres zones mémoire. Cette mesure permet de protéger les pointeurs de fonction placés dans la pile.
  • Déplacement des arguments de pointeur et de tampon dans une zone mémoire plus basse lors de l'exécution afin de réduire certaines attaques par débordement de tampon.

Recommandation pour les éditeurs de logiciels

  • Les éditeurs de logiciels devraient compiler leur code avec la dernière version du compilateur, Visual C++ 2005.
  • Ils devraient utiliser l'argument /GS.
  • L'édition de liens doit mettre en œuvre des bibliothèques utilisant /GS.

/SafeSEH (Protection des gestionnaires d'exceptions)

Un gestionnaire d'exceptions est un bloc de code qui s'exécute lors de l'apparition d'une condition exceptionnelle (division par zéro, par exemple). L'adresse du gestionnaire figure dans la pile de la fonction, et n'est donc pas à l'abri d'une corruption ou d'un piratage. L'éditeur de liens inclus dans Visual Studio 2003, et les versions ultérieures, dispose d'une option qui permet de stocker la liste des gestionnaires d'exceptions valides dans l'en-tête PE de l'image, lors de la compilation. Si une exception se produit au moment de l'exécution, le système d'exploitation (Windows XP SP2, Windows Server 2003, Windows Vista et Windows Server 2008 et postérieures) n'utilisera que les adresses de gestionnaire d'exceptions présentes dans l'en-tête PE.

Remarque   /SafeSEH n'a aucun impact sur les performances tant qu'aucune exception ne se produit.

Recommandation pour les éditeurs de logiciels

  • Les éditeurs de logiciels devraient utiliser l'éditeur de liens avec l'option /SafeSEH.
  • Les bibliothèques doivent être générées avec l'option /SafeSEH.

Data Execution Prevention (DEP) / No eXecute (NX) / eXecute Disable (XD)

Qu'elle s'appelle NX chez AMD, DEP chez Microsoft ou XD chez Intel, cette technologie nécessite des processeurs compatibles pour empêcher l'exécution de code dans les segments de données. Les tout derniers processeurs Intel prennent aujourd'hui en charge cette fonctionnalité et tous les processeurs AMD actuels prennent en charge NX. La prévention de l'exécution des données (DEP) a été introduite, pour la toute première fois, dans Windows XP SP2. Elle constitue un moyen de défense essentiel dans Windows Vista, surtout lorsqu'elle est utilisée conjointement à l'ASLR (voir ci-dessous).

La protection DEP présente toutefois un inconvénient majeur. Si votre application contient un code qui s'automodifie ou si elle réalise une compilation juste-à-temps (JIT), DEP arrêtera l'application. Pour remédier à cela, les éditeurs de logiciels devraient quand même exploiter la protection DEP (voir ci-dessous le commutateur pour l'éditeur de liens) et marquer les données manipulées par le JIT de la façon suivante :

PVOID pBuff = VirtualAlloc(NULL,4096,MEM_COMMIT,PAGE_READWRITE );
if (pBuff) {
    // Copy executable ASM code to buffer
    CopyMemory(pBuff,...) 
    
    // Buffer is ready to go so mark as executable and protect from writes
    DWORD dwOldProtect = 0;
    if (!VirtualProtect(pBuff,sizeof scode,PAGE_EXECUTE_READ,&dwOldProtect)) 
        // oops
    else
        // Call into pBuff
    VirtualFree(pBuff,0,MEM_RELEASE);
}

Remarque   La technologie DEP/NX/XD n'a aucun impact sur les performances.

Recommandation pour les éditeurs de logiciels

  • Les éditeurs de logiciels devraient tester leurs applications sur un processeur compatible DEP, et noter et résoudre tous les problèmes liés à NX.
  • Ils devraient réaliser l'édition de liens avec l'option /NXCOMPAT une fois que le test DEP est concluant.

Address Space Layout Randomization (ASLR)

L'ASLR (chargement à des adresses aléatoires dans l'espace d'adressage) charge les images dans un emplacement différent et aléatoire, à chaque démarrage du PC pour empêcher l'exécution de code shell. Pour qu'un composant puisse profiter de l'ASLR, tous les éléments qu'ils chargent doivent être compatibles. Par exemple, si A.EXE utilise les dll B.DLL et C.DLL, les trois éléments doivent prendre en charge l'ASLR. Par défaut, Windows Vista applique l'ASLR aux DLL et aux EXE système. Les éditeurs de logiciels, créateurs de fichiers DLL et EXE, devraient explicitement activer l'ASLR.

Remarque   En règle générale, l'ASLR n'a aucun impact sur les performances. Une légère amélioration des performances peut même être constatée sur des systèmes 32 bits. Il peut arriver, en revanche, qu'une dégradation se produise sur des systèmes congestionnés par un grand nombre d'images chargées en mémoire, à des adresses aléatoires. Il est toutefois difficile de la quantifier puisqu'elle dépend de la quantité et de la taille des images.

Recommandation pour les éditeurs de logiciels

  • Les éditeurs de logiciels devraient tester leurs applications sous Windows Vista, et noter et résoudre les problèmes résultant de l'ASLR.
  • Ils devraient explicitement activer l'ASLR dans toutes les images en utilisant un éditeur de liens Microsoft mis à jour (version 8.00.50727.161 ou postérieure).
  • Ils devraient activer l'option /DYNAMICBASE dans l'éditeur de liens.

Allocation aléatoire des tas

Lorsqu'une application crée un tas dans Windows Vista, le gestionnaire de tas alloue un espace mémoire de façon aléatoire pour réduire les risques de débordement de tas et contrecarrer d'éventuelles attaques. L'allocation aléatoire des tas est activée par défaut pour toutes les applications Windows Vista.

Remarque   Il y a très peu d'impact sur les performances.

  • Les éditeurs de logiciels devraient tester leurs applications sous Windows Vista, et noter et résoudre les problèmes liés à l'allocation aléatoire des tas.

Allocation aléatoire des piles

Lorsqu'un thread démarre dans un processus compilé avec l'option /DYNAMICBASE, Windows Vista déplace, de façon aléatoire, la pile du thread en mémoire afin de réduire les risques de débordement de pile et contrecarrer d'éventuelles attaques.

Remarque   Il y a très peu d'impact sur les performances.

  • Les éditeurs de logiciels devraient activer l'option /DYNAMICBASE dans l'éditeur de liens, tester leurs applications sous Windows Vista, et noter/résoudre les problèmes liés à l'allocation aléatoire des piles.

Détection de corruption de tas

Ce mécanisme consiste à interrompre l'application dès que le gestionnaire de tas constate une corruption ou une incohérence du tas. Ce paramètre permet non seulement d'anticiper les débordements de tas, mais aussi de détecter certaines opérations illicites. Par exemple, la libération d'un pointeur dans un tas ne correspondant pas au pointeur fera échouer l'application.

Pour rendre une application compatible avec cette détection, une légère modification du code est nécessaire (elle ne s'applique qu'à Windows Vista). Le code suivant ne s'exécutera pas sur des plateformes Windows ne prenant pas en charge la fonction (Windows 2000 plus particulièrement). Il permet d'activer la détection de corruption de tas pour l'application appelante lors de son exécution dans Windows Vista.

BOOL SetHeapOptions() {
   HMODULE hLib = LoadLibrary(L"kernel32.dll");
   if (hLib == NULL) return FALSE;

   typedef BOOL (WINAPI *HSI)
          (HANDLE, HEAP_INFORMATION_CLASS ,PVOID, SIZE_T);
   HSI pHsi = (HSI)GetProcAddress(hLib,"HeapSetInformation");
   if (!pHsi) {
      FreeLibrary(hLib);
      return FALSE;
   }

#ifndef HeapEnableTerminationOnCorruption
#   define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
#endif

   BOOL fRet = (pHsi)(NULL,HeapEnableTerminationOnCorruption,NULL,0) 
            ? TRUE 
            : FALSE;
   if (hLib) FreeLibrary(hLib);

   return fRet;
}

À quoi ressemble la détection de corruption de tas ?

Lorsqu'une application s'arrête suite à une corruption de tas, vous recevrez un message de la sorte dans un débogueur s'exécutant sous Windows Vista.

HEAP[Crash.exe]: Heap block at 001B6758 modified at 001B678E past requested size of 2e
(1770.25ac): Break instruction exception - code 80000003 (first chance)
eax=001b6758 ebx=001b678e ecx=774614cd edx=0012fae9 esi=001b6758 edi=0000002e
eip=77482ea8 esp=0012fd2c ebp=0012fd30 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!DbgBreakPoint:
77482ea8 cc              int     3
0:000> kb
ChildEBP RetAddr  Args to Child              
0012fd30 774dc900 001b6758 00000000 001b6758 ntdll!DbgBreakPoint
0012fd48 774c4b6e 00000000 001b6758 7748c67e ntdll!RtlImageRvaToVa+0x1c3
0012fd64 7745894a 001b0000 001b6758 7748c67e ntdll!RtlDeleteAce+0x1355f
0012fdd0 7614b019 001b0000 00000000 001b6760 ntdll!RtlValidateHeap+0x79
0012fde4 6557cb0a 001b0000 00000000 001b6760 kernel32!HeapValidate+0x14

Remarque   Il n'y a aucun impact sur les performances.

Recommandations pour les éditeurs de logiciels

  • Les éditeurs de logiciels devraient inclure ce code dans tous les exécutables (EXE). Si votre composant est une DLL ou un contrôle ActiveX, il est inutile d'incorporer ce code ou d'activer la détection de corruption de tas.

Importance et priorité des moyens de défense

Le tableau suivant présente l'importance relative des moyens de défense et l'ordre de priorité dans lequel les éditeurs devraient envisager de les mettre en œuvre.

Défense Priorité
Activation de l'ASLR Critique
Activation de la DEP Critique
/GS (détection du débordement de pile) Élevée
/SafeSEH (protection des gestionnaires d'exceptions) Élevée
Test de l'allocation aléatoire des piles Moyenne
Test de l'allocation aléatoire des tas Moyenne
Détection de corruption de tas Moyenne

Déroulement des tests

Après chaque modification de code ou de conception, vous devez vous assurer de la bonne configuration du système d'exploitation et vérifier que les changements ont bien été répercutés dans l'application.

Utilisation du compilateur C++

Vérifiez que la version du compilateur est au minimum 13.10. La version 14.00 (ou postérieure) est fortement recommandée, puisqu'il s'agit de la version qui est intégrée à Visual Studio 2005 et qui offre la meilleure implémentation de /GS.

Utilisation de /GS

Ouvrez le ou les fichiers make associés au produit. Si vous utilisez des versions de Visual C++ antérieures à Visual C++ 2005, vérifiez que tout le code est compilé avec /GS ou –GS. En cas d'utilisation de Visual C++ 2005, vérifiez qu'il n'existe aucune référence à /GS- ou à –GS-. Les arguments du compilateur étant sensibles à la casse, –GS n'est pas la même chose que –Gs.

Emploi de /SafeSEH

Ouvrez les fichiers make associés au produit et vérifiez que l'éditeur de liens utilise bien l'option /SafeSEH. Les options de l'éditeur de liens sont insensibles à la casse.

Compatibilité avec la protection DEP

Il y a plusieurs façons de savoir si votre combinaison BIOS et processeur prend en charge la protection DEP et si cette dernière est activée. Le plus simple est encore de sélectionner Panneau de configuration / Système / Paramètres système avancés / onglet Avancé / Paramètres de performances / onglet Prévention de l'exécution des données. Si la protection DEP est activée, vous verrez apparaître les mots :

Le processeur de votre ordinateur prend en charge la prévention de l’exécution des données au niveau matériel.

Pour savoir si la protection DEP est activée sur l'ordinateur utilisé pour les tests de votre produit, exécutez les commandes suivantes à la ligne de commande.

Commande Résultats possibles Résultat requis
wmic OS Get DataExecutionPrevention_
Available
TRUE – DEP est disponible

FALSE – DEP n'est pas disponible

TRUE
wmic OS Get DataExecutionPrevention_
SupportPolicy
0 – DEP est désactivée pour tous les processus

1 – DEP est activée pour tous les processus

2 – Les composants système Windows sont compatibles avec DEP

3 – DEP est activée pour tous les composants, à l'exception de ceux pour lesquels l'option a été explicitement désactivée

1
wmic OS Get DataExecutionPrevention_
32BitApplications
TRUE – DEP est activée pour toutes les applications

FALSE – DEP est désactivée pour les applications

TRUE
wmic OS Get DataExecutionPrevention_
Drivers
TRUE – DEP est activée pour les pilotes

FALSE – DEP est désactivée pour les pilotes

TRUE

Sur certains ordinateurs, vous devez activer la protection DEP via un paramètre du BIOS. Sachez aussi que, dans certains cas, la fonction NX risque de ne pas être prise en compte par votre application ou votre composant. Cela se produit généralement lorsque vous liez votre application à une bibliothèque non compatible NX, par exemple aux anciennes versions ATL (Abstract Type Library) incluses dans Visual Studio. Si une bibliothèque ATL est requise, utilisez la version livrée avec Visual Studio 2003 SP1 (ATL v7.1) ou Visual Studio 2005 (ATL v8.0). Vous pouvez vérifier la version ATL à l'aide du code de débogage suivant :

  // Version in Visual Studio .NET 2003 
  assert(AtlGetVersion(NULL) >= 0x0710);

Compatibilité avec l'ASLR

Vérifiez que vos fichiers make utilisent l'argument /DYNAMICBASE (insensible à la casse) de l'éditeur de liens. Vérifiez ensuite que votre application est allouée dynamiquement en mémoire.

Vérification d'un exécutable

Si votre application est un fichier EXE, vous pouvez réaliser les tâches suivantes :

  • Exécutez un débogueur et déboguez le processus en question, puis notez l'adresse de chargement.
  • Redémarrez l'ordinateur.
  • Reprenez à l'étape (1).

Si l'allocation aléatoire est explicitement définie pour le processus, vous constaterez que les adresses de chargement varient à chaque redémarrage. Voici un exemple de sortie obtenu à partir du fichier cdb.exe :

Executable search path is: ModLoad: 01270000 0128b000   C:\junk\TestRand\TestRand.exe ModLoad: 77ee0000 77ff5000   C:\Windows\system32\ntdll.dll ModLoad: 77c30000 77d01000   C:\Windows\system32\kernel32.dll ModLoad: 769a0000 76a49000   C:\Windows\system32\msvcrt.dll

Executable search path is: ModLoad: 009e0000 009fb000   C:\junk\TestRand\TestRand.exe ModLoad: 77a40000 77b55000   C:\Windows\system32\ntdll.dll ModLoad: 77820000 778f1000   C:\Windows\system32\kernel32.dll ModLoad: 76620000 766c9000   C:\Windows\system32\msvcrt.dll

Compatibilité avec la détection de corruption de tas

Le meilleur moyen de vérifier la compatibilité est de rechercher l'appel correct à HeapSetInformation dans le code.

Compatibilité avec les tests d'allocation aléatoire des piles et des tas

Il suffit d'exécuter votre application sous Windows Vista pour vous assurer de la compatibilité puisque ces moyens de défense sont activés par défaut pour toutes les applications.

Résumé

Les navigateurs sont, à l'heure actuelle, les logiciels les plus attaqués. Il leur faut donc tirer pleinement parti des moyens de défense proposés par le système d'exploitation. Cet impératif s'étend aux composants utilisés par le navigateur. En suivant les conseils donnés dans cet article, les éditeurs de logiciels sont en mesure de créer des composants capables de fonctionner en toute sécurité en cas d'hébergement dans Internet Explorer.

Références