• Aucun résultat trouvé

1.4 L’injection de fautes logicielle

1.4.2 Génération des données

1.4.2.3 Modèle en mémoire

Il arrive que la spécification d’un programme ne soit pas connue. Par exemple dans le cas d’une application fermée ou ne disposant pas d’une documentation libre-ment accessible. Pour répondre à ce problème, plutôt que de revenir à des mécanismes aveugles, des approches dites en mémoire ont été proposées. Ce type d’approche permet, tout comme les modèles spécifiques, d’atteindre des états du programme en cours de test que n’atteignent pas les mécanismes aveugles, mais sans pour autant avoir connaissance de sa spécification. À noter que ce type d’approche permet égale-ment de répondre au problème d’automatisation du traiteégale-ment de la spécification posé par les modèles spécifiques.

Pour cela, les mécanismes d’injection en mémoire cherchent à déplacer le prob-lème de l’injection des erreurs, des points entrée du programme aux données directe-ment manipulées en mémoire par ce dernier. Cela signifie qu’à l’instar des mécan-ismes logiciels de simulation de faute matérielle (voir section 1.4.2.1), les mécanmécan-ismes d’injection en mémoire doivent avoir un accès direct à l’espace mémoire du proces-sus de l’application. Cependant, le modèle de faute à simuler étant bien plus élaboré qu’une simple corruption aléatoire de la mémoire, le mécanisme d’injection doit pour être pertinent extraire un certain nombre d’informations du code exécuté par le processus. Ce modèle s’apparente donc plus à une approche en boîte grise.

Une première possibilité pour réaliser cela est l’interception des appels sys-tème [DM98, DM00]. Les erreurs peuvent alors être introduites directement dans les données retournées par ces appels système. Cette approche, bien que finale-ment proche des mécanismes de rejeu de données mutées (voir section 1.4.2.1), peut mettre en évidence l’existence d’un plus grand nombre d’erreurs de conception. En effet, grâce à l’apport d’informations sur le fonctionnement interne du programme, à savoir les appels système ainsi que les données qu’ils retournent, la génération des données erronées peut cibler un plus grand nombre de points d’entrée (par exemple, un fichier de configuration). Cependant, ce type de mécanisme reste encore limité face à certains types de données. C’est par exemple le cas des fichiers compressés ou des connexions réseaux chiffrées.

Pour répondre à ce problème, il est nécessaire d’être capable de modifier des données plus en profondeur dans la pile d’appels menant aux appels système. Pour réaliser cela, il est possible de reposer sur des outils de couverture de code. Ces outils sont originalement utilisés dans la phase de développement d’une application afin de s’assurer que celle-ci ne contient pas de code mort et donc que toutes ses branches sont exécutables. Dans le cas d’une campagne d’injections en mémoire,

ce type d’outil permet d’identifier la trace d’exécution des fonctions internes pour chaque appel système effectué par le programme [EFS]. Ces outils de couverture de code reposent sur des débogueurs ou des bibliothèques d’instrumentation, ils sont donc utilisés par la suite pour procéder à l’injection des données erronées. À noter que dans le cas d’un audit de sécurité, afin de se concentrer sur les fautes poten-tiellement capables de générer une intrusion, il est possible de donner la priorité, via un mécanisme de retour, aux jeux de données permettant d’atteindre des appels système potentiellement dangereux [ADAFE].

1.4.3 Positionnement des travaux

Dans cette thèse, nous voulons évaluer un mécanisme de détection des intrusions de niveau applicatif. Or nous avons vu à la section 1.3.3 que l’attaque à l’origine d’une intrusion peut être vue comme faute au sein du programme. Nous proposons donc de simuler le résultat d’une attaque en forçant le processus dans un état erroné. Pour cela, nos travaux reposent sur l’utilisation d’un mécanisme d’injection de fautes. Cependant, afin de reproduire les possibles états erronés d’un programme provoqués par une attaque, le mécanisme d’injection que nous proposons est capable de cibler les données qui influencent l’exécution des appels système.

Dans ce but, à la manière des mécanismes de simulation logicielle d’erreurs matérielles au niveau de la mémoire, notre mécanisme d’injection agit directement dans l’espace mémoire du processus. Toutefois, la simulation d’une attaque contre une application requiert un modèle de faute bien plus compliqué que la simple simu-lation d’une erreur au niveau de la mémoire. De plus, notre objectif étant de simuler un type d’attaque bien particulier, les modèles de fautes au niveau des points d’en-trée du programme ne sont pas non plus suffisants. C’est pourquoi, à l’instar des mécanismes d’injection en mémoire, nous injectons les erreurs directement dans les données manipulées par le programme.

Cependant, tout comme ces derniers, nous avons besoin d’informations sur le fonctionnement interne du programme pour construire notre modèle de faute. C’est ce modèle qui permet à notre mécanisme d’injection de déterminer quels emplace-ments mémoire doivent subir une injection et à quel moment celle-ci doit avoir lieu. Toutefois, contrairement aux mécanismes d’injection en mémoire, nous ne nous limitons pas à l’interception des appels système ou à l’utilisation d’outils de couver-ture de code pour construire notre modèle. Nous proposons une approche par boîte blanche, reposant sur l’analyse statique du code source du programme à tester, pour déterminer les données qui pourraient être la cible d’une attaque.

De plus, afin d’améliorer la pertinence de notre mécanisme d’évaluation, nous proposons de déterminer quels états erronés parmi ceux que nous avons injectés sont les plus susceptibles d’être représentatifs d’une véritable intrusion. Une intru-sion n’étant effective que lorsque des appels système illégaux ont été exécutés, nous utilisons pour cela une méthode d’apprentissage du comportement d’un programme

au niveau de ses appels système. Le modèle de comportement appris tient compte à la fois des séquences des appels systèmes mais aussi des arguments qui leurs sont passés en paramètre. Ce type de modélisation nous permet de déterminer quels états erronés ont effectivement permis de faire exécuter au programme des appels système illégaux.

Modèle de détection d’intrusion

Pour détecter une intrusion au sein d’un programme, nous proposons dans ces travaux de thèse une approche de type comportementale. Située au niveau appli-catif, ce type d’approche requiert donc la construction préalable d’un modèle du comportement normal des programmes à surveiller et ceci afin de pouvoir mettre en évidence, pour chacun d’eux, les éventuelles déviations comportementales engen-drées par une intrusion ou une tentative d’intrusion. Dans le cadre de l’approche que nous proposons, la construction de ce modèle est effectuée à partir du code source des applications concernées et celle-ci repose sur des techniques issues du domaine de l’analyse statique.

Notons que les techniques d’analyse statique sur lesquelles nous avons choisi de nous reposer nous assurent que les résultats obtenus sont des sur-approximations. Cette méthode de construction permet donc de générer un modèle qui est complet, c’est-à-dire un modèle qui inclut tous les comportements possibles du programme. Par conséquent, notre approche ne peut pas générer de fausse alerte. Cependant, si le modèle ainsi obtenu est complet celui-ci n’est pas correct. En effet, ce dernier représente une sur-approximation du comportement normal des programmes consid-érés. Par conséquent, il y a un risque avec notre approche que certaines intrusions ne soient pas détectées.

Dans ce chapitre, nous présentons l’approche comportementale que nous pro-posons pour détecter une intrusion dans un programme. Pour cela, nous abordons d’abord les différents types d’attaques dont peut être la cible un programme partic-ulier et parmi celles-ci lesquelles notre approche cherche spécifiquement à détecter. Nous abordons ensuite en détails la méthode que nous proposons pour détecter le type d’attaques que nous avons identifié comme étant la cible de notre approche. Nous verrons notamment comment nous pouvons construire le modèle de comporte-ment normal associé à notre approche pour la détection. Enfin, nous présentons aussi

l’outil que nous avons développé pour valider et tester notre approche ainsi que les outils d’analyse statique sur lesquels nous avons fondé notre implémentation.

2.1 Les attaques contre les applications

Une intrusion est le résultat d’une attaque réalisée avec succès sur le système d’information ciblé. Si l’attaque échoue, on dit alors qu’une tentative d’intrusion a eu lieu. Au niveau d’un hôte appartenant au système d’information, une attaque peut viser son système d’exploitation ou bien les applications qui s’exécutent au dessus de ce dernier. Quelle que soit la cible, une attaque peut chercher soit à épuiser les ressources du composant ciblé soit à exploiter une vulnérabilité dans sa conception. Dans le premier cas, l’attaque a nécessairement pour but de violer la propriété de disponibilité (on parle alors d’attaque par déni de service [Gli84, Ove99, Cri00]). Au niveau du système d’exploitation, un déluge de paquet réseau est un exemple de déni de service par épuisement de ressource [Edd07].

Dans le second cas, l’attaque cherche en priorité à violer les contraintes de con-fidentialité et d’intégrité. Au niveau du système d’exploitation, la corruption des données d’un pilote de périphérique mal conçu est un exemple d’exploitation de vul-nérabilité permettant de violer l’intégrité du système [But07, Bul07]. Dans le cas où un utilisateur malveillant cherche à attenter à l’intégrité d’une application, l’attaque peut être réalisée à un moment donné en corrompant une ou plusieurs données dans l’espace mémoire du processus en cours d’exécution. Ceci est vrai quelle que soit la vulnérabilité exploitée par l’attaque : dépassement de tampon sur la pile ou sur le tas, dépassement de la capacité d’un entier, dépassement de chaîne formatée, etc.

Ces données peuvent être classées en deux catégorie : les données de contrôle et les données de calcul. Une attaque contre les données de contrôle cherche à corrompre les données utilisées par le processus pour contrôler son flot d’exécution. Une adresse de retour sur la pile ou un pointeur de fonction dans le gestionnaire d’exception sont des exemples de données prises pour cible par de telles attaques. En modifiant ces données, il est possible de forcer l’exécution du processus vers du code injecté ou hors contexte. Dans les deux cas, le code exécuté est illégal et se trouve sur un chemin invalide.

Une attaque contre les données de calcul cherche à corrompre les données utilisées par le processus pour effectuer les tâches qui lui incombent. Les variables contenant les valeurs nécessaires à l’évaluation des branchements conditionnels ou au calcul des arguments d’un appel système sont des exemples de données prises pour cible par de telles attaques. En modifiant ces données, il est possible d’utiliser de manière invalide du code légalement exécutable. Par exemple, il est possible d’effectuer un appel système illégal bien que ce dernier soit situé dans du code valide en l’exécutant au travers d’un chemin invalide ou en lui fournissant des arguments incorrects. Nous allons détailler pour ces deux types d’attaques différentes techniques d’exploitation.