Sunteți pe pagina 1din 161

MATHIEU COUTURE

DÉTECTION D’INTRUSIONS ET ANALYSE


PASSIVE DE RÉSEAUX

Mémoire présenté
à la Faculté des études supérieures de l’Université Laval
dans le cadre du programme de maı̂trise en informatique
pour l’obtention du grade de Maı̂tre ès sciences

DÉPARTEMENT D’INFORMATIQUE ET DE GÉNIE LOGICIEL


FACULTÉ DES SCIENCES ET DE GÉNIE
UNIVERSITÉ LAVAL
QUÉBEC

juin 2005

c
°Mathieu Couture, 2005
Résumé

Dans ce travail, nous proposons un nouveau langage dédié à la détection d’intru-


sions. Il s’agit d’un langage purement déclaratif, basé sur une logique temporelle linéaire
passée avec prédicats du premier ordre. Après avoir effectué une revue de treize lan-
gages de détection d’intrusions existant déjà, nous donnons une liste de dix propriétés
souhaitables pour un langage de détection d’intrusions. Contrairement au langage que
nous proposons, aucun des langages étudiés ne présente à la fois ces dix propriétés. En
plus d’être suffisamment expressif pour répondre aux besoins identifiés, il vient avec un
algorithme de vérification qui s’exécute en temps linéaire avec la quantité d’information
analysée et utilise une quantité bornée d’espace mémoire. Ces deux propriétés per-
mettent de protéger le système de détection d’intrusions contre d’éventuelles attaques
d’inondation.
Avant-propos

Je désire d’abord remercier ma mère, qui a su me donner le goût des études, ainsi
que mon père, qui m’a appris l’importance de l’intégrité et de la transparence. Je les
remercie tous les deux pour le support et les encouragements qu’ils m’ont donné tout
au long de mon cheminement, autant personnel qu’académique.

Je remercie aussi mon directeur de recherches, le professeur Béchir Ktari, pour avoir
accepté de me diriger dans un domaine aussi stimulant, et pour la confiance qu’il m’a
montrée tout au long de ces deux années de travail. J’adresse aussi mes remerciements
à Frédéric Massicotte, ami et collaborateur au Centre de Recherches sur les Communi-
cations, qui a su à plusieurs occasions me donner des critiques constructives sur mon
travail. Je le remercie aussi pour son sens aigu du bien-être d’autrui, qu’il a su en partie
me communiquer. Je tiens aussi à exprimer ma reconnaissance envers les professeurs
Josée Desharnais et Mohamed Mejri qui ont accepté d’évaluer ce mémoire. Je remer-
cie le personnel administratif du département d’informatique, et plus particulièrement
Lynda Goulet, pour sa bonne humeur et les nombreux services qu’elle m’a rendus.

Je remercie le Centre de Recherches sur les Communications, pour son support


matériel et financier, le Gouvernement Français, pour m’avoir accueilli et avoir financé
en partie mes recherches, de même que les professeurs André-Luc Beylot, Marc Boyer et
Jérôme Ermont, pour leur chaleureux accueil à l’ENSEEIHT, à Toulouse. Je remercie
le Ministère des Relations Internationales du Québec, pour avoir rendu possible cet
échange avec l’ENSEEIHT.

Merci à Catherine et aux colocs du 22A, pour leur présence chaleureuse et les nom-
breuses soirées que nous avons passées ensemble. Merci à Alexandre Lacasse, pour son
agréable compagnie au cours des deux voyages que nous avons faits en France, de même
que pour ses commentaires encourageants et constructifs. Finalement, je remercie toutes
celles et ceux de mes amies et amis qui m’ont encouragé et supporté dans mes projets.
À ma mère, Nicole,
avec qui tout a commencé.

Imagination is more important than


knowledge.
Table des matières

Résumé ii

Avant-propos iii

Table des matières v

Liste des tableaux viii

Table des figures x

Introduction 1

1 Systèmes de détection d’intrusions 5


1.1 Langages spécifiques au domaine . . . . . . . . . . . . . . . . . . . . . . 7
1.1.1 Panoptis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.1.2 Snort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.3 NeVO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2 Langages impératifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.2.1 ASAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.2.2 BRO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.3 Systèmes de transitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.3.1 STAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.3.2 IDIOT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.3.3 BSML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.4 Systèmes experts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.4.1 P-BEST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
1.4.2 LAMBDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.5 Logiques temporelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.5.1 LogWeaver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.5.2 Monid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.5.3 Chronicles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
1.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Table des matières vi

2 Logiques temporelles 55
2.1 Logiques temporelles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
2.1.1 Logique temporelle linéaire . . . . . . . . . . . . . . . . . . . . . . 58
2.1.2 Logique temporelle linéaire passée . . . . . . . . . . . . . . . . . . 60
2.1.3 Logique temporelle linéaire avec passé oubliable . . . . . . . . . . 63
2.1.4 µ-calcul linéaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.2 Logiques temporisées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
2.2.1 Logique temporelle temporisée . . . . . . . . . . . . . . . . . . . . 67
2.2.2 Logique temporelle métrique . . . . . . . . . . . . . . . . . . . . . 70
2.2.3 TRIO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.3 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72

3 Détection d’intrusions et analyse passive 74


3.1 Architecture du système . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
3.2 Langage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
3.3 Scénarios d’attaques complexes . . . . . . . . . . . . . . . . . . . . . . . 78
3.4 Acquisition passive d’information . . . . . . . . . . . . . . . . . . . . . . 80
3.4.1 Ports TCP ouverts et sessions actives . . . . . . . . . . . . . . . . 80
3.4.2 Ports TCP fermés . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
3.4.3 Sessions fermées et balayage FIN . . . . . . . . . . . . . . . . . . 82
3.4.4 Vulnérabilités et systèmes d’exploitation . . . . . . . . . . . . . . 83
3.4.5 Adresses du routeur . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.4.6 Inférence de nouvelles connaissances . . . . . . . . . . . . . . . . 85
3.5 Présentation formelle du langage . . . . . . . . . . . . . . . . . . . . . . 86
3.5.1 Modèle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
3.5.2 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.5.3 Sémantique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.5.4 Algorithme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.6 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

4 Logique d’acquisition de connaissances 97


4.1 Un paradigme passé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.1.1 Modèle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.1.2 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.1.3 Sémantique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
4.2 Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
4.2.1 Modèle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
4.2.2 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
4.2.3 Sémantique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
4.3 Récursivité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
4.3.1 Approximants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Table des matières vii

4.3.2 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113


4.4 Algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
4.4.1 Cas propositionnel . . . . . . . . . . . . . . . . . . . . . . . . . . 115
4.4.2 Cas du premier ordre . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129

5 Travaux futurs 132


5.1 Satisfiabilité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.2 Filtrage dynamique et sémantique de symptôme . . . . . . . . . . . . . . 134
5.3 Synthèse de contrôleur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

Conclusion 142

Bibliographie 144
Liste des tableaux

1.1 Algorithme d’apprentissage et de vérification de Panoptis. . . . . . . . . 9


1.2 Syntaxe abstraite de RUSSEL. . . . . . . . . . . . . . . . . . . . . . . . . 20
1.3 Syntaxe des patrons dans BSML. . . . . . . . . . . . . . . . . . . . . . . 33
1.4 Syntaxe du calcul d’événements utilisé dans LAMBDA. . . . . . . . . . . 38
1.5 Syntaxe d’un scénario d’attaque LAMBDA. . . . . . . . . . . . . . . . . 39
1.6 Syntaxe de la première logique de LogWeaver. . . . . . . . . . . . . . . . 42
1.7 Syntaxe des spécifications Eagle. . . . . . . . . . . . . . . . . . . . . . . . 45
1.8 Prédicats de réification de Chronicles. . . . . . . . . . . . . . . . . . . . . 48
1.9 Dix propriétés souhaitables d’un IDS. . . . . . . . . . . . . . . . . . . . . 52
1.10 Tableau récapitulatif des IDS. . . . . . . . . . . . . . . . . . . . . . . . . 53

2.1 Familles de logiques temporelles et leur modèle. . . . . . . . . . . . . . . 57


2.2 Syntaxe de LTL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
2.3 Sémantique de LTL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.4 Opérateurs définis de LTL. . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.5 Syntaxe de P-LTL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.6 Sémantique des opérateurs particuliers à P-LTL. . . . . . . . . . . . . . . 61
2.7 Opérateurs définis de P-LTL. . . . . . . . . . . . . . . . . . . . . . . . . 61
2.8 Syntaxe de N-LTL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
2.9 Sémantique de l’opérateur particulier à N-LTL. . . . . . . . . . . . . . . 64
2.10 Syntaxe du µ-calcul linéaire. . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.11 Sémantique de l’opérateur particulier au µ-calcul linéaire. . . . . . . . . . 65
2.12 Opérateurs définis du µ-calcul linéaire. . . . . . . . . . . . . . . . . . . . 66
2.13 Syntaxe de T-LTL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
2.14 Sémantique des opérateurs particuliers à T-LTL. . . . . . . . . . . . . . . 69
2.15 Un opérateur défini de T-LTL. . . . . . . . . . . . . . . . . . . . . . . . . 69
2.16 Syntaxe de MTL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
2.17 Sémantique des opérateurs particuliers à MTL. . . . . . . . . . . . . . . . 70
2.18 Syntaxe de TRIO. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.19 Sémantique de l’opérateur particulier à TRIO. . . . . . . . . . . . . . . . 71
2.20 Quelques opérateurs définis de TRIO. . . . . . . . . . . . . . . . . . . . . 72
Liste des tableaux ix

3.1 Syntaxe du langage de signatures. . . . . . . . . . . . . . . . . . . . . . . 77


3.2 Syntaxe. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
3.3 Sémantique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.4 Syntaxe du sous-ensemble associé au langage de signatures. . . . . . . . . 90
3.5 Algorithme de vérification. . . . . . . . . . . . . . . . . . . . . . . . . . . 92

4.1 Syntaxe pour le cas propositionnel. . . . . . . . . . . . . . . . . . . . . . 99


4.2 Sémantique pour le cas propositionnel. . . . . . . . . . . . . . . . . . . . 100
4.3 Syntaxe pour le cas du premier ordre. . . . . . . . . . . . . . . . . . . . . 103
4.4 Sémantique opérationnelle pour le cas du premier ordre. . . . . . . . . . 106
4.5 Approximants. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
4.6 Approximants avec intervalles. . . . . . . . . . . . . . . . . . . . . . . . . 114
4.7 Algorithme de vérification pour le cas propositionel. . . . . . . . . . . . . 116
4.8 Algorithme de vérification pour le cas du premier ordre. . . . . . . . . . . 120
4.9 Sémantique dénotationnelle pour le cas du premier ordre. . . . . . . . . . 121

5.1 Système de preuves à base de tableaux. . . . . . . . . . . . . . . . . . . . 134


5.2 Sémantique de symptôme. . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.3 Syntaxe de l’algèbre des automates. . . . . . . . . . . . . . . . . . . . . . 137
5.4 Sémantique de l’algèbre des automates. . . . . . . . . . . . . . . . . . . . 137
5.5 Algorithme de synthèse de contrôleur. . . . . . . . . . . . . . . . . . . . . 139
Table des figures

1.1 Exemple de fichier de configuration de Panoptis. . . . . . . . . . . . . . . 8


1.2 Exemple de fichier de planification de tâches de Panoptis. . . . . . . . . . 9
1.3 Exemple de fichier de sortie de Panoptis. . . . . . . . . . . . . . . . . . . 9
1.4 Structure de Snort. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.5 Exemple de signature Snort. . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.6 Exemple d’utilisation du module de réaction. . . . . . . . . . . . . . . . . 14
1.7 Exemple d’utilisation du module flowbits. . . . . . . . . . . . . . . . . . . 14
1.8 Exemple de signature NeVO. . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.9 Détection d’une pénétration externe dans ASAX. . . . . . . . . . . . . . 21
1.10 Structure de Bro. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.11 Exemple de script Bro. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.12 Session à demi ouverte dans NetSTAT. . . . . . . . . . . . . . . . . . . . 28
1.13 Session à demi ouverte dans l’outil STATed. . . . . . . . . . . . . . . . . 29
1.14 Modélisation d’un réseau avec NetSTAT. . . . . . . . . . . . . . . . . . . 29
1.15 Session TCP dans IDIOT. . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.16 Attaque TCP SYN-Flood dans BSML. . . . . . . . . . . . . . . . . . . . 34
1.17 Exemple de règle d’inférence dans P-BEST. . . . . . . . . . . . . . . . . 37
1.18 Modélisation du scénario d’accès illégal à un fichier avec LAMBDA. . . . 40
1.19 Exemple de spécification Eagle. . . . . . . . . . . . . . . . . . . . . . . . 45
1.20 Exemple de chronique. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

2.1 Exemples de programmes. . . . . . . . . . . . . . . . . . . . . . . . . . . 60

3.1 Architecture du système développée (à droite), et celle de Snort (à gauche). 76
3.2 Balayage SYN. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.3 Poignée de main TCP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.4 Ports TCP fermés. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.5 Fermeture d’une session TCP. . . . . . . . . . . . . . . . . . . . . . . . . 81
3.6 Détection d’un système d’exploitation FreeBSD 3.0 à 4.3. . . . . . . . . . 83
3.7 Règle Snort numéro 343. . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.8 Réduction des faux-positifs à l’aide de la détection de vulnérabilités. . . . 84
3.9 Adresses du routeur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Table des figures xi

3.10 Inférence de nouvelles connaissances. . . . . . . . . . . . . . . . . . . . . 85


3.11 Exemple de trace. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.12 Formule représentant une poignée de main TCP. . . . . . . . . . . . . . . 88
3.13 Spécification associée au scénario d’une poignée de main TCP. . . . . . . 91
3.14 Exemple de trace d’exécution de l’algorithme. . . . . . . . . . . . . . . . 93

4.1 Attaque dans le contexte d’une session TCP active. . . . . . . . . . . . . 99


4.2 Exemple de trace pour le cas propositionnel. . . . . . . . . . . . . . . . . 101
4.3 Exemple de relations de satisfaction pour le cas propositionnel. . . . . . . 101
4.4 Exemple de trace du premier ordre. . . . . . . . . . . . . . . . . . . . . . 102
4.5 Suivi des sessions TCP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
4.6 Exemple de relations de satisfaction pour le cas du premier ordre. . . . . 107
4.7 Utilisation des approximants. . . . . . . . . . . . . . . . . . . . . . . . . 113
4.8 Utilisation des approximants avec intervalles. . . . . . . . . . . . . . . . . 114
4.9 Balayage de ports TCP. . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

5.1 Exemples de contradictions. . . . . . . . . . . . . . . . . . . . . . . . . . 133


5.2 Exemple de conflit. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
5.3 Automate TCP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.4 Contrôleur pour l’automate TCP relativement à ¬(rst ∧ [synack, ack]). . 138
5.5 Contrôleur universel pour ¬(rst ∧ [synack, ack]). . . . . . . . . . . . . . . 140
Introduction

Contexte

La détection d’intrusions est un problème inhérent à la nature abstraite d’un réseau


informatique. Il est plutôt rare que l’on puisse dire, par un simple coup d’oeil aux
équipements informatiques, que ceux-ci ont été ou sont présentement utilisés d’une
façon non-souhaitable. Pour le savoir, il faut étudier attentivement des quantités la
plupart du temps astronomiques de fichiers d’audit et faire preuve d’une patience et
d’une capacité d’analyse hors du commun. Afin de nous alléger cette tâche ardue, les
informaticiens se sont mis à développer des outils logiciels automatisant une partie du
travail. Ils les ont appelés systèmes de détection d’intrusions.

Les systèmes de détection d’intrusions sont généralement classés en deux catégories :


les détecteurs d’anomalies et les systèmes à base de scénarios. Les détecteurs d’anoma-
lies fonctionnent généralement en deux phases : une phase d’apprentissage et une phase
de détection. Au cours de la phase d’apprentissage, le système apprend à reconnaı̂tre
ce qu’est un comportement normal, et au cours de la phase de détection, il identifie les
comportements anormaux. Les systèmes pour lesquels la phase d’apprentissage continue
après le début de la phase de reconnaissance sont dits adaptatifs. L’objectif des systèmes
adaptatifs est de tenir compte de la nature changeante des réseaux informatiques. Les
systèmes à base de scénarios n’ont pas de phase d’apprentissage. Ils viennent avec une
base de scénarios d’attaque prédéfinis qu’ils doivent reconnaı̂tre. L’avantage des détec-
teurs d’anomalies est donc de pouvoir détecter des scénarios d’attaque insoupçonnés,
au risque de générer des faux-positifs (fausses alarmes), alors que celui des systèmes à
base de scénarios est de générer moins de faux-positifs, au risque de laisser passer des
attaques encore inconnues. Dans ce cas, on parle de faux-négatifs. Le présent travail
s’intéresse surtout aux systèmes à base de scénarios.

Une autre façon de classer les systèmes de détection d’intrusions est selon le niveau
auquel ils interviennent : hôte ou réseau. Au niveau hôte, les fichiers d’audit analysés
Introduction 2

sont des fichiers systèmes : utilisation du processeur, du système de fichiers, du réseau,


traces d’exécution de programmes, etc., alors qu’au niveau réseau, les fichiers d’audit
utilisés sont des traces de trafic. Une trace de trafic est une copie de tout le trafic ayant
circulé à un endroit donné d’un réseau informatique. Il s’agit en fin de compte d’un cas
particulier de fichier d’audit, et c’est pourquoi certains systèmes de détection présentent
une approche générale permettant de regrouper les deux cas de cette classification.

Plusieurs paradigmes, auxquels nous reviendrons, ont été proposés pour les lan-
gages de signatures des systèmes de détection d’intrusions à base de scénarios. Parmi
ceux-ci, se trouvent ceux basés sur des logiques temporelles. Les logiques temporelles,
en méthodes formelles, ont été utilisées pour s’attaquer à trois catégories de problèmes :

Vérification Étant donné un programme, décider, avant même de l’exécuter, si toutes


ses exécutions respecteront une propriété donnée.
Synthèse Étant donnée une propriété, générer un programme tel que toutes ses exé-
cutions respectent cette propriété.
Validation Étant donnée une exécution d’un programme, décider si cette exécution
respecte une propriété donnée.

Le présent travail s’inscrit donc dans un contexte de validation. Dans notre cas, le
programme à valider sera le réseau, et son exécution sera matérialisée par les différents
fichiers d’audit mis à notre disposition. Bien que nous nous intéressions particulièrement
aux traces de trafic, la méthode que nous proposons s’adapte aussi bien aux autres
fichiers d’audit. Il convient de noter, avant d’aller plus loin, que la détection d’intrusions
a ceci de particulier que les exécutions auxquelles on s’intéresse sont de longueur infinie.
Les méthodes proposées pour des exécutions finies de programmes ne sauraient donc
s’appliquer ici.

Objectifs et méthodologie

Nous nous sommes donné comme mandat de développer un langage de détection


d’intrusions qui soit basé sur un paradigme purement déclaratif. Pour ce faire, nous
avons d’abord dû effectuer une revue des langages de détection d’intrusions existant
déjà. Bien que plusieurs d’entre eux aient la prétention d’être déclaratifs, il s’est avéré
que le bien fondé de cette affirmation était dans la plupart des cas discutable. Ceux
étant le plus près de répondre à ce critère étant ceux basés sur un paradigme de logique
temporelle, nous avons privilégié cette voie pour la poursuite de nos travaux.
Introduction 3

Nous avons donc dû nous familiariser avec les logiques temporelles, et voir comment
celles-ci pouvaient être utilisées pour répondre à nos besoins. Un de ces besoins était
d’acquérir passivement de l’information sur un réseau informatique, de façon à faire une
détection d’intrusions plus accrue. En effet, un reproche ayant souvent été fait aux sys-
tèmes de détection d’intrusions est de ne pas tenir compte du contexte avant de signaler
des alarmes. Cette sauvegarde du contexte ne saurait cependant être faite sans un coût
additionnel, le pire cas, tout à fait déraisonnable, constituant la sauvegarde complète de
la trace de trafic. Au mieux, on ne sauvegarde que l’information nécessaire, pas un bit
de plus. La capacité à traiter un fichier d’audit enregistrement par enregistrement sans
revenir en arrière est ce que nous appellerons traitement en ligne. Les trois objectifs
principaux de notre travail sont donc :

1. Paradigme déclaratif,
2. acquisition passive d’information,
3. et traitement en ligne.

Nous proposons deux approches pour s’attaquer à ces problèmes. La première est
une approche hybride où la séparation entre l’information acquise et les scénarios à
reconnaı̂tre est claire, tant au niveau du langage que du logiciel développé, et la seconde
approche permet d’uniformiser l’acquisition d’information et la définition de scénarios
à reconnaı̂tre. Dans le premier cas, le langage utilisé pour décrire les scénarios est basé
sur une logique temporelle avec opérateurs futurs, et dans le second cas, il est basé sur
une logique temporelle avec opérateurs passés.

Organisation

La première partie de ce mémoire est une revue de littérature en deux volets. Au


chapitre 1, nous dressons un état de l’art détaillé des langages de signatures utilisés par
treize systèmes de détection d’intrusions. Nous proposons une taxonomie divisant ces
langages en cinq catégories, selon les paradigmes utilisés. À la fin de ce chapitre, nous
dressons une liste de dix propriétés identifiées comme souhaitables pour un langage de
détection d’intrusions. Au chapitre 2, nous présentons les logiques temporelles ayant le
plus influencé notre travail.

La seconde partie de ce mémoire présente le travail effectué. Au chapitre 3, nous


décrivons un outil d’acquisition passive d’information et de détection d’intrusions que
nous avons développé. Cet outil utilise un langage de signatures qui permet de com-
Introduction 4

biner et d’effectuer les deux tâches dans un paradigme uniforme. Au chapitre 4, nous
présentons un autre langage, développé en vue de combler les lacunes identifiées avec le
premier. Entre autres, ce langage a la propriété d’être purement déclaratif. Pour cha-
cun de ces deux langages, nous présentons les algorithmes de vérification à utiliser et
donnons les preuves de complétude et de cohérence.

La troisième partie constitue la conclusion. Au chapitre 5, nous donnons quelques


idées pour la continuation des travaux de recherche effectués dans le cadre de cette
maı̂trise. Nous dressons un bilan des travaux effectués au dernier chapitre.
Chapitre 1

Systèmes de détection d’intrusions

Un système de détection d’intrusions (ou IDS pour Intrusion Detection System) est
une composante logicielle responsable d’identifier une utilisation non-désirable d’une
ressource informatique. On peut classer les IDS selon plusieurs critères. Les critères
les plus généralement rencontrés sont le type de sonde (réseau ou hôte) et le type
d’algorithme (statistique ou à base de signatures).

Les systèmes basés sur des méthodes statistiques, aussi appelés détecteurs d’anoma-
lies, présentent l’avantage de pouvoir découvrir des attaques encore non-répertoriées.
Ils fonctionnent généralement en deux phases : une phase d’apprentissage et une phase
de détection proprement dite. Pendant la phase d’apprentissage, le système dresse un
profil de ce qui sera par la suite considéré comme un comportement normal. La phase
de reconnaissance, quand à elle, consistera à détecter des déviations du comportement
normal. Normalement, la phase d’apprentissage continue pendant la phase de recon-
naissance. Cette stratégie permet de faire face à la nature changeante du comportement
des utilisateurs et ainsi de s’adapter au changement. L’hypothèse derrière cette façon
de faire est que le comportement normal d’un utilisateur change de façon graduelle
et qu’une utilisation non-désirée du réseau entraı̂nera un changement brusque dans le
comportement. Cette hypothèse entraı̂ne à la fois des faux-positifs (fausse alarme) et
des faux-négatifs (attaque non-détectée). Les faux positifs surviennent lorsque les uti-
lisateurs changent leur comportement de façon brusque, par exemple en installant un
nouveau logiciel. Les faux négatifs, quand à eux, surviennent lorsqu’un utilisateur mal-
veillant au courant de la stratégie de détection change son comportement petit à petit
de façon à exploiter la capacité d’adaptation du système.

Les IDS fonctionnant à base de signatures, quand à eux, engendrent moins de faux-
positifs car on leur spécifie exactement ce qu’ils doivent détecter. Cependant, comme
Chapitre 1. Systèmes de détection d’intrusions 6

on doit constamment tenir à jour leur base de signatures, le risque de faux-négatifs


est plus élevé. Le modèle de système de détection d’intrusions à base de signatures le
plus simple que nous puissions imaginer est celui généralement utilisé dans le domaine
des anti-virus : celui du patron de bits. L’hypothèse menant à cette méthode de travail
est qu’un comportement non-désirable peut être détecté à partir d’un petit nombre de
bits physiquement situés à proximité les uns des autres. Dans le cas des anti-virus, il
s’agit généralement des premiers bits du fichier, alors que dans les IDS réseaux tels
que Snort [1], il peut s’agir de quelques bits bien ciblés d’un seul paquet. Bien que
naı̈ve en apparence, la stratégie du patron de bits a mené jusqu’à date à de très bons
résultats, et ce pour deux raisons : premièrement, la complexité algorithmique liée à la
vérification des signatures est des plus avantageuses (constante en mémoire et linéaire en
temps) ; deuxièmement, la simplicité du langage de signature encourage la communauté
d’utilisateurs à participer à l’enrichissement de la base de données de signatures.

Les IDS fonctionnant à base de patrons de bits comportent cependant des faiblesses
au niveau de l’expressivité des signatures qui peuvent mener autant à des faux-positifs
qu’à des faux-négatifs. C’est pourquoi la communauté scientifique persiste à mettre
tant d’efforts dans le développement d’IDS pouvant détecter des patrons d’événements
séparés dans le temps. Les signatures de ces systèmes permettent de prendre en compte
le contexte avant de signaler une alarme. Par exemple, dans le cas d’un IDS réseau, ils
permettent de spécifier que pour être valide, une attaque doit survenir dans le contexte
d’une session active. Différents modèles théoriques ont été utilisés dans le cadre du
développement de ces systèmes, et nous allons consacrer le reste de ce chapitre à leur
étude.

Dans [2], Cédric Michel et Ludovic Mé proposent une taxonomie des langages utilisés
en détection d’intrusions divisant ceux-ci en six catégories : langages d’événements,
d’exploits, de rapports, de détection, de corrélation et de rapports. Dans ce chapitre,
nous nous attaquons au cas particulier des langages de détection, que nous subdivisons
en cinq catégories.

Nous commencerons notre étude dans la section 1.1 par les langages les plus simples :
les langages spécifiques au domaine. Ces langages ont l’avantage de présenter une grande
simplicité, mais offrent souvent une faible expressivité et peu de souplesse. Par la suite,
dans la section 1.2, nous jetterons un oeil aux langages impératifs ayant été développés
spécialement pour la détection d’intrusions. Ce sont ceux qui se rapprochent le plus des
langages de programmation habituels. Ils présentent l’avantage d’offrir des primitives
associées au traitement d’événements et des systèmes de types appropriés à la détection
d’intrusions. Les signatures développées avec ces langages sont cependant souvent diffi-
ciles à maintenir. Les langages basés sur les systèmes de transition, que nous traiterons
Chapitre 1. Systèmes de détection d’intrusions 7

dans la section 1.3, ont été mis au point afin de pouvoir spécifier d’une façon déclarative
les scénarios d’attaques. Ces langages comportent cependant une partie impérative dont
l’objectif est souvent d’emmagasiner une partie du contexte. Les systèmes experts, dont
nous parlerons dans la section 1.4, sont spécialisés dans l’accumulation et, surtout, l’in-
férence d’information concernant le contexte. Afin d’éviter les problèmes de mémoire,
cette information doit cependant être gérée explicitement, et le niveau d’abstraction
désiré n’est pas toujours atteint. Les systèmes basés sur des logiques temporelles, dont
nous parlerons finalement dans la section 1.5, sont ceux qui se rapprochent le plus de
systèmes à base de signatures purement déclaratives.

1.1 Langages spécifiques au domaine

Un langage spécifique au domaine, tel que défini dans [3], est un langage de pro-
grammation spécialement conçu pour capturer la sémantique propre à un domaine d’ap-
plication particulier. Des exemples de langages spécifiques au domaine sont HTML et
SQL, respectivement conçus pour l’édition d’hypertextes et la consultation de bases de
données. Dans le présent chapitre, chacun des systèmes de détection d’intrusions que
nous présentons vient avec son propre langage permettant de le configurer et/ou de
spécifier des signatures particulières d’attaque. Ces langages, spécialement conçus pour
la détection d’intrusions ou pour la surveillance de systèmes en général, tombent donc
tous dans la catégorie des langages spécifiques au domaine. Cependant, dans la plupart
des cas, ces langages sont conçus avec un certain souci d’abstraction permettant des
les utiliser pour différents types de fichiers d’audit. Par exemple, le langage STATL
peut être utilisé autant pour une détection d’intrusions au niveau hôte (dans le cas de
WinSTAT et USTAT), qu’au niveau réseau (dans le cas de NetSTAT).

Les trois systèmes que nous présentons dans cette section se distinguent de par le
fait que les langages qu’ils utilisent sont liés d’encore plus près au type de détection
particulier qu’ils permettent d’effectuer. Ils ne sont pas définis sur une sémantique abs-
traite de fichiers d’audit, mais sur des sémantiques très concrètes reliées aux fichiers
d’audits de processus (dans le cas de Panoptis), au filtrage de paquets (dans le cas de
Snort), et à l’identification passive de failles de sécurité (dans le cas de NeVO). Nous
présentons dès maintenant ces trois systèmes.
Chapitre 1. Systèmes de détection d’intrusions 8

#
# Configuration file for host pooh
#
# $ Id : poo.dsl 1.6 2000/05/30 12 :26 :58 dds Exp $
#
HZ = 100 # "Floating point" value divisor
bigend = FALSE # Set to TRUE for big endian (e.g. Sun), FALSE for
# little endian (e.g. VAX, Intel x86)
map = TRUE # Set to TRUE to map uid/tty numbers to names
EPSILON = 150 # New maxima difference threshold (%)
report = TRUE # Set to TRUE to report new/updated entries
unlink = FALSE # Set to TRUE to start fresh
# Reporting procedure
output = ’| /usr/bin/tee /dev/console | /bin/mail root’
# Databases and parameters to check
dbcheck(tty, minbmin, maxbmin, maxio, maxcount) # Terminals
dbcheck(comm, ALL) # Commands
dbcheck(uid, ALL) # Users
dbcheck(uidtty, maxcount) # Users on a terminal
dbcheck(uidcomm, minbmin, maxbmin, maxutime, # Users of a command
maxstime, maxmem, maxrw, maxcount, maxasu)
# Map users and terminals into groups
usermap(caduser, john, marry, jill)
usermap(admin, root, bin, uucp, mail, news)
termmap(room202, tty31, tty32, tty33, tty34, tty35)
termmap(ptys, ttyp01, ttyp02, ttyp03, ttyp04, ttyp05, ttyp06)

Fig. 1.1 – Exemple de fichier de configuration de Panoptis.

1.1.1 Panoptis

Panoptis [3] se distingue des autres systèmes de détection d’intrusions présentés dans
ce chapitre par le fait qu’il s’agit du seul système basé sur une détection d’anomalies
que nous avons choisi de présenter. Comme nos travaux se situent plutôt dans le cadre
de reconnaissance de scénarios, nous avons mis le focus sur ces types de systèmes, mais
nous avons tout de même jugé utile, par souci de complétude, d’inclure au moins un
détecteur d’anomalies.

Comme nous l’avons déjà dit, Panoptis est configurable à l’aide d’un langage spéci-
fique au domaine. Ce domaine est celui des processus d’un système Unix, et Panoptis
prend donc en entrée des fichiers d’audits de processus. Les auteurs affirment que le
langage a quand même été conçu de façon assez générale pour pouvoir fonctionner sur
n’importe quel système fournissant des fichiers d’audits de processus, dont Windows NT.

Panoptis maintient des tables contenant les profils d’activités pour différents utilisa-
teurs, terminaux, processus, ou groupages et/ou couplage de ces entités. Par exemple,
il peut maintenir une table concernant l’utilisation d’un processus donné, pour trois
utilisateurs spécifiques utilisant un même terminal. Les informations maintenues pour
Chapitre 1. Systèmes de détection d’intrusions 9

#
# Panoptis crontab file for host pooh
#
# The format of this file is :
# Hour Minute Day-of-month Month Day-of-week Command
* 5,25,45 * * * panoptis pooh-quick.cfg pooh.20min 20m
8-18 05 * * * panoptis pooh-hour.cfg pooh.workhour 1h
19-7 05 * * * panoptis pooh-hour.cfg pooh.late 1h
4 50 * * 1-5 panoptis pooh-day.cfg pooh.workday 24h
4 50 * * 6,0 panoptis pooh-day.cfg pooh.weekend 24h
2 20 * * 0 panoptis pooh-full.cfg pooh.weekly 7d
/usr/adm/pacct ? /usr/adm/pacct

Fig. 1.2 – Exemple de fichier de planification de tâches de Panoptis.

read and parse the the domain-specific language configuration


while there are records in the specified accounting file
read and decode an accounting record
synthesise the derived quantities
substitute the name of entities belonging to a group with the group name
for every database table specified
look for an database entry matching the key of the record retrieved
if a matching entry is found
compare it with the entry read
if the accounting record value exceeds the amount stored in the database
produce a new maximum value alert and update the database
else (if no matching entry is found)
produce a new value alert and update the database

Tab. 1.1 – Algorithme d’apprentissage et de vérification de Panoptis.

Database Users*Commands, key [dds/gcc] : New entry.


Command : gcc Terminal : ttyp0 User : dds
Executed from : 2001-01-13 01 :29 :32 to : 2001-01-13 01 :29 :33 (1.36 seconds)
Database Users*Commands, key [root/ls] :
New maximum average character input/output (204.8 / 64 ; 220%)
Command : ls Terminal : ttyp1 User : root
Executed from : 2001-01-13 01 :32 :13 to : 2001-01-13 01 :32 :13 (0.41 seconds)

Fig. 1.3 – Exemple de fichier de sortie de Panoptis.


Chapitre 1. Systèmes de détection d’intrusions 10

chacune des entités surveillées sont paramétrables à l’aide d’un fichier de configuration.
Un exemple de fichier de configuration utilisé par Panoptis se trouve à la figure 1.1. Ce
fichier se divise en quatre parties. En premier lieu, on trouve un ensemble de définitions
de variables qui servent à définir le fonctionnement général du programme. Ensuite, on
indique la méthode de rapport à utiliser. La troisième partie est celle où on spécifie
ce que l’on veut surveiller. Pour chacune des cinq tables tty, comm, uid, uidtty et
uidcomm, on spécifie les champs que l’on veut surveiller. Ces champs sont prédéfinis et
font partie de la définition du langage. Le mot-clé ALL signifie que l’on veut surveiller
tous les champs. Finalement, on trouve les options de groupage qui permettent de re-
grouper certains utilisateurs ou terminaux en un seul. Par exemple, à la figure 1.1, les
utilisateurs john, marry et jill sont regroupés sous l’utilisateur abstrait caduser.

Une fois ces options choisies, il reste à configurer la fréquence à laquelle les fichiers
d’audits sont lus par le système. Panoptis peut soit les lire en continu, soit être exécuté
à des intervalles prédéfinis. Un exemple de fichier de planification de tâches se trouve
à la figure 1.2. On voit que des intervalles différents, de même que des fichiers de
configuration différents, peuvent être utilisés selon les périodes de la journée ou de la
semaine. Finalement, à chaque fois que Panoptis est exécuté sur un fichier d’audit,
celui-ci compare les valeurs calculées à celles se trouvant dans les tables et rapporte les
entrées qui présentent des différences significatives.

À la table 1.1, on peut voir l’algorithme d’apprentissage et de vérification utilisé par


Panoptis. Après avoir synthétisé les données du fichier d’audit, il compare, pour chaque
entrée, la valeur obtenue avec celle qu’il avait déjà. Si aucune valeur n’était présente, un
événement est généré pour signaler une nouvelle entrée et la valeur est insérée dans la
table. Si, au contraire, une valeur était déjà présente, les deux valeurs sont comparées
et si la différence entre celles-ci dépasse le seuil de tolérance spécifié dans le fichier de
configuration, un événement est généré. Ensuite, la nouvelle valeur obtenue est insérée
à la place de l’ancienne. Panoptis est donc un système tolérant à des changements de
comportements graduels. D’un côté, cette caractéristique présente l’avantage de bien
refléter le fait que les utilisateurs changent petit à petit leurs habitudes, mais d’un
autre côté, elle présente aussi l’inconvénient d’offrir ainsi à un utilisateur malveillant
un moyen d’échapper à la détection de la modification de son comportement.

À la figure 1.3, se trouve un exemple de fichier de sortie de Panoptis. Il y a deux


sortes d’événements : ceux correspondant à une nouvelle entrée dans une table et ceux
correspondant au dépassement du seuil de tolérance. Dans chacun des cas, on donne
le nom de la table concernée ainsi que la clé de l’entrée ayant généré l’événement et
le moment où celui-ci est survenu. Dans le cas où il s’agit d’un dépassement de seuil,
on donne aussi la nouvelle valeur. Le premier événement de la figure 1.3 signifie donc
Chapitre 1. Systèmes de détection d’intrusions 11

que l’utilisateur dds a utilisé le compilateur gcc pour la première fois, alors que la
seconde signifie que l’utilisateur root, à l’aide de la commande ls, a listé le contenu de
répertoires contenant deux fois plus de fichiers qu’à l’habitude.

En résumé, Panoptis constitue un cas typique de système de détection d’intrusions


effectuant de la détection d’anomalies. Il est configurable à l’aide d’un langage très
simple et spécifique à l’analyse de fichiers d’audits de processus sur un système Unix.
Un tel outil fait partie des composantes essentielles à une architecture de sécurité car
il permet d’avoir une vision résumée de fichiers d’audits qui, dû à leur grande taille,
seraient inutilisables sinon. Comme la plupart des systèmes effectuant de la détection
d’anomalies, Panoptis est sujet à un grand nombre de faux positifs et sa nature adap-
tative le rend vulnérable aux utilisateurs qui modifieraient tranquillement leur profil
afin de ne pas se faire remarquer. Cependant, son mode de fonctionnement générique le
rend capable de détecter des attaques qu’un système fonctionnant à base de signatures
aurait pu laisser passer.

1.1.2 Snort

Le système de détection d’intrusions Snort [1], originellement conçu pour être un


renifleur (sniffer ) plus évolué que tcpdump [4], s’est avéré petit à petit très efficace
comme outil de détection d’intrusions. En effet, il dispose d’un langage de signatures
permettant de décrire avec une grande précision les caractéristiques des paquets of-
fensifs circulant sur le réseau1 . L’hypothèse derrière le choix de Snort comme système
de détection d’intrusions est donc que les attaques sont repérables à l’aide d’un seul
paquet. Bien que les efforts investis par la communauté pour développer des langages
de signatures permettant de tenir compte de plusieurs paquets - voir tout le reste du
présent chapitre - incitent à penser qu’un langage de signatures concernant un seul
paquet n’est pas suffisamment expressif, la pratique démontre qu’il est tout de même
possible de détecter un grand nombre d’attaques sur la base d’un tel langage (la base
de signatures de Snort contient, à ce jour, plus de 2000 signatures d’attaques).

La structure logicielle de Snort se trouve à la figure 1.4. Utilisant, tout comme


tcpdump, la librairie de capture de paquets LibPcap [5], Snort comporte un module de
décodage de paquets permettant de décoder un grand nombre de protocoles de différents
niveaux. Au dessus de ce module de décodage se situe un ensemble de préprocesseurs,
dont la fonction originale était de régulariser le format des paquets afin de faciliter la
1
Une autre approche consiste à détecter les paquets résultants de l’attaque. Lorsqu’il est possible
de définir de tels paquets, le nombre de faux positifs est alors diminué.
Chapitre 1. Systèmes de détection d’intrusions 12

Modules d’affichage

Module de détection

Préprocesseurs

Module de décodage

Libpcap

Réseau

Fig. 1.4 – Structure de Snort.

tâche de la base de règles.

Par exemple, si une politique de sécurité spécifie que personne ne doit se connecter
au service Telnet en utilisant l’utilisateur root, on voudrait pouvoir spécifier une règle
qui surveille les paquets contenant la chaı̂ne de caractères user : root. Cependant, il
est possible d’échapper à cette règle en utilisant le caractère de retour en arrière <BS>.
En tapant user : roo<BS>ot, un utilisateur peut alors se connecter sous le compte
root sans se faire repérer. Des astuces similaires s’appliquent au trafic HTTP. Pour ces
raisons, Snort comporte des préprocesseurs responsables de régulariser le trafic Telnet
et HTTP. Il est cependant possible d’échapper au système de détection d’intrusions
autrement qu’en utilisant des astuces de formatage au niveau application. Une astuce
bien connue consiste à fragmenter le paquet offensif en plusieurs petits paquets, de
façon à ce qu’aucun des paquets résultants ne soit alarmant. Une autre façon de faire
est d’envoyer les paquets dans un ordre ne correspondant pas à celui dans lequel ils
seront interprétés par la machine qui les recevra. Les préprocesseurs frag2 et stream4
ont donc été ajoutés afin de remettre ensemble les paquets fragmentés et de présenter
les paquets au système de détection d’intrusions dans le même ordre que celui dans
lequel le système les recevant les interprètera. Il s’agit encore une fois d’un travail de
formatage visant à éviter certaines attaques d’évasion.

En plus de cette fonctionnalité, les préprocesseurs sont aussi utilisés à deux autres
fins : détecter les scénarios d’attaques utilisant plusieurs paquets, ainsi que permettre
l’acquisition passive d’information. Un exemple simple de catégorie d’attaques nécessi-
tant la détection de plusieurs paquets est le balayage (scan). Qu’il s’agisse d’un balayage
TCP, UDP, ICMP ou autre, les attaques de balayage ont pour objectif de permettre
Chapitre 1. Systèmes de détection d’intrusions 13

alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg :"SCAN nmap TCP" ;
stateless ; flags :A,12 ; ack :0 ; reference :arachnids,28 ;
classtype :attempted-recon ; sid :628 ; rev :3 ;)

Fig. 1.5 – Exemple de signature Snort.

à l’attaquant d’acquérir de l’information sur le réseau ou la machine victime afin de


mieux se préparer à son attaque. Les balayages peuvent s’effectuer en utilisant des
fonctionnalités tout à fait normales de la pile TCP/IP, et la seule façon de les détec-
ter consiste généralement en la détection de l’utilisation abusive de ces fonctionnalités.
Certains préprocesseurs (par exemple portscan2) ont donc été ajoutés afin de compter,
par exemple, le nombre de connexions TCP demandées par un hôte distant dans une
fenêtre de temps donnée.

Effectuer un minimum d’acquisition est plus que souhaitable pour un système de


détection d’intrusions : il s’agit d’une fonctionnalité vitale. Un exemple de préproces-
seur effectuant de l’acquisition passive d’information est le préprocesseur flow, dont la
tâche est de tenir à jour une table des sessions TCP actives. Avant l’arrivée de flow, il
était possible d’exploiter le fait que Snort fonctionnait paquet par paquet pour générer
automatiquement des paquets correspondant aux signatures d’attaque [6], engorgeant
ainsi le système de détection d’intrusions de faux-positifs.

Au dessus de tous ces préprocesseurs, se trouvent les modules de détection. Ces


derniers sont responsables de reconnaı̂tre les profils de paquets offensifs. La description
des paquets à reconnaı̂tre se trouve dans un fichier texte qui est lu à l’amorçage du
programme. Le langage utilisé pour décrire ces paquets est relativement simple, ce qui
explique en partie la popularité et la croissance rapide du nombre d’attaques que Snort
est maintenant capable de reconnaı̂tre. Typiquement, une signature Snort est une suite
de mots-clé suivis de zéro ou plusieurs arguments. Certains mot-clés ne concernent pas
la détection, mais sont simplement ajoutés afin de documenter l’attaque. Une fois les
paquets reconnus, une série de modules d’affichage et d’archivage sont responsables de
l’interaction avec l’usager. Lorsque les attaques sont identifiées, Snort peut, entre autres,
afficher un message à l’écran, ajouter une ligne dans un fichier texte, une entrée dans
une base de données, envoyer un message via le protocole SNMP, etc.

À la figure 1.5, on présente un exemple de signature Snort. L’interprétation de cette


signature se lit comme suit : si un paquet TCP provenant de l’extérieur ($EXTER-
NAL_NET) pénètre dans notre réseau ($HOME_NET), peu importe les ports (any), et que
ce paquet a le drapeau ACK activé, de même que les deux bits réservés (flags :A,12),
et que le numéro d’acquiescement est 0 (ack :0), peu importe l’état de la session (sta-
Chapitre 1. Systèmes de détection d’intrusions 14

alert tcp any any <> 192.168.1.0/24 80 (content : "sex" ;


msg : "Not for children !" ; react : block, msg ;)

Fig. 1.6 – Exemple d’utilisation du module de réaction.

alert tcp any 143 -> any any (msg :"IMAP login" ; content :"OK LOGIN" ;
flow :established,to_client ; flowbits :set,logged_in ; flowbits :noalert)
alert tcp any any -> any 143 (msg :"IMAP list overflow attempt" ;
flow :established,to_server ; flowbits :isset,logged_in ; content :"LIST" ;)

Fig. 1.7 – Exemple d’utilisation du module flowbits.

teless), il faut alors signaler un balayage TCP fait avec nmap [7] (msg :"SCAN nmap
TCP"). Le reste de la signature constitue la documentation de l’attaque.

Cette revue de Snort serait incomplète si aucune allusion n’était faite à la nature
particulière des modules de détection react et flowbits. Le module react fait de Snort
non plus un simple système de détection d’intrusions, mais un système de prévention
d’intrusions. Il permet de réagir en interrompant les sessions TCP jugées offensives. Il
permet, de plus, d’envoyer un message d’avertissement à l’usager expliquant pourquoi la
communication a été interrompue. À la figure 1.6, on voit comment ce module peut être
utilisé pour empêcher les enfants d’aller sur des sites pornographiques. Lorsqu’un usager
tente de consulter un site web contenant le mot sex, la session TCP correspondante
est interrompue et le message not for children est affiché dans son navigateur. Cet
exemple est tiré de [1].

Le module flowbits, quant à lui, a été mis au point afin de répondre au besoin de
plus en plus grandissant de permettre à Snort de tenir compte de l’état des sessions au
niveau application. Il permet de définir des variables booléennes (drapeaux) servant à
suivre l’état de la session en changeant la valeur de ces variables (set, unset, reset,
toggle) dans certaines règles, et en les lisant dans d’autres (isset, isnotset). Comme
ce module a été mis au point pour suivre l’état des sessions au dessus de TCP, ces
variables sont instanciées et initialisées à chaque nouvelle session TCP. Il est à noter
que les règles servant à mettre à jour les valeurs des drapeaux contiennent généralement
le mot-clé noalert, signifiant que même si la règle est activée, aucune alerte ne doit
être générée. À la figure 1.7, on peut voir comment l’utilisation du module de détection
flowbits permet de diminuer le nombre de faux positifs lors de la détection d’une attaque
de débordement de tampon d’un serveur IMAP, un protocole s’exécutant généralement
Chapitre 1. Systèmes de détection d’intrusions 15

sur le port TCP numéro 143. Cette attaque, exploitant une vulnérabilité de certaines
versions de imapd, s’effectue en utilisant incorrectement la commande LIST, et ne peut
réussir que si l’usager est correctement connecté (au niveau application). La première
signature de la figure 1.7 sert donc à mémoriser le fait que le mot de passe de l’usager a
été accepté. Ce sera le cas si on voit passer la chaı̂ne de caractères OK LOGIN. Dans ce
cas, on active drapeau logged_in et on spécifie que cette règle ne doit pas déclencher
d’alerte en utilisant le mot-clé noalert. L’utilisation du préprocesseur flow nous permet,
d’une part, de vérifier que le paquet survient bel et bien dans le contexte d’une session
TCP active, et d’autre part, qu’il provient bien du serveur. La seconde signature permet
la détection de l’attaque proprement dite. Il s’agit d’une version légèrement modifiée
de la signature numéro 2118. Une alerte sera lancée si on voit passer le mot LIST en
direction du serveur dans le cadre d’une session TCP active appartenant à un usager
ayant réussi à se connecter.

En résumé, Snort est un système de détection d’intrusions ouvert jouissant d’une


grande popularité. Il fonctionne au niveau réseau et sa base de signatures contient la
description d’un nombre considérable d’attaques. Il comporte plusieurs préprocesseurs
qui permettent de faciliter le travail des modules de détection ainsi que de protéger le
système d’évasions communes et d’attaques d’inondation de faux-positifs. Ces modules
devraient faire partie intégrante de tout bon système de détection d’intrusions. Les
modules de détection, situés au-dessus des préprocesseurs, permettent à l’utilisateur
de définir ses propres règles dans un langage simple. Certains moyens ont été mis en
oeuvre pour compenser les manques les plus importants reliés à la nature un paquet-
une signature de ce langage. Cependant, à mesure que ces moyens sont mis en place, il
devient de plus en plus évident qu’un langage de spécification idéal devrait pouvoir tenir
compte de plusieurs paquets. Le reste de ce chapitre est donc consacré aux langages
permettant d’exprimer des signatures s’étalant sur plusieurs paquets (ou événements).

1.1.3 NeVO

NeVO [8] n’est pas un système de détection d’intrusions, mais un identificateur


passif de failles de sécurité. La raison pour laquelle nous le mentionnons ici est que
la connaissance des failles de sécurité du réseau fait partie des moyens étant souvent
mentionnés pour réduire le nombre de faux positifs générés par un système de détection
d’intrusions [9, 10]. Dans [9], on trouve une courte étude des différentes façons dont les
identificateurs de failles de sécurité pourraient ou devraient, selon les besoins, coopérer
avec les systèmes de détection d’intrusions. Par exemple, la connaissance des failles de
sécurité peut être utilisée pour activer ou désactiver certaines règles du système de dé-
tection d’intrusions, diminuant ainsi à la fois le nombre de faux positifs et la demande
Chapitre 1. Systèmes de détection d’intrusions 16

faite au processeur de la machine sur laquelle le système s’exécute. D’autres pensent


plutôt que toutes les attaques devraient continuer à être surveillées, sauf que l’outil de
visualisation utilisé pour consulter le journal d’attaques devrait permettre d’identifier
celles dont le succès est le plus vraisemblable étant données les vulnérabilités identifiées.
Réciproquement, un autre modèle de coopération envisageable serait de lancer l’identi-
ficateur de failles seulement au moment où les attaques surviennent, allégeant ainsi le
travail de ce dernier plutôt que celui du système de détection d’intrusions. Finalement,
on ne doit pas oublier, lors de l’étude de ces différents modèles, que les identificateurs
de failles sont autant sujets aux faux positifs et aux faux négatifs que les systèmes de
détection d’intrusions.

NeVO, mis au point par la compagnie Tenable, est un identificateur passif de failles
de sécurité, ce qui signifie qu’il effectue son travail sans avoir à injecter de trafic sur le
réseau. Il se présente comme un complément ou une alternative à Nessus [11, 12, 13], qui
est un identificateur actif de failles de sécurité. Bien que l’identification active puisse
en général fournir des rapports plus exhaustifs que l’identification passive, elle peut
souvent s’avérer très dérangeante pour le réseau en cours d’audit, voire même mener à
son instabilité. De plus, l’identification passive permet d’avoir accès à une information
continuellement mise à jour. Finalement, l’identification passive permet non-seulement
de trouver les failles sur les serveurs, mais aussi sur les clients. Pour toutes ces raisons,
l’identification passive s’impose comme un complément essentiel à l’identification active.

À première vue, un identificateur passif de failles de sécurité ne devrait pas fonction-


ner différemment d’un système de détection d’intrusions. D’un point de vue abstrait, on
tente d’identifier un comportement caractéristique de l’information à laquelle on s’in-
téresse. Les auteurs de [8] citent cependant deux différences notables entre le mode de
fonctionnement d’un identificateur de failles et celui d’un système de détection d’intru-
sions : la tolérance à l’échantillonnage et le modèle d’accès à l’information.

Tolérance à l’échantillonnage : Alors que le fait de n’être pas capable de traiter


en temps réel tout le trafic circulant sur le réseau peut être considéré, pour un
système de détection d’intrusions, comme une faiblesse majeure, cette particularité
est beaucoup plus tolérable pour un identificateur passif de failles de sécurité.
L’hypothèse permettant de dire qu’un identificateur de failles peut fonctionner
aussi bien sur une base échantillonnale qu’en observant la totalité du trafic est
que le comportement caractéristique de la faille sera observable pratiquement à
chaque fois que le système défaillant entrera en communication. Si on n’identifie
pas la faille maintenant, on l’identifiera bien plus tard.
Modèle d’accès à l’information : L’information acquise par un identificateur pas-
sif de failles de sécurité ne doit pas, contrairement à celle acquise par un sys-
Chapitre 1. Systèmes de détection d’intrusions 17

id=700018
nooutput
hs_sport=21
name=Anonymous FTP (login : ftp)
pmatch=^USER ftp
match=^331
NEXT #-------------------------------
id=700019
dependency=700018
timed-dependency=5
hs_sport=21
name=Anonymous FTP enabled
description=The remote FTP server has anonymous access enabled.
risk=LOW
pmatch=^PASS
match=^230

Fig. 1.8 – Exemple de signature NeVO.

tème de détection d’intrusions, être archivée à chaque fois qu’elle est détectée. Au
contraire, l’identificateur doit plutôt tenir à jour un modèle du réseau et le fournir
sur demande. Cette demande sera typiquement faite au moment de la consultation
du journal d’attaques du système de détection d’intrusions. L’information n’est
donc pas envoyée directement à l’utilisateur, mais conservée jusqu’à ce qu’elle soit
demandée.

Ces deux différences concernant l’architecture logicielle d’un identificateur passif de


failles ne justifient cependant pas le fait que le langage utilisé pour décrire les façons de
détecter les failles soit différent de celui utilisé par un système de détection d’intrusions,
et c’est pourquoi nous nous attarderons maintenant au langage de NeVO. Ce langage
est fortement dédié à l’acquisition passive d’information sur un réseau informatique. De
plus, l’acquisition d’information faite à l’aide de ce langage se limite au niveau appli-
cation. Cela signifie que l’inspection faite sur les paquets se limite à la partie données
de ceux-ci, et non aux différents champs des différents protocoles ni à l’interaction de
ceux-ci. NeVO incorpore cependant un module permettant de détecter passivement les
systèmes d’exploitation des machines communiquant sur le réseau en inspectant les
entêtes des paquets SYN, mais ce module n’utilise pas le langage de NeVO.

Une signature NeVO concerne généralement un seul paquet. On spécifie le contenu


recherché à l’aide du mot-clé match. Cependant, il est possible de spécifier, à l’aide
du mot-clé pmatch, le contenu du paquet précédent dans la même session. Par défaut,
le contenu est recherché en mode texte. Il est cependant possible de rechercher un
contenu binaire à l’aide du mot-clé bmatch. Le mot-clé regex permet de rechercher des
expressions régulières. Le mot-clé dependency permet de spécifier qu’une signature ne
doit être considérée, pour un flux TCP donné, que si une autre signature a été activée.
Chapitre 1. Systèmes de détection d’intrusions 18

Le mot-clé time-dependency permet de limiter le délai d’attente entre l’activation des


deux signatures. Le mot-clé description permet de spécifier le message a afficher et le
mot-clé nooutput signifie qu’il n’y a pas de message à afficher. On trouve à la figure 1.8
un exemple de signature NeVO (tiré de [8]) responsable d’identifier les serveurs FTP
ayant un compte anonymous actif. Cette signature se divise en deux sous-signatures. La
première est responsable d’identifier les paquets contenant une demande de connexion
pour l’utilisateur ftp suivie d’une acceptation du nom d’utilisateur (code 331). La
seconde, activée seulement dans les 5 secondes suivant l’activation de la première, est
responsable d’identifier l’envoi d’un mot de passe suivi de l’acceptation de ce mot de
passe (code 230).

En résumé, NeVO est un exemple très intéressant d’identificateur passif de failles


de sécurité. Même s’il a été développé à des fins commerciales (habituellement, la docu-
mentation disponible sur les outils commerciaux se limite aux aspects externes), on peut
trouver une documentation riche et précise sur son fonctionnement interne. À première
vue, son langage dédié à l’identification de simples paquets au niveau application pour-
rait nous porter à croire que le nombre de comportements observables est plutôt limité,
mais la possibilité de spécifier une notion de dépendance entre les règles augmente sans
aucun doute l’expressivité du langage de façon significative. Cependant, cette façon ad
hoc de régler le problème des scénarios complexes ne saurait amener toute l’expressivité
désirable. Entre autres, elle ne permet pas de reconnaı̂tre le fait qu’un paquet donné
n’arrive pas dans un certain délai, ou encore de compter le nombre d’occurrences d’un
paquet donné. De plus, il est dommage que la sémantique du langage se limite au niveau
application. Le fait de pouvoir spécifier des paquets au niveau des différentes couches
de protocoles permettrait sans doute de détecter plus de failles et, sans nécessairement
se limiter aux failles, d’acquérir encore plus d’information.

1.2 Langages impératifs

La distinction entre les langages impératifs et les langages déclaratifs, dans la litté-
rature, n’est pas aussi nette que l’on pourrait le croire. Généralement, on se contente
de définir informellement un langage déclaratif comme étant un langage permettant de
définir ce que l’on veut identifier, plutôt que comment l’identifier. Les autres langages
tombent tous sous la catégorie des langages impératifs. Sur la base de cette définition,
le langage de signatures de Snort, par exemple, est clairement déclaratif.

À l’exception des langages présentés dans la présente section, les langages que nous
présentons dans ce chapitre ont tous la prétention d’être déclaratifs. Plus loin dans
Chapitre 1. Systèmes de détection d’intrusions 19

ce chapitre, nous proposerons une définition plus formelle permettant de départager


les langages déclaratifs des langages impératifs. Pour l’instant, nous présentons deux
langages qui, peu importe la définition utilisée, appartiennent certainement à la catégo-
rie des langages impératifs. RUSSEL, que nous présentons dans un premier lieu, a été
conçu dans le but d’analyser séquentiellement des fichiers d’audit, alors que Bro, que
nous présentons par la suite, a été conçu afin de pouvoir exprimer les signatures dans
un langage typé proche de celui utilisé pour exprimer les politiques de sécurité dans un
contexte de détection d’intrusions au niveau réseau.

1.2.1 ASAX

ASAX [14, 15, 16] a été développé dans le but d’analyser des traces d’audit. La syn-
taxe de RUSSEL, le langage utilisé pour écrire les signatures, se trouve à la table 1.2.
L’exécution d’un programme RUSSEL implique l’analyse séquentielle d’un fichier de
trace d’audit. Une trace d’audit est définie comme étant une séquence finie d’enregis-
trements, qui eux sont définis comme étant une fonction partielle associant des valeurs
à un ensemble d’étiquettes, aussi appelées champs. Ces enregistrements sont passés à
ASAX dans un format normalisé (Normalized Audit Data Format), qui permet de faire
abstraction du type d’audit.

La notion centrale de ASAX est celle de contexte d’exécution. Un contexte d’exé-


cution est entre autres caractérisé par trois ensembles de règles : Cur, Nxt et Cmp.
L’ensemble Cur contient les instances de règles qui doivent être exécutées sur l’en-
registrement courant, l’ensemble Nxt contient les instances de règles qui doivent être
exécutées sur l’enregistrement suivant, et l’ensemble Cmp contient l’ensemble des règles
qui doivent être exécutées une fois que tous les enregistrements sont traités. L’utilité de
ce dernier ensemble est de permettre d’effectuer des traitements sur des valeurs ayant
été accumulées tout au long du traitement de la trace d’audit. Par exemple, il permet
de calculer une moyenne et de la sauvegarder quelque part. Ces trois ensembles doivent
être maintenus à jour explicitement par celui qui écrit les règles via le mot-clé Trigger
off.

Une autre caractéristique d’un contexte d’exécution est l’ensemble des valeurs des
différentes variables. Ces variables peuvent être de trois types : locales à une règle, glo-
bales, ou correspondre aux champs d’un enregistrement. Comme l’ordre d’exécution des
règles n’est pas spécifié, l’utilisation de variables globales peut amener un certain non-
déterminisme. Aussi, pour palier aux problèmes découlant du fait que certains champs
peuvent être absents de certains enregistrements, on a ajouté un mot-clé present, per-
mettant de tester la présence d’un champ. Lorsqu’un champ est absent, il est tout de
Chapitre 1. Systèmes de détection d’intrusions 20

R : := rule Q ( . . . ; G ; . . . ) ; . . . ; H ; . . . ; A
G : := P:T
H : := V:T
A : := V := E | Y ( . . . , E , . . . )
| trigger off M Q ( . . . , E , . . . ) | begin . . . ; A ;. . . end
| do . . . ; C → A ; . . . od | if . . . ; C → A ; . . . fi
C : := true | F present | not C | C B C | E S E
E : := L | V | F | P | -E | E O E | X(. . . ,E,. . . )
B : := and | or
O : := + | - | * | div | mod
S : := > | < | = | 6= | ≤ | ≥
M : := for current | for next | at completion
T : := integer | byte | string

A actions O arithmetic operators


B logical operators P formal parameters
C conditions Q rule names
E expressions R rules
F field names S relational operators
G parameter declarations T types
H variable declarations V local variables
L literals X external function names
M triggering modes Y external procedure names

Tab. 1.2 – Syntaxe abstraite de RUSSEL.

même possible de lire sa valeur. La valeur lue est alors celle qui était présente la der-
nière fois que le champ était présent, et les résultats obtenus sont susceptibles d’être
très différents de ceux escomptés.

À la figure 1.9 se trouve un exemple de signature écrit en RUSSEL pour ASAX. Cet
exemple a été copié tel quel de [14]. Les variables evt et res sont des champs qui sont
supposés faire partie de chaque enregistrement. Ensemble, les deux règles Failed_login
et Count_rule1 servent à surveiller un utilisateur qui échouerait à se connecter trop
souvent. On remarque que la règle Failed_login se réactive inconditionnellement (ligne
8). La règle Count_rule1 démontre la sémantique particulière d’un bloc if . . . fi. Ces
blocs renferment une série de conditions, chacune suivie d’un bloc d’actions. Dès qu’une
condition est vérifiée, le bloc correspondant est exécuté et le bloc if . . . fi est terminé.
Chapitre 1. Systèmes de détection d’intrusions 21

01.rule Failed login (maxtimes, duration : integer)


02.# This rule detects a first failed login and triggers off an accounting rule with an
03.# expiration time
04.begin
05.if evt=’login’ and res=’failure’ and is unsecure (terminal)
06. → Trigger off for next Count rule1 (maxtimes-1, timestp+duration)
07.fi ;
08.Trigger off for next Failed login (maxtimes, duration)
09.end
10.
11.rule Count rule1 (countdown, expiration : integer)
12.# This rule counts the subsequent failed logins,
13.# it remains active until its expiration time or until the countdown becomes 0
14.if evt=’login’ and res=’failure’
15. and is unsecure(terminal) and timestp < expiration
16. → if countdown > 1
17. → Trigger off for next Count rule1(countdown-1, expiration) ;
18. countdown =1
19. → SendMessage (”too much failed login’s”)
20. fi ;
21. timestp ≥ expiration
22. → Skip ;
23. true
24. → Trigger off for next Count rule1 (countdown, expiration)
25.fi

Fig. 1.9 – Détection d’une pénétration externe dans ASAX.

L’instruction skip est l’instruction qui ne fait rien. Donc, si le délai est écoulé (ligne 21),
la règle n’est pas réactivée. Sinon, la condition true est vérifiée et la règle est réactivée
pour le prochain enregistrement.

En résumé, RUSSEL est un langage de programmation impératif comportant, en


plus des notions les plus communes, celles de contexte d’exécution et de variables d’en-
registrement. Ces notions permettent d’automatiser certaines tâches communes au trai-
tement séquentiel de fichier d’audit telles que la mise à jour de la base de règles et la
mise à jour des valeurs des champs des enregistrements. Cependant, il n’est pas du tout
clair que l’approche consistant à développer un nouveau langage ait été ici la meilleure
à adopter. Les notions particulières au langage RUSSEL auraient très bien pu être im-
plantées sans développer un nouveau langage. Par exemple, on aurait pu envisager une
approche orientée-objet dans un langage déjà connu de la communauté (tel que C++),
et arriver à des résultats semblables sinon meilleurs du point de vue de la facilité de
maintenance et de la courbe d’apprentissage.
Chapitre 1. Systèmes de détection d’intrusions 22

Interpréteur de scripts

Module d ’événements

Libpcap

Réseau

Fig. 1.10 – Structure de Bro.

1.2.2 BRO

Bro [17] est un système de détection d’intrusions conçu spécifiquement en vue de


détecter les attaques survenant au niveau réseau. Son architecture logicielle en couches,
que l’on peut voir à la figure 1.10, est comparable à celle de Snort. À la base, se trouve la
même librairie de capture de paquets (libpcap). Le module d’événements fait un travail
relativement semblable à celui auquel les préprocesseurs de Snort, conjointement au
module de décodage, étaient initialement destinés : faire un premier travail de formatage
sur les paquets afin de simplifier la tâche de l’interpréteur de scripts de politiques de
sécurité. Ce dernier est comparable au module de détection. Il n’y a pas, pour Bro,
d’équivalent des modules d’affichage. Tout est archivé dans des fichiers en format texte.

Malgré le fait que les deux architectures puissent se ressembler au premier abord,
il y a tout de même deux différences majeures entre les deux systèmes : l’abstraction
créée par le module d’événements et la nature impérative du langage de signatures.
Alors que les objets fournis par les préprocesseurs de Snort au module de détection
sont des paquets, ceux fournis par le module d’événements de Bro à l’interpréteur de
scripts sont d’un type plus abstrait : celui d’événement. Ceci implique qu’un processus
de filtrage est fait par le module d’événements, et que seulement les informations jugées
pertinentes sont passées à l’interpréteur de scripts. Un des objectifs de cette étape
de filtrage est de permettre au système de fonctionner en temps réel, malgré la grande
affluence de trafic pouvant circuler sur le réseau. Aussi, les politiques de sécurité vérifiées
avec Bro sont normalement plus haut niveau que celles vérifiées par Snort, voire même la
plupart des autres systèmes de détection d’intrusions. En fait, il s’agit d’un des objectifs
poursuivis par les auteurs : permettre une séparation claire entre les mécanismes et les
politiques de sécurité. Les mécanismes, dans ce cas, sont les différents moyens de générer
un événement donné, alors que les politiques de sécurité sont les actions à prendre
lorsque les événements sont identifiés. Les mécanismes sont donc gérés au niveau du
Chapitre 1. Systèmes de détection d’intrusions 23

01.global hot_names = { "root", "lp", "uucp" } ;


02.global finger_log = open("finger.log") ;
03.event finger_request(c :connection, request : string, full : bool)
04.{
05. if ( byte_len(request) > 80 ) {
06. request = fmt("%s...", sub_bytes(request, 1, 80)) ;
07. ++c$hot ;
08. }
09. if ( request in hot_names )
10. ++c$hot ;
11. local req = request == "" ? "ANY" : fmt("\"%s\"", request) ;
12. if ( c$addl != "" )
13. # This is an additional request.
14. req = fmt("(%s)", req) ;
15. if ( full )
16. req = fmt("%s (/W)", req) ;
17. local msg = fmt("%s > %s %s", c$id$orig_h, c$id$resp_h, req) ;
18. if ( c$hot > 0 )
19. log fmt("finger : %s", msg) ;
20. print finger_log, fmt("%.6f %s", c$start_time, msg) ;
21. c$addl = c$addl == "" ? req : fmt("*%s, %s", c$addl, req) ;
22.}

Fig. 1.11 – Exemple de script Bro.

moteur d’événements, programmé en C++, alors que les politiques de sécurité sont
mises en oeuvre par l’interpréteur de scripts. Ces dernières sont écrites dans un langage
de signatures propre à Bro.

Alors que le langage de signatures de Snort est un langage déclaratif décrivant


certaines caractéristiques bien ciblées des paquets circulant sur le réseau, celui de Bro est
un langage impératif, quasi aussi expressif que le langage C (sa syntaxe en est d’ailleurs
en plusieurs points très semblable), avec déclaration de variables (locales ou globales),
expressions composées, inférence et vérification de types, etc. Un des objectifs visé par le
développement de ce langage est de permettre, au moment de la compilation, d’effectuer
un maximum de vérification de types, de façon à ce qu’au moment de l’exécution, le
type des variables référencées soit cohérent. Pour ce faire, le langage de Bro dispose
de plusieurs types de variables propres à la réseautique : port, adresse IP, nom réseau,
etc. Le langage dispose aussi de structures de données abstraites : ensemble, table
d’associations, enregistrement ; permettant d’exprimer naturellement des vérifications
dans un langage proche des politiques de sécurité : si il y a une connexion Telnet
sur un des serveurs d’impression, alors. . . . Il est à noter que bien que le langage de
Bro soit impératif, celui-ci ne dispose pas de construction syntaxique itérative. Cette
caractéristique a été voulue par les concepteurs afin de minimiser le temps de traitement
d’un événement. Cependant, comme la récursivité est tout de même permise, l’itération
est toujours possible. Les concepteurs n’ont toutefois pas relevé de cas ou son utilisation
se faisait ressentir.
Chapitre 1. Systèmes de détection d’intrusions 24

À la figure 1.11, on peut voir un exemple de script de politique de sécurité écrit


dans le langage de Bro. Cet exemple, tiré de [17], montre comment on doit réagir à
une requête finger. L’opérateur $ sert à accéder aux différents champs des structures.
L’opérateur in (ligne 9) sert à vérifier si un élément fait partie d’un ensemble. Le script
vérifie en premier lieu si la requête est excessivement longue (ligne 5). Le cas advenant,
celle-ci est tronquée et le champ hot est incrémenté. Ensuite, il vérifie si la requête
concerne un nom sensible (ligne 9). Si tel est le cas, le champ hot est encore incrémenté.
La variable locale req est ensuite initialisée (lignes 11 à 16) et, si le champ hot a été
incrémenté (ligne 18), une notification en temps réel est effectuée (ligne 19). Dans tous
les cas, la requête est archivée (ligne 20). Finalement, la requête est enregistrée dans le
champ addl de la connexion (ligne 21). Advenant le cas où une autre requête a eu lieu
dans le contexte de la même connexion, ce qui peut être associé à un comportement
malicieux, elle est ajoutée à celle s’y trouvant déjà et un astérisque est ajouté pour
attirer l’attention lors de l’inspection du fichier d’audit.

L’algorithme de vérification en temps-réel de Bro fonctionne en une seule passe et


suivant un seul fil d’exécution (thread). Lorsqu’un paquet est capturé sur le réseau par
la librairie libpcap, il est passé au moteur d’événements. Celui-ci peut alors générer de
zéro à plusieurs événements pour un seul paquet (réciproquement, un type d’événement
donné peut être généré par plusieurs types de paquets différents). Ces événements sont
alors placés dans une file d’attente et, par la suite, traités par l’interpréteur de scripts de
politiques de sécurité. Pour un événement donné, il peut y avoir plusieurs traitements à
faire. Ceux-ci sont effectués dans l’ordre où ils apparaissent dans le fichier de script. Il
est à noter que ces traitements peuvent, en plus de générer des alarmes, créer d’autres
événements qui iront se placer à la fin de la file et devront être traités avant de retourner
au moteur d’événements et de passer au paquet suivant.

Les travaux entourant Bro sont grandement motivés par la pratique et l’article [17]
traite de plusieurs problèmes concrets devant être pris en compte lors de la concep-
tion d’un système de détection d’intrusions en temps réel. L’auteur parle entre autres
des différentes façons d’attaquer le système lui-même et des moyens de s’en défendre,
des problèmes reliés à la surveillance de réseaux haut-débit (principalement en ce qui
concerne la perte de paquets), des différentes façons de gérer les chronomètres, des
avantages et des inconvénients de disposer d’un langage compilé, des problèmes reliés
au redémarrage du système (souvent nécessaire pour faire un nettoyage de la mémoire),
etc. Quiconque s’engage dans des travaux reliés à la détection d’intrusions en temps
réel devrait au moins prendre quelques minutes pour jeter un oeil à cet article.

En résumé, Bro est un système de détection d’intrusions fonctionnant au niveau


réseau dont la conception a grandement été influencée par des problèmes pratiques. Il
Chapitre 1. Systèmes de détection d’intrusions 25

vient avec un langage impératif interprété qui permet d’exprimer des scripts vérifiant
des politiques de sécurité à un plus haut niveau que la plupart des autres systèmes de
détection d’intrusions. Son architecture en couches, semblable à celle de Snort, a été
conçu avec un souci d’extensibilité et permet d’ajouter des modules d’événements et
de définir nos propres politiques de sécurité assez facilement. Le langage utilisé pour
exprimer les politiques de sécurité reste cependant impératif, ce qui implique que l’on
indique plutôt comment vérifier les politiques, que les politiques elle-mêmes. Il faut
tout de même donner à Bro le mérite d’avoir eu l’idée de séparer les mécanismes des
politiques au niveau même de l’architecture du logiciel et d’avoir prévu deux modules
différents pour gérer ces deux aspects.

1.3 Systèmes de transitions

Les systèmes de transition jouissent d’une grande popularité en informatique lorsque


vient le temps de décrire des programmes ou des processus de façon abstraite. Les ob-
jectifs visés par ces descriptions varient de la simple communication entre les différents
intervenants dans le processus de développement à la vérification automatique de pro-
priétés de programmes. Les systèmes de détection d’intrusions que nous allons présenter
dans cette section utilisent différents formalismes basés sur des systèmes de transitions
pour représenter les attaques. STAT, dans un premier lieu, utilise les automates géné-
ralisés, qui diffèrent des automates traditionnels par la présence de variables dans les
états permettant de relier les différents symboles de l’alphabet (ou événements) entre
eux. IDIOT, dans un second lieu, utilise les réseaux de Petri colorés, qui diffèrent des ré-
seaux de Petri conventionnels de la même façon. L’avantage des réseaux de Petri sur les
automates est de permettre une représentation du synchronisme et du parallélisme de
façon plus naturelle. En troisième et dernier lieu, nous présenterons BSML, qui se base
sur les expressions régulières pour les événements, qui diffèrent elles aussi des expres-
sions régulières par la présence de variables du premier ordre, ainsi que de contraintes
de temps. Même si les expressions régulières sont connues pour avoir le même pouvoir
de représentation que les automates, BSML n’a pas exactement la même expressivité
que STATL. Ceci est dû au fait que STATL, comme nous le verrons, utilise plusieurs
types de transitions pour spécifier l’algorithme de vérification relié à l’automate, alors
que les expressions de BSML sont traduites en automates généralisés avec seulement
des transitions classiques.

Chacun des systèmes que nous présentons dans cette section a été conçu dans l’idée
de permettre la représentation des signatures d’attaque de façon déclarative. Les au-
teurs de ces travaux, surtout en ce qui concerne STATL et IDIOT, considèrent que
Chapitre 1. Systèmes de détection d’intrusions 26

les systèmes de transition constituent un moyen de représenter ce qui doit être détecté
plutôt que comment le détecter. Le dictionnaire en ligne Foldoc2 propose, pour le terme
langage déclaratif, la définition suivante :

Définition 1.1 (Langage déclaratif ) Any relational language or functional language.


These kinds of programming language describe relationships between variables in terms
of functions or inference rules, and the language executor (interpreter or compiler)
applies some fixed algorithm to these relations to produce a result.

Declarative languages contrast with imperative languages which specify explicit ma-
nipulation of the computer’s internal state ; or procedural languages which specify an
explicit sequence of steps to follow.

The most common examples of declarative languages are logic programming lan-
guages such as Prolog and functional languages like Haskell.

Tous les langages étudiés dans le reste de ce chapitre disposent d’un algorithme fixe
de vérification, comme mentionné dans le premier paragraphe de la définition. Cepen-
dant, comme nous le verrons, plusieurs sont aussi pourvus de constructions syntaxiques
qui permettent de modifier explicitement le contenu de la mémoire. En se référant au
second paragraphe de la définition, on réalise alors que l’on se rapproche de la frontière
entre langage déclaratif et langage impératif.

Maintenant que nous avons une définition plus claire de la différence entre langage
déclaratif et impératif, nous sommes prêts à étudier les systèmes de détection d’intru-
sions basés sur les systèmes de transition, qui sont à la frontière entre les deux types de
langages.

1.3.1 STAT

STAT (State Transition Analysis Technique) [18] utilise une approche qui se veut
déclarative. Le formalisme utilisé pour spécifier les signatures se base sur les automates
généralisés. STAT fournit un langage général, STATL (STAT Language), permettant de
spécifier des automates. La sémantique attribuée à ces systèmes dépend de l’extension
utilisée. Les différentes extensions permettent de faire différents types de détection d’in-
trusions. Par exemple, les extensions USTAT [19] et WinSTAT permettent de spécifier
des signatures au niveau hôte pour les systèmes d’exploitation Unix et Windows, alors
2
www.foldoc.org
Chapitre 1. Systèmes de détection d’intrusions 27

que l’extension NetSTAT [20] permet de spécifier des signatures au niveau réseau.

Les systèmes de transition définis à l’aide du langage STATL sont constitués d’états
et de transitions. Les transitions, qui peuvent être activées par les actions effectuées sur
le système, sont de trois types : consuming, non-consuming, et unwinding. Les transi-
tions de type consuming correspondent exactement aux transitions régulières des auto-
mates finis : lorsque l’action correspondant à l’étiquette de la transition est effectuée,
le système change d’état. Les transitions non-consuming créent une copie du système
avant de changer d’état. Les transitions consuming permettent donc de spécifier des pro-
priétés de la prochaine action telle que, alors les transitions non-consuming permettent
de spécifier l’existence d’une prochaine action telle que. Finalement, les actions unwin-
ding permettent d’éliminer des systèmes. Plus spécifiquement, ils permettent d’éliminer
toutes les copies ayant été faites du système depuis qu’il a traversé un état donné. Ceci
permet de représenter le fait qu’une action peut tout annuler.

À la figure 1.12, on peut voir un exemple, tiré de [18], de scénario exprimable à l’aide
du langage STATL. Ce scénario permet de détecter une session TCP à demi ouverte. Une
session TCP est dite à demi ouverte si le serveur a accepté une demande de connexion
(le SYN et le SYN-ACK ont eu lieu) et est en attente de la confirmation (le dernier
ACK) de la part de l’hôte ayant demandé la connexion. Si, pour un serveur donné,
trop de connexions sont simultanément à demi ouvertes, celui-ci peut cesser d’accepter
de nouvelles connexions. Il est possible d’exploiter ce fait pour effectuer une attaque
de déni de service, connue sous le nom de SYN-Flood. Le scénario de la figure 1.12
comporte donc trois états : l’état initial (s0), l’état en attente de confirmation (s1), et
l’état final s2. Le scénario est paramétré par une variable, timeout, indiquant combien
de temps on doit attendre la confirmation. La première transition du scénario, SYN,
est activée lorsqu’un paquet de type SYN (ayant le drapeau SYN activé et le drapeau
ACK désactivé) est identifié. Cette transition est de type non-consuming, signifiant que
lorsqu’elle est activée, une copie du scénario est effectuée avant de passer à l’état s1.
Aussi, lors de l’activation de cette transition, les variables locales du scénario servant
à mémoriser les adresses IP et les ports TCP de la victime et de l’attaquant sont
initialisées selon les valeurs contenues dans le paquet. On voit bien, avec la transition
SYN, les deux parties constituantes d’une transition : la condition d’activation et les
actions à poser lors de l’activation. En arrivant dans l’état s1, le chronomètre t0 est
démarré avec comme délai la valeur de la variable timeout fournie en paramètre. Si,
avant l’écoulement du délai, un paquet ACK ou un paquet RST sont vus pour les
adresses IP et les ports TCP correspondant au SYN (transitions ACK et RST), tout est
annulé et le scénario retourne à l’état original car ces transitions sont de type unwinding.
Si, au contraire, aucun de ces paquets n’est vu avant que le délai ne s’écoule, la transition
Timed_out est activée et le scénario passe (sans duplication, puisque la transition est de
Chapitre 1. Systèmes de détection d’intrusions 28

use netstat ;
scenario halfopentcp(int timeout)
{
IPAddress victim_addr ;
Port victim_port ;
IPAddress attacker_addr ;
Port attacker_port ;
timer t0 ;
initial state s0 {}
transition SYN (s0 -> s1) nonconsuming
{
[IP ip [TCP tcp]] :
(tcp.tcp_header.flags & TH_SYN) && !(tcp.tcp_header.flags & TH_ACK)
{
victim_addr=ip.header.dst ;
victim_port=tcp.header.dst ;
attacker_addr=ip.header.src ;
attacker_port=tcp.header.src ;
}
}
state s1
{
{ timer_start(t0, timeout) ; }
}
transition ACK (s1 -> s0) unwinding
{
[IP ip [TCP tcp]] :
(ip.header.dst==victim_addr) && (tcp.header.dst==victim_port) &&
(ip.header.src==attacker_addr) && (tcp.header.src==attacker_port) &&
!(tcp.header.flags & TH_SYN) && (tcp.header.flags & TH_ACK)
}
transition RST (s1 -> s0) unwinding
{
[IP ip [TCP tcp]] :
(ip.header.src==victim_addr) && (tcp.header.src==victim_port) &&
(ip.header.dst==attacker_addr) && (tcp.header.dst==attacker_port) &&
(tcp.header.flags & TH_RST)
}
transition Timed_out (s1 -> s2) consuming
{
timer t0 ;
}
state s2
{
{
HALFOPENTCP e ;
e = new HALFOPENTCP(attacker_addr, attacker_port, victim_addr,
victim_port, start) ;
enqueue_event(e, HALFOPENTCP, start) ;
}
}
}

Fig. 1.12 – Session à demi ouverte dans NetSTAT.


Chapitre 1. Systèmes de détection d’intrusions 29

Fig. 1.13 – Session à demi ouverte dans l’outil STATed.

Fig. 1.14 – Modélisation d’un réseau avec NetSTAT.


Chapitre 1. Systèmes de détection d’intrusions 30

type consuming) à l’état s2. En arrivant dans cet état, un nouvel événement est créé,
signalant ainsi le fait qu’une session à demi ouverte a été identifiée. Cet événement peut
alors servir de condition d’activation aux transitions d’autres scénarios, servant ainsi à
la détection, par exemple, d’une attaque SYN-Flood. À la figure 1.13, on peut voir une
représentation graphique de ce scénario, obtenue à l’aide de l’outil STATed, un éditeur
de scénarios téléchargeable avec STAT. Les conditions d’activation et les actions à poser
peuvent être spécifiées en cliquant sur les états et les transitions.

Un aspect intéressant de l’extension NetSTAT est qu’elle permet de spécifier la topo-


logie du réseau afin d’améliorer la détection d’intrusions. On peut voir, à la figure 1.14,
un exemple de telle modélisation. Un réseau est vu comme un ensemble d’interfaces,
alors que les hôtes et les liens de données sont vus comme deux partitions différentes de
cet ensemble. La spécification comprend en outre des renseignements sur ces hôtes et ces
interfaces tels que leurs adresses matérielles et logicielles. Ces informations permettent,
par exemple, de détecter des attaques d’usurpation d’identité en calculant le chemin par
lequel deux hôtes sont supposés communiquer. Par exemple, à la figure 1.14, Wilder ne
devrait pas voir passer de message en provenance de Carpenter pour Lang. Finalement,
la spécification de la topologie permet, en fonction des attaques que l’on veut détecter,
de calculer l’emplacement des sondes et de les configurer pour qu’elles surveillent les
événements pertinents.

En bref, le système STAT, avec le langage STATL, constitue une avancée en direction
d’un langage déclaratif. Cependant, avec ses trois types de transition, ses variables
partagées et ses actions à exécuter à l’activation des transitions, le langage STATL
comporte encore une bonne part d’impérativité. Un aspect très intéressant de l’extension
NetSTAT est la prise en compte de la topologie pour spécifier, améliorer et automatiser
une partie de la détection d’intrusions.

1.3.2 IDIOT

IDIOT [21, 22, 23], tout comme STAT, utilise une approche qui se veut déclarative.
Le formalisme employé pour spécifier les signatures se base sur les réseaux de Petri
colorés. Cette approche présente deux avantages. Premièrement, les réseaux de Petri
permettent de bien représenter le parallélisme et le synchronisme, deux phénomènes
bien présents en détection d’intrusions. Avec STAT, le parallélisme est sous-entendu par
l’exécution parallèle des systèmes de transition, alors que le synchronisme est représenté
par l’utilisation des variables partagées. Le deuxième avantage de l’approche choisie
réside dans le choix de réseaux colorés. Les réseaux colorés permettent, comme avec
STAT, de définir des patrons d’événements reliés les uns aux autres. Par exemple, ils
Chapitre 1. Systèmes de détection d’intrusions 31

01. extern int RLOGIN PORT CLIENT, RLOGIN PORT SERV,


02. print tcp conn(int, int) ;
03. pattern TCP Conn Mon ”Monitor rlogin connections” priority 10
04. int FROM PORT, FROM HOST ;
05. int TO PORT, TO HOST ;
06. state start ;
07. nodup state after syn, after syn ack ;
08. state after ack ;
09. post action{print tcp conn(FROM HOST, TO HOST) ;}
10. neg invariant first inv
11. state inv start, inv final ;
12. trans rst(TCP)
13. <- inv start ;
14. -> inv final ;
15. | {this[RST] = 1 && TO HOST = this[FROM HOST] &&
16. this[TO HOST] = FROM HOST ;}
17. end rst ;
18. end first inv
19. trans tcp 1(TCP) /* TCP is the event type of the transition */
20. <- start ;
21. -> after syn ;
22. | {this[SYN] = 1 && this[ACK] = 0 &&
23. FROM PORT = this[FROM PORT] &&
24. this[TO PORT] = RLOGIN PORT SERV &&
25. FROM HOST = this[FROM HOST] && TO HOST = this[TO HOST] ;}
26. end tcp 1 ;
27. trans tcp 2(TCP)
28. <- after syn ;
29. -> after syn ack ;
30. | { this[SYN] = 1 && this[ACK] = 1 &&
31. (this[FROM PORT] = RLOGIN PORT SERV)&&
32. (this[TO PORT] = FROM PORT) &&
33. (this[FROM HOST] = TO HOST) && (this[TO HOST] = FROM HOST) ;
34. }
35. end tcp 2 ;
36. trans tcp 3(TCP)
37. <- after syn ack ;
38. -> after ack ;
39. | { this[SYN] = 0 && this[ACK] = 1 &&
40. (this[FROM PORT] = FROM PORT) &&
41. (this[TO PORT] = RLOGIN PORT SERV) &&
42. (this[FROM HOST] = FROM HOST) && (this[TO HOST] = TO HOST) ;
43. }
44. end tcp 3 ;
45. end TCP Conn Mon ;

Fig. 1.15 – Session TCP dans IDIOT.


Chapitre 1. Systèmes de détection d’intrusions 32

permettent de spécifier que deux paquets différents doivent avoir les mêmes adresses IP
sources et destination.

La documentation sur IDIOT est plutôt rare, et il fait partie des systèmes qu’il ne
nous a pas semblé possible de télécharger. Ce que nous avons à notre disposition est
un nombre assez restreint d’exemples tirés d’articles. L’exemple de la figure 1.15 est
tiré de [23]. Il montre comment on peut utiliser, dans le système IDIOT, les réseaux de
Petri colorés pour spécifier l’établissement d’une session TCP. Aux lignes 6,7, et 8 se
trouvent les déclarations des états. Comme l’établissement d’une session TCP se fait
en trois étapes, on a besoin de quatre états pour la spécifier. Le mot-clé nodup, à la
ligne 7, signifie que lorsqu’une transition sortante est activée, l’état n’est pas dupliqué.
On obtient donc ici un résultat semblable aux transitions consuming de STAT, sauf
que la spécification se fait au niveau des états plutôt qu’au niveau des transitions. La
ligne 9 spécifie quelle est l’action à effectuer lorsque l’état final a été atteint. Les lignes
10 à 18 spécifient un invariant qui doit être vérifié par le système. La notion d’invariant,
en IDIOT, s’apparente à la notion de transition unwinding en STATL. La spécification
d’un invariant prend la forme d’un réseau de Petri, avec ses propres états et ses propres
transitions. À chaque fois qu’un jeton sort de l’état initial, une copie en est placée dans
l’état initial de l’invariant. Dans l’exemple qui nous occupe, l’invariant nous permet de
spécifier qu’aucun paquet RST ne doit être envoyé pendant l’établissement d’une session
TCP. Les lignes 19 à 26, 27 à 35, et 36 à 45 spécifient chacune des transitions. Les deux
premières lignes indiquent les états entrants et sortants de la transition, alors que le
reste spécifie les conditions devant être respectées par le jeton pour que la transition
soit activée.

En résumé, le système IDIOT semble présenter certains avantages au niveau de la


représentation du parallélisme et du synchronisme. Cependant, sa dépendance envers un
autre langage, qui se matérialise par la présence du mot-clé extern, en fait un système
incomplet qui devrait finalement plutôt être vu comme une librairie complémentaire
à un système déjà existant. De plus, la présence du mot-clé nodup nous amène au
même reproche que nous avons fait à STAT quand à la nature déclarative du langage.
Finalement, l’absence dans la littérature d’une documentation complète du langage, ne
serait-ce que de sa syntaxe, nous empêche de porter un jugement éclairé à souhait sur
ses forces et ses faiblesses.

1.3.3 BSML

Le langage BSML (Behavioral Specification Monitoring Language) [24] tend lui aussi
vers une approche déclarative. Le formalisme utilisé pour représenter les comportements
Chapitre 1. Systèmes de détection d’intrusions 33

¯ ¯ ¯ ¯ ¯
p := e(x1 , . . . , xn )|cond ¯ !p ¯ p1 ; p2 ¯ p1 ||p2 ¯ p∗ ¯ p within [t1 , t2 ]

Tab. 1.3 – Syntaxe des patrons dans BSML.

à identifier est celui des expressions régulières pour les événements (ERE). Les diffé-
rences entre les expressions régulières conventionnelles et les ERE sont la présence de
prédicats du premier ordre pour relier les événements entre eux et la possibilité de
spécifier des contraintes de temps-réel.

La syntaxe des ERE utilisées dans le langage BSML se trouve à la table 1.3. Un
patron d’événements est soit un événement primitif, paramétré par (x1 , . . . , xn ) et res-
pectant la condition cond, soit la non-occurrence d’un patron donné (!p), soit le patron
p1 suivi immédiatement du patron p2 (p1 ; p2 ), soit le patron p1 ou bien le patron p2
(p1 ||p2 ), soit la répétition un nombre indéterminé de fois du patron p (p∗ ), soit le pa-
tron p devant survenir dans l’intervalle de temps [t1 , t2 ] (p within [t1 , t2 ]). L’expression
p..q est utilisée pour abréger p; (!(p||q))∗ ; q. Aussi, on utilise les expressions p over t et
p within t pour abréger respectivement p within [t, ∞] et p within [0, t].

Une spécification BSML ne comporte cependant pas que des ERE. Une spécification
BSML est un ensemble de déclarations de variables ainsi que de couples p → act, où act
est une action à poser chaque fois que le patron p est reconnu. Cette action peut être
un simple archivage ou encore la manipulation (incrémentation, décrémentation) d’un
compteur. Plusieurs compteurs peuvent aussi être regroupés dans une table. La notion
de compteur en BSML est assez originale car elle permet de donner une valeur plus
élevée aux événements plus récents. Les conditions d’activation des actions associées
aux compteurs ne sont alors plus nécessairement définies sur le nombre d’événements
identifiés, mais sur le poids pondéré de ces événements.

L’exemple de la figure 1.16 est tiré de [24]. Cette signature sert à détecter une inon-
dation TCP SYN. Aux lignes 1 à 3, on définit un prédicat sur deux paquets TCP servant
à vérifier si ceux-ci appartiennent à la même session (dans des directions opposées, i.e.
avec les adresses IP et les ports inversés). À la ligne 5, on définit l’événement tcp_syn(p)
comme étant la réception d’un paquet (rx(p)) tel que le drapeau tcp_syn est activé,
mais pas le drapeau tcp_ack. À la ligne 6, on définit l’événement tcp_syn_ack(p,q)
comme étant l’envoi, en direction opposée, d’un paquet ayant les drapeaux tcp_syn et
tcp_ack activés. Finalement, à la ligne 7, on définit l’événement tcp_ack(q, r) comme
étant la réception, en direction opposée au syn_ack, d’un paquet ayant seulement le
drapeau tcp_ack d’activé. Aux lignes 9 à 13, on déclare la table neptune qui servira
Chapitre 1. Systèmes de détection d’intrusions 34

01.same_session(p, q) =
02. p.daddr=q.saddr && p.tcp_dport=q.tcp_sport &&
03. p.saddr=q.daddr && p.tcp_sport=q.tcp_dport
04.
05.event tcp_syn(p) = rx(p)| p.tcp_syn && !p.tcp_ack
06.event tcp_synack(p, q) = tx(q)| same_session(p,q) && q.tcp_syn && q.tcp_ack
07.event tcp_ack(q, r) = rx(r)| same_session(q,r) && !r.tcp_syn && r.tcp_ack
08.
09.Table neptune(
10. (unsigned int, unsigned short) /*key : (IP address,port)*/
11. 1000, 120, /*size 1000, window 120 seconds */
12. 4, 1, neptuneBegin, neptuneEnd /* thresholds and associated functions*/
13.)
14.
15.(tcp_syn(p1)..tcp_synack(p1,p2)) ;(( !tcp_ack(p2,p3))* over 60) ->
16. neptune.inc((p1.daddr, p1.tcp_dport))

Fig. 1.16 – Attaque TCP SYN-Flood dans BSML.

à compter les occurrences de poignées de main non-complétées. La clé de cette table


est le couple adresse-port destination du paquet syn. La fonction neptuneBegin est
appelée, par exemple pour lancer une alerte, chaque fois que 4 incrémentations sur une
entrée de la table sont faites dans un intervalle de 120 secondes. Cette incrémentation
est elle-même effectuée chaque fois que les deux premières étapes d’une poignée de main
TCP sont effectuées mais que la troisième ne survient pas dans les 60 secondes suivant
la deuxième (lignes 15 et 16).

Le langage BSML est un langage compilé, et les ERE sont transformées en machines
à états étendues. La différence entre une machine à état étendue et une machine à états
conventionnelle est la même qu’entre des réseaux de Petri conventionnels et des réseaux
de Petri colorés : les états de la machine contiennent des variables qui permettent de
relier entre eux les symboles lus par la machine. Bien entendu, les symboles de l’alphabet
(événements primitifs) sont donc eux aussi caractérisés par des variables.

Contrairement aux langages étudiés jusqu’à maintenant, le langage BSML permet


non-seulement de spécifier les comportements qui ne doivent pas survenir, mais aussi les
comportements qui doivent survenir, lançant ainsi des alarmes lorsque le comportement
observé dévie de ce qui est attendu. Le langage BSML permet ainsi une certaine forme
de détection d’anomalies, non pas basée sur des méthodes statistiques et l’établissement
empirique d’un profil normal, mais sur la spécification explicite de ce profil. L’avantage
principal de cette démarche, selon les auteurs, est la détection d’attaques inconnues. Les
auteurs jugent aussi que cette démarche diffère suffisamment des démarches statistiques
et à base de scénarios pour en faire une troisième catégorie : détection d’intrusions à
base de spécifications. Dans [25], on trouve une spécification complète de ftpd faite avec
BSML. BSML peut donc servir non-seulement au niveau réseau, mais aussi au niveau
Chapitre 1. Systèmes de détection d’intrusions 35

des applications s’exécutant sur un système donné.

Les actions effectuées lors de la reconnaissance de patrons peuvent dépasser le simple


archivage : il peut aussi s’agir d’instructions visant à protéger le système qui se fait
attaquer. BSML permet ainsi non-seulement une détection d’intrusions en temps réel,
mais aussi une prévention d’intrusion.

En résumé, BSML est un langage simple et concis basé sur un formalisme cou-
ramment utilisé par les informaticiens : celui des expressions régulières. Ce langage est
compilé vers un autre formalisme qui le rend comparable à STATL et à IDIOT : ce-
lui des machines à état étendues. Il dispose de primitives permettant d’exprimer des
contraintes temps-réel posées sur les événements et de relier ceux-ci entre eux par la
valeur de leurs attributs. Parmi les trois langages étudiés dans cette section, il est celui
qui se rapproche le plus d’un langage déclaratif car les actions posées lors de la recon-
naissance de patrons n’influencent pas la reconnaissance ultérieure d’autres patrons.
Cependant, les actions posées par les compteurs indiquent qu’ils jouent eux aussi un
rôle important dans l’expressivité du langage et que les expressions régulières à elles
seules ne sont pas suffisantes pour exprimer les propriétés voulues. Il s’agit cependant
d’un bon compromis entre expressivité et complexité algorithmique.

1.4 Systèmes experts

Un système expert est un logiciel capable d’accumuler et d’inférer de la connais-


sance. De plus, les systèmes experts sont souvent appelés à prendre des décisions. Un
exemple célèbre de système expert est celui de MYCIN, mis au point dans les années 70
afin d’aider les médecins à poser des diagnostics sur les maladies infectieuses du sang.
Le système demande au médecin quels sont les symptômes de la maladie, et pose un
diagnostique à l’aide d’une base de règles ayant préalablement été établie par d’autres
médecins. L’objectif d’un système expert est de simuler le raisonnement d’un expert
dans un domaine particulier de connaissance. Peu importe le domaine, l’hypothèse de
base faite sur les raisonnements effectués par les experts est que ceux-ci sont générale-
ment de la forme SI. . . ALORS. . . . Par exemple : SI il y a de la fumée ALORS il y a
du feu. Cette règle, ajoutée à la règle SI il y a du feu ALORS on doit sonner l’alarme
d’incendie, permet au système expert de recommander de sonner l’alarme d’incendie
lorsqu’il y a de la fumée.

Les systèmes experts en détection d’intrusions sont intéressants pour deux raisons :
leur capacité d’accumuler et d’inférer de la connaissance permet une détection plus pré-
Chapitre 1. Systèmes de détection d’intrusions 36

cise, et leur capacité à prendre des décisions permet de réagir aux attaques identifiées de
façon automatisée. Dans cette section, nous donnerons deux exemples de systèmes ex-
perts employés en détection d’intrusions : P-BEST et Lambda. P-BEST est un système
expert ayant été employé pendant plusieurs années dans le domaine de la recherche
et de la détection d’intrusions, alors que LAMBDA a d’abord été conçu comme un
langage ayant pour objectif de permettre aux experts du domaine de transcrire leurs
connaissances dans un langage simple.

1.4.1 P-BEST

P-BEST (Production-Based Expert System Toolset) [26, 27, 28] est en fait un en-
semble d’outils permettant de développer un système expert. Il ne s’agit pas d’un sys-
tèmes expert au sens propre du terme car, en théorie, un systèmes expert comporte
trois composantes principales : un moteur d’inférence, un moteur de décision et une
base de connaissance (qui elle même se divise en faits et en règles d’inférences). P-
BEST ne comporte que les deux premières composantes, qui d’un point de vue pratique
ne forment qu’une seule et même composante. Par abus de langage, nous dirons tout
de même que P-BEST est un système expert.

D’un point de vue algorithmique, P-BEST fonctionne en chaı̂nage avant. Ceci signifie
que, à chaque ajout d’un nouveau fait, l’ensemble des règles d’inférence est parcouru et
que toute l’information déductible de ce fait est ajoutée à la base de connaissance. La
base de faits contient donc à la fois les faits de base et les faits calculés. Cette philosophie
s’oppose au chaı̂nage arrière, qui consiste à emmagasiner seulement les faits de base, et à
inférer sur demande. En détection d’intrusions, où chaque événement signalé correspond
à un fait, et où les conclusions critiques doivent être tirées aussitôt que possible, le
chaı̂nage avant s’impose donc comme la voie à suivre. De plus, le chaı̂nage avant permet
à P-BEST de conserver un minimum d’information en ne conservant dans sa base de
faits que ceux qui ont été déduits et en éliminant ceux correspondant aux événements.

P-BEST fournit un langage compilé en C qui offre la possibilité de faire appel à des
routines programmées dans ce langage, conservant ainsi une certaine simplicité tout en
permettant un maximum de souplesse. Un exemple de règle d’inférence utilisée par P-
BEST se trouve à la figure 1.17. Aux ligne 1 à 3, on voit comment le langage fourni par
P-BEST permet de déclarer des types d’événements. Les paramètres #10 ;* indiquent
le niveau de priorité de la règle et le fait que la même règle peut être appliquée plusieurs
fois à un même événement. Les niveaux de priorité permettent de spécifier que certaines
règles doivent être exécutées avant d’autres. L’opérateur *, indiquant que la règle peut
être rappelée plusieurs fois, peut causer des boucles infinies à l’exécution du système
Chapitre 1. Systèmes de détection d’intrusions 37

01. ptype[event event_type :int, return_code :int,


02. username :string, hostname :string]
03. ptype[bad_login username :string, hostname :string]
04.
05. rule[Bad_Login(#10 ;*) :
06. [+e :event| event_type == login, return_code == BAD_PASSWORD]
07. ==>
08. [+bad_login| username = e.username, hostname = e.hostname]
09. [-|e]
10. [ !|printf("Bad login for user %s from host %s\n",
11. e.username, e.hostname)]
12. ]

Fig. 1.17 – Exemple de règle d’inférence dans P-BEST.

s’il n’est pas utilisé correctement. À chaque fois que cet opérateur est utilisé, on doit
s’assurer que l’événement sera détruit par au moins une des règles. L’opérateur -, utilisé
à la ligne 9, permet justement d’effectuer cette tâche. Il permet de retirer des faits de la
base de connaissances ; en particulier, il permet aussi de retirer des événements. Chaque
événement doit normalement être soigneusement effacé de la base de connaissance afin
d’éviter un engorgement de la mémoire utilisée. On doit aussi s’assurer que la règle
supprimant l’événement ait la priorité la plus basse, afin d’éviter que celui-ci ne soit
supprimé avant que toutes les règles en ayant besoin aient pu l’utiliser. Finalement,
l’opérateur + doit être vu comme un quantificateur existentiel, l’opérateur ! permet de
faire un appel à une routine externe, et l’opérateur ==> sépare la prémisse des conclu-
sions. En conclusion, la règle de la figure 1.17 est donc activée par les événements de
type login ayant un code de retour égal à la constante BAD_PASSWORD. Lorsqu’un tel
événement survient, celui-ci est détruit (ligne 9) après avoir créé un nouvel événement
de type bad_login (ligne 8), et un message, indiquant qu’une tentative de connexion a
échoué (ligne 10), est affiché à l’aide de la fonction externe printf.

D’autres opérateurs sont aussi disponibles dans le langage de P-BEST. Par exemple,
il existe des opérateurs permettant d’activer ou de désactiver dynamiquement des règles
d’inférence. P-BEST peut donc prendre des décisions à propos de son propre fonction-
nement. Le principal intérêt de cette fonctionnalité est de permettre une forme d’opti-
misation en désactivant des règles jugées temporairement non pertinentes. Il est aussi
possible, afin de simplifier l’écriture de certaines règles, de modifier les champs de cer-
tains faits. Ceci permet d’émuler la notion de variable, facilitant ainsi, par exemple, la
définition de compteurs. Finalement, le langage fournit des opérateurs permettant de
marquer ou de démarquer des événements, permettant ainsi à certaines règles de laisser
des traces qui peuvent être utilisées par d’autres règles.

En résumé, le système P-BEST permet de développer des systèmes experts dans


Chapitre 1. Systèmes de détection d’intrusions 38

¯ ¯ ¯ ¯ ¯ ¯
e := a ¯ 0 ¯ e1 ; e2 ¯ e1 |e2 ¯ e ¯ e1 ?e2 ¯ e1 &e2

Tab. 1.4 – Syntaxe du calcul d’événements utilisé dans LAMBDA.

un langage compilé qui permet une interopérabilité simple et efficace avec d’autres lan-
gages, et par conséquent d’autres systèmes. D’un point de vue abstrait, les fonctionna-
lités de base d’un système expert, soient l’accumulation et l’inférence de connaissance,
de même que l’aide à la prise de décision, sont tout à fait souhaitables en détection
d’intrusions et P-BEST les implémente très bien. Cependant, le langage de P-BEST
n’est peut-être pas des plus appropriés pour la gestion de flots d’événements. En effet,
l’association fait-événement respecte bien le paradigme des systèmes experts, mais cer-
tains aspects du langage et de l’implémentation dépassent le cadre des systèmes experts
et demandent une connaissance des algorithmes utilisés en arrière-plan. Par exemple,
le chaı̂nage avant force l’utilisateur à s’assurer de donner les bons niveaux priorité aux
différentes règles. De plus, la suppression des événements qu’il n’est plus nécessaire de
conserver en mémoire doit être faite explicitement, ce qui complique (inutilement) la
rédaction de règles. Finalement, les notions de règles répétables, de marquage d’événe-
ments, et d’activation/désactivation de règles donnent à P-BEST des possibilités pra-
tiquement algorithmiques qui dépassent le cadre strict des systèmes experts. P-BEST
nous convainc donc que les caractéristiques principales d’un système expert sont sou-
haitables en détection d’intrusions, mais l’implémentation suggère que le paradigme pur
des systèmes experts semble n’être pas parfaitement adapté à la tâche.

1.4.2 LAMBDA

LAMBDA est un langage abstrait permettant de décrire des scénarios d’attaque sans
se soucier des détails de détection. La description qui en est donnée dans [29] est détachée
des détails d’implémentation, et repose sur l’hypothèse que d’autres outils d’audit (dont
des systèmes de détection d’intrusions) fournissent un flux d’événements bas niveau à
analyser. LAMBDA n’est pas décrit par ses auteurs comme un langage de spécification
de système expert, mais comporte tout de même les trois caractéristiques que nous
avons identifiées comme étant désirables d’un système expert, soient l’accumulation
et l’inférence de connaissance, de même que la possibilité de prendre des décisions et
de réagir automatiquement. De plus, comme LAMBDA est spécialement conçu pour la
description d’attaques, il comporte des primitives spécialement reliées à ce domaine. Par
exemple, il permet de définir un scénario d’attaque de deux points de vue différents :
Chapitre 1. Systèmes de détection d’intrusions 39

attack attack name(arg1 , arg2 , . . .)


pre : φpre
post : φpost
scenario : ²s
where : ψs
detection : ²d
where : ψd
verification : ²v
where : ψv
où φi est une formule de la logique du deuxième ordre
ψi est une formule de la logique du premier ordre
²i est une formule du calcul des événements

Tab. 1.5 – Syntaxe d’un scénario d’attaque LAMBDA.

celui de l’attaquant et celui du système de détection. Bien que les deux points de vue
soient en général très semblables, il arrive cependant que certaines actions effectuées
par l’attaquant puissent ne pas être détectables par le système attaqué. Par exemple, il
peut s’agir de certains calculs effectués à l’interne.

Le langage LAMBDA offre une solution hybride pour exprimer les scénarios d’at-
taque, et repose sur trois langages différents, dépendamment du point de vue auquel on
se place. Pour représenter la connaissance acquise, on utilise la logique du deuxième
ordre. On utilise des prédicats du deuxième ordre pour modéliser, par exemple, la
connaissance de l’attaquant : le prédicat knows(A, φ) exprime le fait que l’attaquant
A acquiert la connaissance φ. Pour représenter les contraintes d’ordonnancement de-
vant être respectées par les différentes étapes d’un scénario d’attaque, on utilise le
calcul d’événements [30], dont la syntaxe est donnée à la table 1.4. Un événement peut
être soit un événement primitif (a), soit l’événement nul (0), soit une séquence d’évé-
nements (e1 ; e2 ), soit l’exécution parallèle de deux événements (e1 |e2 ), soit l’absence
d’événements (e), soit le choix non-déterministe d’événements (e1 ?e2 ), soit l’exécution
synchronisée d’événements (e1 &e2 ). L’action nulle, combinée au choix non-déterministe,
def
permet de représenter un événement facultatif [e] = e ? 0. Il est à noter que l’événe-
ment primitif a est défini sur un intervalle de temps [t1 , t2 ], et non comme étant un
événement ponctuel. Pour les événements ponctuels survenant à l’instant t, l’intervalle
est [t, t]. Finalement, pour exprimer les contraintes autres que l’ordonnancement de-
vant être respectées par les différents événements du scénario, on utilise simplement la
logique du premier ordre.

La syntaxe d’un scénario d’attaque exprimé en LAMBDA se trouve à la table 1.5.


Chapitre 1. Systèmes de détection d’intrusions 40

Action touch(Agent,File) Action block(Agent,Printer)


Pre : true Pre : printer(Printer),
Post : file(File), owner(Agent,File) physical access(Agent,Printer)
Post : blocked(Printer)
Action lpr-s(Agent,Printer,File) Action remove(Agent,File)
Pre : printer(Printer), file(File), Pre : owner(Agent, File)
authorized(Agent,read,File) Post : not(file(File))
Post : queued(File,Printer)
Action ln-s(Agent,Link,File) Action unblock(Agent,Printer)
Pre : not(file(Link)) Pre : printer(Printer), blocked(Printer),
Post : linked(Link,File) physical access(Agent,Printer)
Post : not(blocked(Printer))
Action print-process(Printer,Link) Action get-file(Agent,File)
Pre : queued(Link,Printer), Pre : printed(Printer,File),
linked(Link,File), physical access(Agent,Printer)
not(blocked(Printer)) Post : read access(Agent,File)
Post : printed(Printer,File),
not(queued(Link,Printer))

Fig. 1.18 – Modélisation du scénario d’accès illégal à un fichier avec LAMBDA.

Le lien avec la base de connaissances se fait via les clauses pre et post. La clause pre
exprime les conditions devant être satisfaites par le système avant l’exécution du scénario
afin que celui-ci puisse réussir. La clause post exprime l’état du système une fois que
l’exécution du scénario a réussi. Les clauses scenario et detection donnent les détails
de l’attaque du point de vue de l’attaquant et de l’attaqué. La clause verification,
quand à elle, indique comment valider l’effet de l’attaque. Les auteurs parlent aussi
d’ajouter une clause reaction, permettant d’indiquer comment réagir à l’attaque pour
se protéger. Finalement, les clauses where donnent les relations entre les diverses étapes
des scénarios.

Les notions de post et de pre-conditions permettent, en plus de valider l’occurrence


d’attaques, de corréler les différents scénarios entre eux. Deux scénarios sont corrélés si
les post-conditions de l’un correspondent aux pre-conditions de l’autre. Cette notion de
corrélation permet d’une part de factoriser les scénarios d’attaques lorsqu’un objectif
intermédiaire peut être atteint de différentes façons, et d’autre part de découvrir des
scénarios d’attaques insoupçonnés.

Nous n’avons pas trouvé, dans la littérature, d’exemple de scénario d’attaque ex-
primé avec LAMBDA utilisant le calcul des événements. Tous les exemples sur lesquels
nous avons pu mettre la main n’utilisaient, dans la partie scenario, que des actions
simples. C’est le cas, entre autres, du scénario d’accès illégal à un fichier, présenté à la
figure 1.18. Ce scénario se déroule en huit étapes. Premièrement (1), l’attaquant crée
un fichier auquel il aura alors les droits d’accès. Ensuite (2), l’attaquant ayant un accès
physique à une imprimante bloque celle-ci, par exemple en retirant le bac de papier. Il
lance alors la commande d’impression du fichier créé (3), et comme il a les droits d’accès
Chapitre 1. Systèmes de détection d’intrusions 41

pour ce fichier, la commande est placée dans la file d’impression. L’impression ne peut
avoir lieu immédiatement, car l’imprimante est bloquée. Les prochaines étapes sont la
suppression du fichier pour lequel une commande d’impression a été lancée (4), et la
création d’un lien logique vers le fichier cible ayant le même nom que le fichier supprimé
(5). Il ne reste ensuite qu’à débloquer l’imprimante (6), pour que l’impression du fichier
puisse avoir lieu (7), donnant ainsi accès à l’attaquant au fichier auquel il n’avait pas
droit (8).

En résumé, LAMBDA est un langage qui offre les mêmes avantages qu’un para-
digme basé sur un système expert : soient l’accumulation et l’inférence d’information,
de même qu’un support à la décision. De plus, l’approche hybride utilisant le calcul
d’événements permet d’exprimer des scénarios d’attaque selon une syntaxe claire et
concise qui semble au moins aussi satisfaisante sinon plus que celles employées avec
les systèmes de transition. Des aspects intéressants de LAMBDA sont la séparation du
point de vue de l’attaquant et du point de vue de l’attaqué, de même que les notions de
vérification et de réaction. Aussi, la description logique des effets et des prémisses d’une
attaque permet de faire un calcul de scénario d’attaque afin d’élaborer de nouveaux
scénarios.

1.5 Logiques temporelles

Les trois derniers travaux que nous présentons, LogWeaver, Monid, et Chronicles,
sont ceux qui se rapprochent le plus de ceux que nous avons effectués de par le fait que
les langages de spécification utilisés ont été développés à partir de logiques temporelles.
LogWeaver utilise une logique temporelle future du premier ordre, alors que Monid
utilise une logique temporelle passée et future du premier ordre avec points fixes nommée
Eagle et que Chronicles se base sur une logique réifiée. Dans les trois cas, les algorithmes
utilisés ont été conçus de façon à pouvoir travailler en ligne (online). Grossièrement,
un algorithme est dit en ligne s’il peut traiter séquentiellement sur un fichier d’audit
à partir du début sans avoir à mémoriser tout le fichier. Un exemple d’algorithme qui
n’est pas en ligne est celui décrit dans [31], où le traitement s’effectue à partir de la fin
du fichier. Un tel algorithme est appelé, dans la littérature anglaise, offline.
Chapitre 1. Systèmes de détection d’intrusions 42

F ::= A | ¬F | F1 ∧ F2 | F1 ∨ F2 | ¦F

Tab. 1.6 – Syntaxe de la première logique de LogWeaver.

1.5.1 LogWeaver

L’article sur LogWeaver [32] est divisé en deux parties. Premièrement, les auteurs
montrent comment on peut utiliser une logique temporelle classique pour effectuer de
la détection d’intrusions sur des fichiers d’audits. Ils montrent ensuite les faiblesses de
cette logique et en proposent une autre moins orthodoxe, mais qu’ils considèrent comme
plus appropriée. Nous focuserons ici sur la première logique, car elle est plus proche de
celles que nous avons déjà vues et elle nous permet de voir comment on peut effectuer
de la vérification sur une logique du premier ordre.

Le modèle sur lequel travaille LogWeaver est une séquence finie ou infinie d’enre-
gistrements, appelé indistinctement log, ou trace. Un enregistrement est défini comme
étant une fonction d’un ensemble d’étiquettes vers un ensemble de valeurs, lesquelles
sont considérées comme étant des chaı̂nes de caractères.

La syntaxe des formules utilisées dans la première logique abordée est présentée à la
table 1.6. À première vue, il s’agit d’un sous-ensemble de LTL, que nous étudierons plus
en détails à la section 2.1.1. Les opérateurs U (jusqu’à ce que) et ° (immédiatement
après) ont été omis car, selon les auteurs, ces derniers ne se révèlent pas d’une grande
utilité pour la détection d’intrusions. Le seul opérateur temporel ayant été conservé est
donc l’opérateur ¦, signifiant nécessairement. Il est à noter que l’opérateur ¦ utilisé ici
exprime un futur strict. La principale différence entre cette logique et LTL se trouve au
niveau du type de la relation de satisfaction et de la sémantique attribuée aux formules
atomiques A, aussi appelées patrons d’événements. Un exemple de patron d’événements
est {id=X, action="creat", object="/usr/spool/mail/root$"}. Ce patron filtre
tous les événements dont la valeur du champ id est égale à celle de la variable X, et
dont les valeurs des champs action et object sont respectivement égales à "creat" et
"/usr/spool/mail/root$".

Étant donné une trace σ et une formule F , l’ensemble des éléments satisfaisant F
est alors défini non-pas seulement comme étant un sous-ensemble d’enregistrements de
σ, mais comme un ensemble de couples (s, ρ), où s est un enregistrement de σ et ρ
est un environnement. Un environnement est une fonction attribuant des valeurs aux
Chapitre 1. Systèmes de détection d’intrusions 43

variables se trouvant dans les patrons d’événements d’une formule. C’est exactement
cette notion d’environnement qui justifie l’emploi d’une logique du premier ordre. Ce
sont les environnements qui permettent de relier les divers enregistrements entre eux en
unifiant les valeurs de certains de leurs champs.

L’algorithme de vérification utilisé pour cette logique est semblable à celui utilisé
pour RUSSEL (section 1.2.1). On travaille cependant avec deux ensembles plutôt que
trois, l’ensemble Cmp n’ayant aucun sens ici. Pour vérifier une formule F , on initialise
donc l’ensemble Cur à {F } et l’ensemble N xt à ∅. Ensuite, pour chaque enregistrement,
on traite chacune des formules de l’ensemble Cur. Ce traitement peut soit amener à
ajouter d’autres formules dans l’ensemble Cur (dans le cas de formules de la forme
F1 ∧ F2 , F1 ∨ F2 , ou ¬F ), soit à ajouter des formules dans l’ensemble N xt (dans le
cas des formules de la forme ¦F ). Il est à noter que dans le dernier cas, on utilise
l’équivalence ¦F ⇔ °F ∨ ° ¦ F , signifiant que formule F sera satisfaite dans le futur
si et seulement si elle sera satisfaite à l’état suivant ou si, à partir de l’état suivant, elle
sera satisfaite dans le futur. En plus de manipuler ces ensembles, il faut aussi ajouter
les structures de données nécessaires au chaı̂nage des formules et à la manipulation des
environnements. Le lecteur intéressé aux détails de cet algorithme est référé à [32].

Les problèmes reliés à cette logique se divisent en deux catégories. La première


catégorie de problèmes concerne le traitement des événements répétés, c’est-à-dire la
reconnaissance d’un nombre déterminé d’événements semblables. En plus de ralentir
considérablement l’algorithme de vérification en augmentant rapidement et de façon
démesurée le nombre de formules à vérifier, le nombre d’alarmes lancées est lui aussi
beaucoup trop grand. Ceci est du au fait que la reconnaissance d’une suite de n évé-
nements sur une trace en contenant m lance (m n ) alarmes, alors qu’en fait une seule
pourrait suffire.

La deuxième catégorie de problèmes concerne l’expressivité de la logique, qui est


jugée à la fois trop et pas assez expressive. Elle est trop expressive car elle permet
la combinaison booléenne de formules de la forme ¦F , qui sont jugées par les auteurs
comme étant rarement utiles en pratique. De plus, la présence de négations est jugée
comme étant superflue dans la plupart des cas. Ces deux familles de formules, de plus,
contribuent chacune à leur façon à la complication de l’algorithme de vérification. Elle
n’est pas suffisamment expressive car elle ne permet pas de spécifier naturellement des
propriétés de comptage (autrement qu’en écrivant des formules de la forme F ∧ ¦(F ∧
¦(F ∧ . . . ∧ ¦F ))), ni de parité. Les propriétés de parité sont celles où une action peut
annuler temporairement l’effet d’une autre, comme dans le cas de l’ouverture et de la
fermeture d’une session TCP.
Chapitre 1. Systèmes de détection d’intrusions 44

Pour résoudre ces problèmes, les auteurs suggèrent d’utiliser une autre logique, moins
classique, inspirée de ETL [33]. Nous ne nous étendrons pas ici sur les détails de ETL,
mais nous nous contenterons de dire que cette dernière a la particularité d’offrir la
possibilité d’inclure des automates dans la définition des formules. Une partie de la
sémantique des formules est donc définie par la notion d’acceptation d’une chaı̂ne par
un automate. Cette solution hybride présente des avantages au niveau de l’expressi-
vité, mais les spécifications obtenues, comme le remarquent les auteurs, commencent à
ressembler étrangement à celles de STATL.

En résumé, le travail de M. Roger et J. Goubault-Larrecq est une mine d’or d’un


point de vue théorique. L’article [32] comportent plusieurs résultats théoriques qui n’ont
pas été cités ici, mais qui sont tout de même très intéressants. De plus, il représente un
cas complet d’étude d’utilisation d’une logique temporelle classique du premier ordre
pour résoudre un problème de vérification en ligne. Les algorithmes utilisés sont pré-
sentés en détails et les résultats obtenus sont commentés avec objectivité. Plusieurs
problèmes importants reliés à l’utilisation d’une logique classique ont été relevés, et une
solution intéressante a été proposée pour résoudre ces problèmes. Cependant, comme
nous l’avons déjà remarqué, cette solution nous fait tendre tranquillement vers un lan-
gage à tendance impérative.

1.5.2 Monid

Le système de vérification en ligne Monid se présente comme étant une alternative


à LogWeaver. Ce système utilise une logique temporelle du premier ordre passée et
future avec points fixes nommée Eagle [34, 35]. Les auteurs affirment que Eagle est plus
expressive que le fragment de LTL utilisé par les auteurs de LogWeaver en ce qu’elle
permet d’exprimer des contraintes temps-réel et des propriétés statistiques.

La syntaxe des spécifications Eagle se trouve à la table 1.7. Il s’agit d’une syntaxe
plutôt concrète, puisqu’on y spécifie plus que la syntaxe des formules. Une spécification
S consiste en une partie de déclarations D et une partie où on définit des observateurs O.
D est constitué de zéro ou plus définitions de règles R, et O de zéro ou plus moniteurs M .
Les règles et les moniteurs sont nommés N . Le nommage des règles permet la définition
de règles récursives. Le domaine lexical T représente les types, qui sont utilisés lors de
la définition des règles. Finalement, xi représente une variable, et exp est une expression
qui s’évalue soit à vrai soit à faux à chacun des états de la trace.
J
Les opérateurs °, , désignent respectivement l’événement suivant et l’événement
précédent, alors que les opérateurs de points fixes min et max permettent de définir des
Chapitre 1. Systèmes de détection d’intrusions 45

S ::= DO
D ::= R∗
O ::= M∗
R ::= {max|min} N (T1 x1 , . . . , Tn xn ) = F
M ::= mon N = F
T ::= Form | primitive type
F ::= exp | true | false | ¬F | F1 ∧ F2 | F1 ∨ F2 | F1 → F2
J
| °F | F | F1 · F2 | N (F1 , . . . , Fn ) | xi

Tab. 1.7 – Syntaxe des spécifications Eagle.

max Always(Form F) = F ∧ °Always(F)


min EvTimedLogout(string k, double t, double δ) = (time - t ≤ δ)
∧ ((action = logout ∧ userid = k) ∨ °EvTimedLogout(k,t,δ))
mon M = Always(action = login → EvTimedLogout(userid,time,100))

Fig. 1.19 – Exemple de spécification Eagle.

formules récursivement. L’opérateur de concaténation · permet de définir des séquences


d’événements. Notons aussi que bien que Eagle utilise des variables dans la définition de
ses formules, aucune notion d’environnement n’est utilisée pour définir la sémantique.
On utilise cependant une notion de substitution textuelle pour définir la sémantique
des formules paramétrées N (F1 , . . . , Fn ).

À la figure 1.19, se trouve un exemple de spécification Eagle. Comme d’habitude, on


peut définir l’opérateur Always, souvent noté 2, à l’aide de l’opérateur de plus grand
point fixe et de l’opérateur °. La définition de la règle EvTimedLogout montre comment
on peut spécifier qu’un événement doit survenir au plus δ unités de temps après un autre.
On procède exactement de la même façon que lorsque l’on définit l’opérateur ¦ à l’aide
des opérateurs de plus petit point fixe et °, sauf que l’on exige en plus que les temps
associés aux deux événements ne soient pas séparés de plus de δ unités de temps. Aucun
caractère spécial n’est donc donné au temps dans Eagle.

L’algorithme de vérification utilisé par Eagle Flier, le logiciel implantant Eagle,


fonctionne à l’aide d’une fonction eval qui, étant donné une formule F et un état σ(i),
donne une nouvelle formule F 0 telle que σ, i |= F si et seulement si σ, i + 1 |= F 0 . À
Chapitre 1. Systèmes de détection d’intrusions 46

la fin de la trace, on calcule une fonction booléenne value(F ) qui s’évalue à vrai si et
seulement si le dernier état de σ satisfait F . Donc, pour une trace σ de longueur n et une
formule F , on a σ, 1 |= F ssi value(eval(. . . eval(eval(F, σ(1)), σ(2)) . . . , σ(n))) = vrai.

On remarque ici une différence importante entre les hypothèses faites sur le modèle
utilisé par LogWeaver et celui de Eagle : les traces vérifiées par Eagle sont de longueur
finie, alors que celles sur lesquelles travaille LogWeaver sont définies comme étant finies
ou infinies. Il est à noter que cette différence reste tout de même très mince car, d’une
part, les résultats théoriques sur l’algorithme employé par LogWeaver demandent une
trace finie, et d’autre part, les auteurs de [35] citent une méthode, appelée specifying-
bad prefixes [36], permettant d’exprimer la formule à vérifier de telle sorte que celle-ci
devient tôt ou tard vraie ou fausse, peu importe le reste de la trace. L’intérêt de cette
technique réside dans le fait qu’elle évite d’avoir à vérifier la satisfiabilité éventuelle
d’une formule, problème qui est indécidable dans le cas de Eagle dû à l’utilisation de
prédicats du premier ordre.

En résumé, les travaux effectués dans le cadre du développement de Eagle sont plus
près de la pratique que ceux effectués pour LogWeaver. Les propriétés intéressantes pour
la détection d’intrusions telles que le comptage ou les propriétés de parité s’expriment
beaucoup plus naturellement dans Eagle que dans le sous-ensemble de LTL considéré
par les auteurs de LogWeaver ou encore la seconde logique proposée, inspirée de ETL.
Les algorithmes de vérification utilisés par Eagle sont simples, mais semblent mieux
adaptés au cas où la trace d’événements considérée est de longueur finie. Finalement,
on trouve dans [35] toute une série d’exemples de scénarios d’attaque allant des plus
classiques à d’autres plus originaux pouvant s’exprimer dans Eagle.

1.5.3 Chronicles

Les deux systèmes que nous venons de présenter ont été construits sur des para-
digmes provenant de la théorie de la vérification formelle de modèles. Ils utilisent des
logiques qui ont été historiquement développées dans l’intention de vérifier automati-
quement certaines propriétés de programmes. La vérification par évaluation de modèle
Model Checking en informatique peut être utile à plusieurs phases du développement,
tant à la conception (vérification d’algorithmes), à la programmation (vérification de
code), qu’à la phase de tests (validation de logiciels). Le système Eagle, en particulier, a
d’abord été développé pour ce type d’usage. L’avantage d’utiliser une logique commune
dans un tel cadre d’utilisation est qu’à chaque phase du processus de développement,
on peut vérifier à nouveau les mêmes propriétés avec un minimum d’ajustement. La
vérification par évaluation de modèle est très bien adapté aux problèmes booléens :
Chapitre 1. Systèmes de détection d’intrusions 47

est-ce que, oui ou non, telle propriété est satisfaite ?. Les problèmes du genre : dans
quelle mesure cette propriété est-elle satisfaite ? ou encore quelles sont toutes les façons
dont cette propriété est violée ? présentent peu d’intérêt dans ce domaine et c’est sans
doute pourquoi les tentatives d’y appliquer les logiques y ayant été développées peuvent
parfois sembler artificielles et tordues.

En intelligence artificielle, les problèmes auxquels on s’attaque sont traditionnelle-


ment différents. La nature des problèmes abordés est beaucoup plus vaste, et souvent
plutôt éloignée de ceux généralement rencontrés par un informaticien. Par exemple, on
pourra être intéressé à analyser les battements cardiaques d’un être humain. Ici, on a
un exemple intéressant où tout ce qui compte est la fréquence des événements, ceux-ci
étant indistinctibles les uns des autres. On sera intéressé au nombre de battements dans
une minute, à l’accélération du rythme, aux moments où de légères anomalies semblent
survenir, etc. Dans un tel contexte, le besoin de logiques où le comptage et les intervalles
de temps sont élevés au rang de primitives du langage se fait bien sentir, et c’est dans
un tel contexte que les logiques réifiées ont été développées. Les logiques réifiées sont des
logiques du deuxième ordre où les prédicats du deuxième ordre servent essentiellement
à exprimer les instants ou les intervalles de temps où les faits exprimés par les prédicats
du premier ordre sont vrais.

Chronicles, introduit dans [37], est un langage basé sur les logiques réifiées permet-
tant de reconnaı̂tre des chroniques dans un flot d’événements. Il vient avec CRS (Chro-
nicles Recognition System) un vérificateur qui peut fonctionner en ligne. Informellement,
une chronique est un ensemble d’événements reliés entre eux par des contraintes tempo-
relles. Dans la littérature, les chroniques sont reliées au calcul des événements présenté
plus haut.

La représentation du temps dans Chronicles est discrète et il est donc vu comme une
suite ordonnée d’instants dont la résolution est assez fine pour les besoins de l’analyse.
Deux événements différents peuvent survenir au même instant, mais lorsque deux ou
plusieurs événements identiques surviennent au même instant (selon la résolution choi-
sie), ceux-ci sont fusionnés en un seul événement. En détection d’intrusions, où plusieurs
événements identiques peuvent survenir en peu de temps, le choix de la résolution doit
donc être fait avec précautions.

L’environnement est décrit par les attributs du domaine. Un attribut du domaine est
un tuple P (a1 , . . . , an ) : v, où P est le nom de l’attribut, a1 , . . . , an ses arguments, et v
sa valeur. Certains attributs peuvent ne pas avoir de valeur. Ces derniers sont appelés
messages. Finalement, les événements correspondent à des changements de valeurs des
attributs du domaine.
Chapitre 1. Systèmes de détection d’intrusions 48

hold(P : v; (t1 ; t2 ))
event(P : (v1 ; v2 ); t)
event(P ; t)
noevent(P ; (t1 ; t2 ))
occurs((n1 ; n2 ); P ; (t1 ; t2 ))

Tab. 1.8 – Prédicats de réification de Chronicles.

1. chronicle exemple1 {
2. event(e1,t1) ;
3. event(e2,t2) ;
4. event(e3,t3) ;
5.
6. t1<t2<t3
7. t3-t2 <= 4
8. }

Fig. 1.20 – Exemple de chronique.

Les prédicats de réification de Chronicles sont présentés à la table 1.8. Le prédicat


hold(P : v; (t1 ; t2 )) signifie que l’attribut P conserve la valeur v sur l’intervalle [t1 , t2 [.
Le prédicat event(P : (v1 ; v2 ); t) signifie que l’attribut P a passé de la valeur v1 à v2 à
l’instant t. Le prédicat event(P ; t) signifie que le message P est survenu à l’instant t. Le
prédicat noevent(P ; (t1 ; t2 )) signifie qu’aucun changement de valeur de l’attribut P n’est
survenu durant l’intervalle [t1 , t2 [. Finalement, le prédicat occurs((n1 ; n2 ); P ; (t1 ; t2 ))
permet de compter. Il signifie que sur l’intervalle [t1 , t2[, l’événement P est survenu de
n1 à n2 fois.

Un modèle de chronique est constitué de cinq éléments : i) un ensemble d’instants,


ii) un ensemble de contraintes temporelles sur ces instants, iii) un ensemble de pa-
trons d’événements qui représentent les changements de l’environnement auxquels on
s’intéresse iv) un ensemble d’assertions qui représentent le contexte dans lequel les évé-
nements surviennent, et v) un ensemble d’actions externes à prendre lorsqu’une instance
de chronique est reconnue.

Un exemple de modèle de chronique est présenté à la figure 1.20. Il sera reconnu à


chaque fois que les messages e1 , e2 et e3 surviendront dans cet ordre et que, de plus, le
message e3 ne doit pas survenir plus de 4 unités de temps après e2 .

L’algorithme de reconnaissance des chroniques fonctionne de façon semblable à


d’autres que nous avons déjà vus. Au début, un modèle vide est créé. À chaque fois
Chapitre 1. Systèmes de détection d’intrusions 49

qu’un événement satisfaisant les contraintes exprimées par un modèle de chronique sur-
vient, celui-ci est ajouté à l’instance de chronique en cours après duplication de cette
dernière. La duplication sert à s’assurer de reconnaı̂tre toutes les instances possible.
Certaines instances en cours de reconnaissance peuvent être éliminées lorsque des as-
sertions sont violées ou que les contraintes temporelles ne peuvent plus être satisfaites.
Il est important, lors de la spécification de chroniques, de s’assurer de spécifier de telles
assertions ou contraintes temporelles. Autrement, et particulièrement dans un mode de
fonctionnement en ligne, certaines instances peuvent rester indéfiniment en mémoire et
même, éventuellement, faire exploser celle-ci.

Un aspect important de Chronicles est que l’action à prendre lors de la reconnais-


sance d’une chronique n’est pas nécessairement une simple action d’archivage externe. Il
peut aussi s’agir de l’émission d’un nouvel événement, qui peut alors être utilisé soit par
une autre chronique en cours de reconnaissance, soit par d’autres instances de la même
chronique. Cette fonctionnalité donne à Chronicles une certaine forme d’acquisition de
connaissance, celle-ci pouvant être représentée par certains attributs du domaine dont
les valeurs sont tenues à jour par le mécanisme même de reconnaissance de chroniques.

Chronicles n’a pas été conçu spécialement en vue de faire de la détection d’intru-
sions. À ses débuts, il a été appliqué à l’analyse de journaux d’alarmes d’équipements
de télécommunications. Il a par la suite été appliqué à d’autres domaines tels que l’ana-
lyse de circulation routière et on lui a même trouvé quelques applications en médecine.
Dans [38], on a finalement suggéré une façon de l’utiliser en détection d’intrusions.
Bien qu’il soit possible d’utiliser Chronicles pour représenter des scénarios d’attaque,
l’utilisation proposée par les auteurs tend vers d’autres objectifs. Principalement, ils
proposent d’utiliser Chronicles comme outil d’analyse des journaux d’alarmes de sys-
tèmes de détection d’intrusions afin i) de permettre une détection plus précise, ii) de
réduire le nombre d’alarmes et iii) d’améliorer la sémantique des alarmes.

L’utilisation de Chronicles peut permettre une détection plus précise. Plus préci-
sément, elle peut aider à diminuer le nombre de faux-positifs en invalidant certaines
alarmes. L’invalidation d’alarmes avec Chronicles peut se faire en spécifiant certains
contextes dans lesquels certains comportement ne doivent pas être considérés comme
dangereux. Par exemple, l’initialisation d’une vidéo-conférence entre plusieurs utilisa-
teurs peut sous certains aspects ressembler à un balayage de ports, mais ne doit tout
de même pas déclencher d’alarme. L’utilisation de Chronicles peut réduire le nombre
d’alarmes, principalement en utilisant le prédicat occurs. Certaines attaques, telles que
la propagation de vers informatiques, se caractérisent par la répétition à outrance de
comportements identiques, et peuvent déclencher un nombre abrutissant et inutile-
ment élevé d’alarmes. Chronicles peut fusionner ces différents événements en un seul.
Chapitre 1. Systèmes de détection d’intrusions 50

Chronicles peut améliorer la sémantique des alarmes en combinant les différents symp-
tômes d’une même attaque. Mieux encore, certaines attaques ne doivent être considérées
comme réussies que si un ensemble bien déterminé d’actions ont été effectuées. Certains
systèmes de détection d’intrusions lanceront des alarmes à chacune de ces actions, don-
nant ainsi l’impression que de nombreuses attaques ont eu lieu alors qu’il s’agit en fait
d’une seule. L’utilisation de Chronicles permet de ne déclencher qu’une seule alarme
rendant ainsi mieux compte du fait qu’une seule attaque a eu lieu.

Les auteurs de [38], en plus de donner plusieurs exemples où Chronicles peut s’avérer
utile dans l’analyse de journaux d’alarme, montrent comment ce langage peut être
utilisé de paire avec M2D2 [10] pour corréler les alarmes avec le contexte. M2D2 est un
modèle formel incluant celui de NetSTAT [20]. Il permet de représenter non-seulement
la topologie physique du réseau, mais aussi toute sa configuration, les logiciels installés
sur chacune des machines, les vulnérabilités de ceux-ci de même que leurs effets, les
logiciels de sécurité installés sur chacune des composantes, les attaques détectables par
chacun des systèmes de détection d’intrusions, et bien encore. La façon dont les alarmes
sont représentées dans M2D2 sont de plus compatibles avec IDWG [39], un format
d’échange d’alarmes standardisé mis au point à l’IETF. L’utilisation d’un tel modèle
permet, en plus de corréler différentes alarmes entre elles, de tenir compte dans l’analyse
des caractéristiques du système attaqué et même du système de détection d’intrusions
ayant signalé l’attaque.

En résumé, Chronicles est un langage de surveillance basé sur une logique réifiée
dont la sémantique semble mieux adaptée à la détection d’intrusions que les logiques
traditionnellement utilisées en Model Checking. L’ordonnancement des différents évé-
nements est exprimé par un ensemble de contraintes sur les instants leur étant associés
plutôt que par des opérateurs dédiés. Les opérateurs spécifiques à Chronicles mettent
plutôt le focus sur les différents changements pouvant survenir pendant le processus
de reconnaissance d’une chronique. Un des ces opérateurs permet même de compter
de façon très naturelle le nombre d’événements semblables. Chronicles a de plus été
utilisé dans de nombreux domaines d’application à première vue assez différents les uns
des autres. Un des dangers reliés à l’utilisation de Chronicles est l’explosion de l’utili-
sation de la mémoire lorsque les chroniques ne sont pas spécifiées consciencieusement.
Finalement, des travaux ayant été effectués tendent à démontrer que Chronicles peut
s’avérer utile en corrélation d’alarmes. La notion de corrélation d’alarmes employée par
les auteurs de [38] est cependant légèrement différente de celle utilisée par les auteurs
de [29]. Les premiers utilisent le mot corrélation dans un sens plutôt général, désignant
par exemples différentes alarmes symptomatiques d’un même phénomène, alors que les
deuxièmes parlent plutôt d’une corrélation logique, voire même causale. Le vérificateur
de chroniques CRS permet de générer des événements en cours d’exécution. Cette fonc-
Chapitre 1. Systèmes de détection d’intrusions 51

tionnalité, jointe à l’utilisation du modèle M2D2, permet de bien mettre les attaques
signalées en contexte.

1.6 Conclusion

Dans ce chapitre, nous avons étudié 13 systèmes de détection d’intrusions différents,


dont 12 sont à base de scénarios. Les langages utilisés pour exprimer les scénarios re-
posent sur 5 catégories de paradigmes : les langages spécifiques au domaine, les langages
impératifs, les systèmes de transition, les systèmes experts, et les logiques temporelles.
Nous avons identifié, pour chacun de ces langages, des avantages et des inconvénients
qui leur sont propres. Nous donnons à la table 1.9 la liste des dix caractéristiques, parmi
celles que nous avons relevées, que nous jugeons les plus importantes pour un système de
détection d’intrusions à base de scénarios. Il est à noter que les deux dernières proprié-
tés ne concernent pas directement le langage, mais l’algorithme de vérification utilisé
pour le mettre en oeuvre. Cependant, nous avons jugé bon de les mentionner car un
bon IDS ne doit pas seulement fournir un langage de signatures suffisamment expressif,
il doit aussi être capable de fonctionner à un bon rythme. Autrement dit, l’expressivité
ne doit pas coûter trop cher en complexité algorithmique. À la table 1.10, nous indi-
quons, pour chacun des IDS étudiés, quelles caractéristiques il comporte. Nous n’avons
pas mentionné si le langage LAMBDA comporte les caractéristiques 9 et 10 car nous
n’avons réussi à mettre la main sur aucune documentation au sujet d’une éventuelle
implantation de LAMBDA.

La revue que nous avons faite mettait cependant l’accent sur les langages et les
paradigmes utilisés pour exprimer les signatures dans un contexte de détection. D’autres
langages, comme ADeLe [2], NASL [40] et CASL [41] mettent plutôt l’accent sur le côté
attaquant. ADeLe a été développé en parallèle avec LAMBDA dans le but de développer
une base données d’attaques. Une des préoccupations des auteurs était de concevoir un
langage assez général pour pouvoir représenter, comme LAMBDA, les attaques à la fois
du point de vue de l’attaquant et de l’attaqué, de même que de permettre une corrélation
logique entre les différentes attaques et de spécifier comment réagir à ces attaques. NASL
est un langage de scripts développé pour l’identificateur de failles de sécurité Nessus. Il
fournit des primitives permettant d’envoyer des paquets forgés à la main sur le réseau
et de recevoir d’autres paquets en vue d’effectuer des tests bien précis. CASL a été
développé avec des objectifs similaires et offre des fonctionnalités semblables, mais a
été conçu avec l’objectif de réaliser des attaques plutôt que de simplement tester les
failles.
Chapitre 1. Systèmes de détection d’intrusions 52

1) scénarios à plusieurs événements Les différents exemples que nous avons étudié
nous convainquent que le fait de pouvoir exprimer des scénarios comportant plu-
sieurs événements est nécessaire pour effectuer une détection d’intrusions précise
et complète.
2) non-occurrence d’événements Il arrive parfois que le fait de ne pas observer un
événement soit révélateur d’information.
3) contraintes temps-réel On doit être capable de spécifier un délai entre les diffé-
rents événements d’un scénario.
4) comptage Dans certains cas, c’est la répétition d’un événement ou d’un scénario
donné qui est porteur d’information.
5) acquisition de connaissance Le fait de pouvoir extraire et conserver de façon dy-
namique de l’information des événements observés aide à réduire les faux positifs.
6) propriétés de parité Certaines conditions ne sont valides qu’entre deux événe-
ments donnés.
7) gestion des attributs absents ou multiples Le langage doit prendre automati-
quement en compte le fait que certains attributs peuvent être optionnels pour
certains événements. Aussi, il est possible qu’un attribut ait une liste de valeurs.
8) approche déclarative Un paradigme déclaratif permet, en plus de faciliter la
maintenance de la base de signatures, d’effectuer un calcul de corrélation entre
les différents scénarios. Ce calcul n’est cependant possible que si le langage est
purement déclaratif, au sens de la définition 1.1.
9) fonctionnement en ligne Il doit être possible d’élaborer un algorithme de vérifi-
cation capable de traiter les événements dans l’ordre où ils surviennent, et de les
effacer automatiquement de la mémoire dès qu’ils sont traités.
10) utilisation bornée de la mémoire Il doit être possible d’élaborer un algorithme
de vérification capable de fonctionner avec une quantité fixe de mémoire qui n’aug-
mente pas avec le nombre d’événements.

Tab. 1.9 – Dix propriétés souhaitables d’un IDS.


Chapitre 1. Systèmes de détection d’intrusions 53

nom 1 2 3 4 5 6 7 8 9 10
Snort N N N N O O N O O O
NeVO O N O N O N N O O O
ASAX O N O O O O O N N N
Bro O N O O O O N N O O
STAT O O O O O O N N O N
IDIOT O N O O O O N N O N
BSML O O O O N N N N O O
P-BEST O N N N O O N N O N
LAMBDA O O O N O O N O - -
LogWeaver O O O N N N N O O N
Monid O O O O N N N O O N
Chronicles O O O O N N N O O N

Tab. 1.10 – Tableau récapitulatif des IDS.

Une approche dont nous n’avons pas parlé est celle de [42], basée sur un paradigme
de grammaires. Les auteurs de cet article proposent un formalisme pour représenter les
attaques comprenant entre autres des notions de filtre, de temps-réel, et de séquence-
ment. Ce formalisme vient avec un algorithme de vérification à base de tableaux qui est
prouvé complet et cohérent. On trouve aussi dans cet article une discussion intéressante
au sujet des problèmes pratiques découlant de la complétude des algorithmes de vérifi-
cation de signatures dans le cas des langages déclaratifs. Principalement, on soulève le
fait que la complétude, bien qu’intéressante d’un point de vue théorique, engendre sou-
vent des problèmes de complexité algorithmique et de surcharge d’alarmes. La méthode
proposée par les auteurs permet de régler l’algorithme de vérification en définissant une
relation d’équivalence sur les alarmes de façon à réduire le nombre d’alarmes tout en
conservant celles qui sont considérées comme étant les plus significatives. Le système
ARMD [43], mis au point pour le langage MuSigs, basé sur l’algèbre relationnelle, offre
lui aussi la possibilité de régler l’algorithme de vérification selon les besoins particuliers
de l’utilisateur.

Plusieurs approches issues de techniques d’intelligence artificielle ont aussi été propo-
sées pour attaquer le problème de la détection d’intrusions. Dans [44], on propose une
méthode utilisant les algorithmes génétiques pour apprendre, à partir d’un ensemble
d’entraı̂nement, à reconnaı̂tre les patrons d’attaque. Cette méthode, bien que compor-
tant une phase d’apprentissage, présente cependant deux différences importantes avec
les méthodes basées sur une détection d’anomalies (méthodes statistiques). Première-
ment, l’ensemble d’entraı̂nement fourni à l’algorithme doit être étiqueté. Alors que les
méthodes basées sur une détection d’anomalies ne prennent en entrée que des fichiers
Chapitre 1. Systèmes de détection d’intrusions 54

d’audit, la méthode proposée a besoin, en plus, de savoir où se trouvent les attaques dans
ce fichier. Deuxièmement, le résultat de la phase d’apprentissage ne donne pas un profil
qui doit être considéré comme normal, mais un ensemble de signatures qui peut par la
suite être utilisé par un algorithme de reconnaissance de scénarios. Dans [45] et [46], on
propose d’autres méthodes basées sur la programmation génétique. La programmation
génétique peut être vue comme un cas particulier des algorithmes génétiques où les
individus participant aux croisements sont des programmes, et les gênes sont des bouts
de code. Encore une fois, ces approches dépendent de la disponibilité de fichiers d’au-
dits étiquetés pour la phase d’apprentissage. Cette contrainte n’est pas des moindres
car, depuis les fichiers d’évaluation créés par le laboratoire Lincoln du MIT en 1998 et
1999 [47, 48], peu d’autres fichiers d’audit ou de traces de trafic ont été fournis à la
communauté pour permettre de tels travaux.
Chapitre 2

Logiques temporelles

Dans Le Bourgeois Gentilhomme, de Molière, Monsieur Jourdain répond au maı̂tre


de philosophie qui lui propose de lui enseigner les trois opérations de l’esprit : Cette
logique-là ne me revient point. Apprenons autre chose qui soit plus joli. Les intérêts
de Molière, à cette époque, étaient probablement bien loin de ceux qui nous occupent
aujourd’hui. Cependant, il semblerait qu’il avait déjà compris une vérité fondamentale :
il existe plusieurs logiques. D’abord, qu’est-ce qu’une logique ? L’encyclopédie Hachette
Multimédia, sous l’onglet logique, nous donne un article qui commence par ces mots :
Un raisonnement est une activité de la pensée qui à partir de certains
états de connaissance pris comme prémisses ou hypothèses permet d’arriver
à un autre état de connaissance obtenu dans la conclusion du raisonnement.
Étrangement, il faut attendre la seconde phrase pour retrouver le mot logique, et cette
phrase ne fait plus du tout référence aux notions de raisonnement et de connaissance.
En allant sous l’onglet logique formelle, les résultats commencent à être un peu plus
satisfaisants :
Branche maı̂tresse de la logique, la logique formelle n’étudie la validité
des raisonnements qu’en vertu de leur forme, sans s’intéresser à leur contenu.
La logique semble être ici perçue comme une science, dont une des branches serait la
logique formelle. Si on définit la logique comme une science, il semble alors difficile d’ad-
mettre qu’il puisse exister plusieurs logiques. En effet, on ne parle jamais de la physique
de Untel, ou de la biologie de monsieur Machin. Il y aurait alors, vraisemblablement,
logique et Logique. La Logique serait alors la science qui étudie les logiques, et il nous
resterait toujours à trouver une définition pour le mot logique avec un petit ’l’. Étrange-
ment, dépendamment du domaine dans lequel on se trouve, la définition du mot logique
semble toujours implicite, comme si on avait peur de s’y mouiller, et chacun présente
sa logique de la façon qui lui convient.
Chapitre 2. Logiques temporelles 56

Même au sein d’un domaine comme l’informatique, la définition semble différente dé-
pendamment de la branche qui nous intéresse. Les deux domaines où l’intérêt semble le
plus marqué pour les logiques sont l’intelligence artificielle et la vérification formelle. En
vérification formelle, une logique est perçue comme un langage permettant d’exprimer
certaines propriétés d’un programme ou d’un autre objet abstrait. En intelligence arti-
ficielle, où on s’intéresse davantage aux aspects déductifs et inductifs, la multiplication
des logiques semble un problème moins important et lorsque l’on lit, par exemple, Mc-
Dermott [49], Allen [50], ou Bacchus [51], les formalismes utilisés sont beaucoup moins
uniformes que celles que l’on rencontre en lisant Pnueli [52], Kozen [53], Wolper [33],
Emerson et Halpern [54, 55], ou Henzinger [56], qui eux s’intéressent à la vérification
formelle.

Le présent travail se situant dans un contexte de vérification formelle, le formalisme


que nous allons employer sera celui de ces derniers auteurs. En vérification formelle,
une logique est perçue comme un triplet hM, L, |=i, où M est l’objet du discours, aussi
appelé modèle, L est le langage du discours, et |= est la sémantique du discours, c’est-à-
dire un ensemble de règles permettant de décider, étant donné φ un élément du langage
et m une instance de modèle, si l’affirmation faite par φ est vraie à propos de m. En
termes ensemblistes, M et L sont des ensembles, et |= est un sous-ensemble de M × L.
Généralement, l’ensemble L est généré par un ensemble de règles récursives que l’on
appelle la syntaxe du langage. Souvent, quand on pense à une logique en particulier, on
pense d’abord à sa syntaxe. Cependant, il ne faut pas négliger l’importance du choix
du modèle. Entre autres, si on veut comparer deux logiques en termes d’expressivité,
on demandera que celles-ci s’appliquent au même modèle. Une logique L1 est dite aussi
expressive qu’une logique L2 si, pour chaque formule φ2 de L2 , il existe une formule φ1 de
L1 telle que pour tous les éléments m de M , m |= φ1 si et seulement si m |= φ2 . Elle est
dite plus expressive que L2 si il elle est aussi expressive que L2 et qu’il existe une formule
φ1 de L1 telle qu’il n’existe pas de formule φ2 de L2 telle que pour tous les éléments m
de M , m |= φ1 si et seulement si m |= φ2 . Une logique est donc plus expressive si elle
permet de discerner avec plus de finesse les éléments du modèle. Ceci implique donc,
entre autres, que si deux logiques ne sont pas définies à partir du même modèle, alors
il est par définition impossible de comparer leur expressivité. Notons au passage qu’il
se peut fort bien que deux logiques ne soient pas comparables en expressivité, même si
elles sont définies à partir du même modèle.

La présentation des logiques temporelles que nous ferons dans ce chapitre sera donc
faite d’abord en fonction des modèles utilisés, puis en fonction des différentes syntaxes
et sémantiques. Les principaux modèles utilisés pour les logiques temporelles sont pré-
sentés à la table 2.1. La première ligne, ainsi que la quatrième, sont les modèles statiques
de base, c’est-à-dire intemporels. La logique propositionnelle, telle quelle, ne comprend
Chapitre 2. Logiques temporelles 57

Famille de logiques Modèle


Logique propositionnelle I ∈ 2P
Logiques linéaires propositionelles σ : N → 2P
Logiques temporisée propositionnelles σ : N → 2P × T
Logique du premier ordre I ∈ {P → {X → V }}
Logiques linéaires du premier ordre σ : N → {P → {X → V }}
Logiques temporisées du premier ordre σ : N → {P → {X → V }} × T
Logiques réifiées σ : T → {P → {X → V }}

Tab. 2.1 – Familles de logiques temporelles et leur modèle.

aucune notion de temps. On suppose qu’il existe un nombre fini de constantes pro-
positionnelles, regroupées dans un ensemble P qui sont toutes soit vraies soit fausses.
L’attribution du caractère de vérité à ces constantes est ce qu’on appelle une interpré-
tation, ou encore une valuation, et l’ensemble de ces interprétations constitue le modèle
de la logique propositionnelle. Dans le cas de la logique du premier ordre, l’ensemble
primitif P n’est plus un ensemble de constantes propositionnelles, mais un ensemble de
prédicats, c’est-à-dire un ensemble d’objets de la forme p(x1 , . . . , xn ). Une interpréta-
tion sera alors vue comme un ensemble de façons d’attribuer des valeurs aux variables
x1 , . . . , xn , lesquelles valeurs seront choisies dans un ensemble V .

Vient alors la question de la temporisation de ces modèles. Dans le cas du premier


ordre, une avenue possible est de considérer que tous les prédicats sont de la forme
p(t, x1 , . . . , xn ). Autrement dit, chaque attribution de valeurs aux variables d’un prédicat
est associée à un temps. Cette stratégie est celle qui est parfois utilisée dans les bases
de données. Les règles permettant de manipuler de tels objets ne sont cependant pas
des plus nettes, et d’autres façon de modéliser le temps ont été suggérées. Une première
façon, la plus couramment utilisée, est de dire que les modèles que nous considérions
comme statiques évoluent de façon discrète dans le temps et que celui-ci s’écoule à
intervalles réguliers t0 , t1 , t2 , . . .. À chaque instant tn on retrouve donc une nouvelle
interprétation. Le terme logique temporelle réfère habituellement aux logiques utilisant
ce type de modèle. Un peu plus tard, on a ressenti le besoin de tenir compte du fait
que le temps ne s’écoulait peut-être pas à intervalles réguliers. On a alors ajouté, à
chaque instant tn , un temps auquel le changement de valuation s’effectue. Ces logiques
sont habituellement désignées sous le vocable de logiques temporisées (timed temporal
logics). Finalement, lorsque les changements s’effectuent de façon possiblement continue
dans le temps, la notion d’instant est supprimée, à chaque point de la ligne du temps
se trouve une nouvelle interprétation. Ce modèle est généralement celui qui est sous-
entendu lors de la définition de logiques réifiées, qui ne seront pas traitées ici. Une
excellente revue des logiques réifiées se trouve cependant dans [57]. Dans les sections
Chapitre 2. Logiques temporelles 58

qui suivent, nous traitons d’abord les logiques temporelles, puis les logiques temporisées.

2.1 Logiques temporelles

Toutes les logiques temporelles que nous présentons dans cette section sont des
logiques dites linéaires, qui se définissent par opposition aux logiques dites arbores-
centes, telles que CTL, CTL∗ [58], ou le µ-calcul modal [53]. La différence entre une
logique linéaire et une logique arborescente se situe d’abord au niveau du modèle. Étant
donné l’ensemble des exécutions possibles d’un programme, une logique linéaire permet
d’exprimer les propriétés de chacune de ces exécutions, prises séparément, alors qu’une
logique arborescente permet de tenir compte des liens entre ces diverses exécutions. Prin-
cipalement, une logique arborescente permet d’exprimer la propriété à partir d’un cer-
tain moment, il sera possible d’aller vers un chemin d’exécution ayant la propriété. . . .
La pertinence de telles propriétés a fait l’objet d’une guerre de clochers pendant un
certain temps [55], et il semblerait que le succès technologique de l’outil de vérification
SPIN [59] ait fait pencher la balance du côté des logiques linéaires. Peu importe, la
raison pour laquelle les logiques arborescentes ne sont pas présentées ici est qu’étant
donné que, dans le contexte de notre travail, nous ne considérons qu’un seul chemin
d’exécution (celui en cours), la notion d’arborescence ne présente aucun intérêt pour
nous.

2.1.1 Logique temporelle linéaire

φ ::= p | ¬φ | φ1 ∨ φ2 | °φ | φ1 U φ2

Tab. 2.2 – Syntaxe de LTL.

De toutes les logiques présentées dans ce chapitre, la logique temporelle linéaire est
celle qui historiquement est apparue en premier [60, 52]. La majorité des autres logiques
que nous présentons dans ce chapitre sont des variantes de celles-ci ou s’y réfèrent. Le
modèle sur lequel LTL est définie, comme toutes les autres logiques présentées dans cette
section, est celui de la deuxième ligne de la table 2.1, c’est-à-dire une séquence infinie
d’états, aussi appelée trace, que l’on note σ. Les états de la trace sont des ensembles de
constantes propositionnelles. Par σ(i), on dénote le ieme état de σ.
Chapitre 2. Logiques temporelles 59

σ(i) |= p ssi p ∈ σ(i)


σ(i) |= ¬φ ssi σ(i) 6|= φ
σ(i) |= φ1 ∨ φ2 ssi σ(i) |= φ1 ou σ(i) |= φ2
σ(i) |= °φ ssi σ(i + 1) |= φ
σ(i) |= φ1 U φ2 ssi ∃k ≥ i.σ(k) |= φ2 et ∀i ≤ j < k.σ(j) |= φ1

Tab. 2.3 – Sémantique de LTL.

La syntaxe de LTL est donnée à la table 2.2. Les formules de LTL sont les mêmes
que celles du calcul propositionnel, auxquelles on ajoute les opérateurs temporels °
et U. Il s’agit d’opérateurs temporels futurs, au sens où ils permettent d’exprimer
des propriétés concernant les états à venir. L’opérateur ° est l’opérateur prochain
permettant d’exprimer une propriété du prochain état, et l’opérateur U est l’opérateur
jusqu’à ce que, permettant d’exprimer qu’une propriété qui doit être respectée jusqu’à
ce qu’une autre propriété le soit.

À la table 2.3, se trouve la sémantique formelle de LTL. La formule élémentaire p


est satisfaite par un état σ(i) si et seulement si celui-ci contient p. La formule ¬φ est
satisfaite par un état σ(i) si et seulement si celui-ci ne satisfait pas φ. La formule φ1 ∨ φ2
est satisfaite par un état σ(i) si et seulement si celui-ci satisfait φ1 ou φ2 . La formule
°φ est satisfaite par un état σ(i) si et seulement si son successeur immédiat σ(i + 1)
satisfait φ. Finalement, la formule φ1 U φ2 est satisfaite un état σ(i) si et seulement si
il a un successeur σ(k) (pas nécessairement immédiat) satisfaisant φ2 et dont tous les
états σ(j) entre σ(i) et σ(k) satisfont φ1 .

def
¦φ = vrai U φ
def
¤φ = ¬ ¦ ¬φ
def
φ1 U φ2 = (φ1 U φ2 ) ∨ ¤φ1

Tab. 2.4 – Opérateurs définis de LTL.

Outre les opérateurs ∧, →, et ↔, que l’on peut définir comme d’habitude à l’aide des
opérateurs ∨ et ¬, la logique temporelle linéaire comporte d’autres opérateurs définis,
dont la définition se trouve à la table 2.4. Le premier opérateur, ¦φ, se lit nécessairement,
φ se produira. Notons au passage qu’en anglais, cet opérateur se lit eventually. Le lecteur
francophone doit donc faire attention, ici, à la confusion linguistique entre le mot français
Chapitre 2. Logiques temporelles 60

lire i lire i
si i < 0 alors si i < 0 alors
i := −i i := −i
lire j lire j
k := i + j k := i + j

Fig. 2.1 – Exemples de programmes.

éventuellement et le mot anglais eventually. En anglais, si quelque chose se produit


eventually, nous sommes certains que cela finira par arriver, alors qu’en français, le mot
éventuellement laisse place à l’incertitude, et c’est pourquoi nous traduisons eventually
par nécessairement. L’opérateur ¤φ, quand à lui, doit se lire toujours φ. Il est défini
comme il est faux de dire que φ sera fausse. Finalement, l’opérateur φ1 U φ2 est le faible
jusqu’à ce que, qui permet que φ2 ne soit jamais satisfaite.

La logique temporelle linéaire a été conçue pour permettre la vérification statique


de propriétés de programmes. Par exemple, si v est une variable utilisée dans un pro-
gramme, les constantes propositionnelles write(v) et read(v) peuvent représenter l’accès
en écriture et en lecture de cette variable. Alors, la formule ¬read(v) U write(v) signifie
que la variable v ne doit pas être utilisée tant qu’elle n’a pas été initialisée. On utilise
l’opérateur U plutôt que U car si la variable n’est jamais lue, alors il n’est nullement
besoin qu’elle soit initialisée. Considérons, par exemple, les deux programmes montrés
à la figure 2.1, où l’indentation est utilisée pour délimiter les blocs d’instructions. Ces
deux programmes comportent chacun deux chemins d’exécution, dépendamment de la
valeur i fournie par l’usager. Le premier état de chacun de ces quatre chemins respecte la
propriété ¬read(i) U write(i), alors que le premier état d’un des chemins du programme
de gauche ne respecte pas la propriété ¬read(j) U write(j).

2.1.2 Logique temporelle linéaire passée

J
φ ::= p | ¬φ | φ1 ∨ φ2 | °φ | φ1 U φ2 | φ | φ1 S φ2

Tab. 2.5 – Syntaxe de P-LTL.

Les opérateurs temporels de LTL, comme nous l’avons déjà dit, sont des opérateurs
Chapitre 2. Logiques temporelles 61

J
σ(i) |= φ ssi i > 0 et σ(i − 1) |= φ
σ(i) |= φ1 S φ2 ssi ∃0 ≤ k ≤ i.σ(k) |= φ2 et ∀i ≥ j > k.σ(j) |= φ1

Tab. 2.6 – Sémantique des opérateurs particuliers à P-LTL.

temporels futurs, au sens où leur sémantique ne réfère qu’à des états se trouvant après
l’état considéré. Il est alors naturel de se demander s’il serait possible d’augmenter
l’expressivité de LTL en lui ajoutant des opérateurs faisant référence au passé. La logique
obtenue en ajoutant les équivalents passés de U et ° à LTL s’appelle P-LTL (Past-
LTL).

La syntaxe de P-LTL est donnée à la table 2.5. Il s’agit de la même syntaxe que
J
pour LTL, à laquelle on a ajouté des opérateurs temporels passés S et . L’opérateur
J
S doit se lire depuis, et l’opérateur doit se lire juste avant.

La sémantique de ces opérateurs est donnée à la table 2.6. La formule φ1 S φ2 est


satisfaite par un état σ(i) si et seulement si celui-ci est précédé d’un état σ(k) satisfaisant
J
φ2 tel que tous les états σ(j) entre σ(k) et σ(i) satisfont φ1 . La formule φ, quand
à elle, est satisfaite par un état σ(i) si et seulement si il dispose d’un prédécesseur
immédiat et que celui-ci satisfait φ.

def
¨φ = vrai S φ
def
¥φ = ¬¨¬φ

Tab. 2.7 – Opérateurs définis de P-LTL.

Comme dans le cas de LTL, il est possible de définir de nouveaux opérateurs à l’aide
de ces opérateurs. Quelques exemples de ces opérateurs sont donnés à la table 2.7. Les
opérateurs ¨ et ¥ sont les équivalents passés de ¦ et ¤. La formule ¨φ est satisfaite par
un état σ(i) si et seulement si celui-ci est précédé d’un état satisfaisant φ, et la formule
¥φ est satisfaite un état σ(i) si et seulement si tous ses prédécesseurs satisfont φ.

Pour reprendre l’exemple que nous déjà étudié, la formule ¤(read(v) → ¨write(v))
signifie alors que si une variable v est lue, alors avant cela, elle a été initialisée. Cette
formule est satisfaite, pour une variable v et une trace σ données, par les mêmes états
que la formule ¬read(v) U write(v). En effet, cette formule signifie que l’on ne doit pas
Chapitre 2. Logiques temporelles 62

lire la variable v avant de l’avoir initialisée. Ceci nous amène alors à douter du fait que
l’ajout d’opérateurs passés à LTL a vraiment augmenté son expressivité.

Pour répondre à cette question, nous devons d’abord nous demander de quelle ex-
pressivité on parle. En effet, dans les exemples étudiés jusqu’à maintenant, nous n’avons
considéré que l’état initial des chemins d’exécution. En effet, dans plusieurs cas d’ap-
plication, il est sous-entendu qu’un programme donné satisfait une propriété si son état
initial la satisfait. La relation d’équivalence entre des formules obtenue en ne considé-
rant que l’état initial est différente de celle obtenue si on regarde la trace en entier. On
distingue donc deux relations d’équivalence distinctes :

Définition 2.1 (Équivalence de formules) Deux formules φ1 et φ2 sont dites équi-


valentes si, pour toute trace σ et pour tout i ∈ N, σ(i) |= φ1 ssi σ(i) |= φ2 . Elles sont
dites initialement équivalentes si, pour toute trace σ, σ(0) |= φ1 ssi σ(0) |= φ2 .
J J
Exemple 2.2 Les formules p ∧ q et p ∧ ¬q sont initialement équivalentes, mais
ne sont pas équivalentes.

En raisonnant un peu, on voit que toute formule purement passée (n’utilisant pas les
J
opérateurs U et °) de la forme φ est initialement équivalente à faux, et que toute
formule purement passée de la forme φ1 S φ2 est initialement équivalente à φ1 ∧ φ2 .
Donc, si on était capable de sortir les opérateurs passés d’une formule combinant des
opérateurs passés et futurs, de façon à obtenir une combinaison booléenne de formules
purement passées et purement futures, on serait capable de lui trouver une formule
purement future qui lui serait initialement équivalente.

Le résultat suivant, démontré dans [61], nous dit que tel est le cas. La démonstration
étant un peu longue et hautement technique, nous ne la reproduirons pas ici.

Lemme 2.3 (Séparation) Toute formule de P-LTL peut être exprimée comme une
combinaison booléenne de formules purement futures et purement passées.

Ce résultat nous permet alors d’arriver au théorème suivant, dont le détail de la


démonstration se trouve lui aussi dans [61].

Théorème 2.4 (Expressivité de P-LTL) Pour toute formule de P-LTL, il existe


une formule de LTL initialement équivalente.

Il ne faut cependant pas tomber dans le piège de croire que les opérateurs passés
n’apportent rien à la logique temporelle linéaire. En fait, en plus de permettre d’exprimer
Chapitre 2. Logiques temporelles 63

certaines propriétés de façon plus intuitive et, comme nous le verrons sous peu, plus
succincte, il existe des cas d’utilisation où les algorithmes de vérification à utiliser sont
significativement plus efficaces. Par exemple, dans le cas où on ne vérifie qu’un seul
chemin d’exécution en temps réel, si la formule à vérifier est de la forme ¤φ, où φ ne
comporte que les opérateurs passés, il a déjà été démontré, dans [62], qu’il est possible
d’utiliser des algorithmes de vérification qui s’exécutent en temps linéaire et n’utilisent
qu’une quantité de mémoire constante en fonction de la taille du chemin vérifié. En
fait, il est aussi possible d’arriver au même résultat si on se limite aux opérateurs
futurs [31], mais comme les algorithmes utilisés partent alors de la fin de la trace, on
est forcé d’attendre que le programme ait terminé son exécution avant de passer à la
vérification. En plus de ne fonctionner que dans le cas où le programme à vérifier n’a
pas de chemin d’exécution infini, cette méthode oblige à sauvegarder toute la trace
d’exécution, plutôt que de simplement la vérifier en même temps qu’elle se crée. Pour
ces raisons, l’introduction d’opérateurs passés en logique temporelle représente donc un
grand intérêt.

2.1.3 Logique temporelle linéaire avec passé oubliable

J
φ ::= p | ¬φ | φ1 ∨ φ2 | °φ | φ1 U φ2 | φ | φ1 S φ2 | N φ

Tab. 2.8 – Syntaxe de N-LTL.

Lorsque l’on permet de mélanger les opérateurs de passé et de futur, il se peut


que certaines spécifications n’aient pas exactement le sens attendu. Considérons, par
exemple, la formule ¤(reset → ¤(alarm → ¨problem)), signifiant qu’à chaque fois
qu’une remise à zéro est effectuée, l’alarme n’est pas déclenchée tant qu’il n’y a pas eu
un problème (l’alarme ne sonne pas pour rien). Alors, cette formule est satisfaite par la
trace :
reset, problem, alarm, alarm, . . .
mais aussi par la trace :

reset, problem, alarm, reset, alarm, . . .

on se retrouve alors aux prises avec le problème suivant : l’alarme continue de sonner
après la remise à zéro. On voudrait être capable de dire que le problème est survenu
depuis la remise à zéro. Autrement dit, on voudrait être capable d’oublier le passé. Pour
régler ce problème, certains auteurs [63, 64] ont proposé d’ajouter à P-LTL l’opérateur
Chapitre 2. Logiques temporelles 64

σ(i) |= N φ ssi σ i (0) |= φ

Tab. 2.9 – Sémantique de l’opérateur particulier à N-LTL.

N, qui se lit à partir de maintenant, et dont l’objectif est justement de permettre


d’oublier le passé. La logique obtenue alors s’appelle N-LTL, et sa syntaxe est donnée
à la table 2.8. La sémantique de l’opérateur N est donnée à la table 2.9. La notation σ i
désigne le ieme suffixe de σ. Il s’agit de la trace obtenue en coupant les i premiers états
de σ. C’est-à-dire que σ i (j) = σ(j + i).

Avec ce nouvel opérateur, il est alors en mesure d’exprimer la propriété voulue à


l’aide de la formule : ¤(reset → N(¤(alarm → ¨problem))).

Cependant, on remarque aussi que la formule de LTL ¤(reset → ¬alarm U problem)


exprime la propriété voulue, et que cet exemple ne nous convainc pas tellement de la né-
cessité, du point de vue de l’expressivité, de l’opérateur N. En fait, les introducteurs de
N-LTL ne prétendent nullement ajouter à l’expressivité de P-LTL. Ils montrent même,
dans [63], que toute formule de N-LTL peut être traduite en une formule de LTL qui lui
est équivalente. D’après eux, le principal avantage lié à son utilisation réside au niveau
de la taille des formules nécessaires pour exprimer certaines propriétés. Dans [64], ils
montrent les deux résultats suivants, qui ensemble justifient l’introduction de P-LTL et
N-LTL, malgré le fait qu’elles n’apportent rien du point de vue de l’expressivité :

Théorème 2.5 (Succinteté de P-LTL) P-LTL peut être exponentiellement plus suc-
cinte que LTL.

Théorème 2.6 (Succinteté de N-LTL) N-LTL peut être exponentiellement plus suc-
cincte que P-LTL.

Une logique L1 est dite exponentiellement plus succincte qu’une logique L2 si il


existe une propriété ψ tel que si φ1 et φ2 sont les plus petites formules de L1 et L2
exprimant ψ, alors |φ2 | ∈ Ω(|φ1 |). C’est-à-dire que le nombre d’opérateurs de φ2 est au
moins exponentiellement plus grand que celui de φ1 . Ce résultat est intéressant pour au
moins deux raisons. Premièrement, du point de vue de la vérification, cela signifie que le
temps de vérification peut être exponentiellement plus court. Deuxièmement, du point
de vue de la facilité d’utilisation, cela signifie que les spécifications peuvent s’écrire à
l’aide de formules exponentiellement plus courtes.
Chapitre 2. Logiques temporelles 65

2.1.4 µ-calcul linéaire

J
φ ::= X | p | ¬φ | φ1 ∨ φ2 | °φ | φ | µX.φ

Tab. 2.10 – Syntaxe du µ-calcul linéaire.

σ(i) |= µX.φ ssi ∃j.σ(i) |= µj X.φ


def
avec µ0 X.φ = faux
def
et pour j 6= 0, µj X.φ = φ[µj−1 X.φ/X]

Tab. 2.11 – Sémantique de l’opérateur particulier au µ-calcul linéaire.

Une autre façon de percevoir le déroulement du temps est de ne prendre que les
opérateurs de passé et de futur immédiats, et de permettre de définir des formules
récursivement. Par exemple, on pourrait définir l’opérateur ¦φ de la façon suivante :

σ(i) |= ¦φ si et seulement si σ(i) |= φ ou σ(i) |= ° ¦ φ.

Par induction, cette formule serait alors satisfaite par σ(i) si et seulement si il existe
un état à partir de σ(i) satisfaisant la formule φ.

L’opérateur permettant de définir de façon générale des formules de façon récursive


est l’opérateur µ, appelé opérateur de plus petit point fixe. Lorsque l’on ajoute l’opéra-
teur de point fixe à une logique déjà existante, on dit que l’on obtient un µ-calcul. Par
exemple, si on ajoute l’opérateur de point fixe à la logique modale, on obtient le µ-calcul
modal. Le µ-calcul dont il est question ici est le µ-calcul linéaire, dont la syntaxe est
donnée à la table 2.10. Avec cette syntaxe, on peut définir la formule ¦φ de la façon
def
suivante : ¦φ = µX.(φ ∨ °X). Informellement, ¦φ se trouve alors à être définie comme
la formule X satisfaite si et seulement si φ est satisfaite ou X est satisfaite par l’état
suivant.

La sémantique formelle de l’opérateur de point fixe est donnée à la table 2.11. Il


s’agit d’une sémantique basée sur la notion d’approximants. Pour X une variable libre
(n’apparaissant pas sous la portée d’un opérateur µX) apparaissant dans φ, le premier
approximant de φ selon X est tout simplement la formule faux. Ensuite, on définit
le ième approximant récursivement à partir du i − 1ème de la façon suivante : le ième
approximant selon X est la formule φ, dans laquelle on a textuellement substitué la
Chapitre 2. Logiques temporelles 66

def
νX.φ = ¬µX.φ[¬X/X]
def
φ1 U φ2 = µX.φ2 ∨ (φ1 ∧ °X)
def J
φ1 S φ2 = µX.φ2 ∨ (φ1 ∧ X)

Tab. 2.12 – Opérateurs définis du µ-calcul linéaire.

variable X par le i − 1ème approximant selon X. La formule µX.φ est alors satisfaite par
σ(i) si et seulement si il existe un approximant selon X satisfait par σ(i). Par exemple,
dans le cas de la formule ¦φ, la suite des approximants selon X est faux, φ ∨ °faux,
φ ∨ °(φ ∨ °faux),. . . .

On peut aussi définir, à partir de l’opérateur de plus petit point fixe, l’opérateur
ν, qui est l’opérateur de plus grand point fixe. Sa définition formelle est donnée à la
table 2.12. L’approximant de base devient alors true, et la formule νX.φ est alors
satisfaite si et seulement si elle est satisfaite par tous ses approximants selon X. Par
exemple, il permet de définir ¤φ comme étant νX.(φ ∧ °X).

Il est facile de voir que le µ-calcul linéaire est au moins aussi expressif que LTL ou
P-LTL. À la figure 2.12, on montre comment on peut définir les opérateurs de LTL et
P-LTL à l’aide des opérateurs du µ-calcul linéaire. Il est cependant moins facile de voir
si celui-ci est strictement plus expressif. En fait, le lemme suivant [33], que nous ne
démontrerons pas ici, nous permet de conclure que le µ-calcul linéaire est strictement
plus expressif que LTL :

Lemme 2.7 Il n’existe pas de formule de LTL satisfaite par tous les états σ(i) d’une
trace si et seulement si ∀i.p ∈ σ(i) ssi i est pair.

Alors, comme la formule du µ-calcul linéaire νX.p ∧ °¬p ∧ ° ° X exprime exac-


tement cette propriété, on arrive au résultat suivant :

Théorème 2.8 Le µ-calcul linéaire est strictement plus expressif que LTL.

Nous terminons cette section en remarquant que nous n’avons pas justifié l’emploi
du terme point fixe pour désigner les opérateurs µ et ν. Ceci est dû au choix que
nous avons fait de présenter leur sémantique à l’aide de la notion d’approximant. Une
autre façon de faire est de voir une formule φ comportant une variable libre X comme
une fonction prenant en entrée un ensemble d’états et donnant en sortie l’ensemble
Chapitre 2. Logiques temporelles 67

des états satisfaisant φ sous l’hypothèse que ceux-ci satisfont déjà X. L’ensemble des
états satisfaisant µX.φ (νX.φ) est alors le plus petit (le plus grand) point fixe de
cette fonction. L’existence de ce point fixe est garantie à condition que la fonction
soit monotone (ce qui sera le cas si on pose les restrictions nécessaires sur l’imbrication
des négations ou mieux encore, si on ne permet de ne les appliquer qu’aux constantes
propositionnelles). L’existence de ce point fixe a été démontrée par Tarski [65] en 1955
dans un contexte plus général : celui des treillis. Nous nous arrêtons ici sur cette façon
de définir la sémantique des opérateurs de point fixe, mais sachons seulement qu’il est
possible de les démontrer comme étant équivalentes [53].

2.2 Logiques temporisées

Les quatre logiques que nous venons de présenter étaient toutes comparables du point
de vue de l’expressivité car elles étaient toutes définies à partir du même modèle : celui
de la deuxième ligne de la table 2.1. Les logiques que nous présentons dans cette section
ne leur sont pas comparables car elles sont définies à partir de modèles temporisés.

Les deux premières logiques que nous présentons sont des logiques propositionnelles,
et leur modèle est celui de la troisième ligne. À chaque événement on associe, en plus
d’un ensemble de constantes propositionnelles, un instant t pris dans un domaine tem-
porel T. Les éléments σ(i) d’une trace σ sont donc des couples, dont la première compo-
sante, σ1 (i), est l’ensemble de constantes propositionnelles, et la seconde composante,
σ2 (i), est le temps qui lui est associé. Si l’ensemble T choisi pour représenter le temps
est R, on dispose d’une représentation du temps qui est continue, et s’il s’agit de N,
on dispose d’une représentation discrète. Dans le cas de ces deux premières logiques,
l’ensemble choisi par les auteurs est N.

La troisième logique que nous présentons se distingue des deux autres de par le fait
que le modèle sur lequel elle est définie est celui d’une logique temporisée du premier
ordre (avant-dernière ligne de la table 2.1). Aussi, le choix du domaine temporel (continu
ou discret) est laissé libre à l’utilisateur.

2.2.1 Logique temporelle temporisée

Les premiers articles sur la vérification de systèmes temps-réel datent de la fin des
années 80. Il ne faut pas confondre ce problème avec celui de la vérification de systèmes
Chapitre 2. Logiques temporelles 68

π ::= x + c | c
φ ::= p | π1 ≤ π2 | π1 ≡d π2 | x.φ | ¬φ | φ1 ∨ φ2 | °φ | φ1 U φ2

Tab. 2.13 – Syntaxe de T-LTL.

en temps réel (qui est celui qui nous intéresse dans le cadre du présent mémoire). Un
système temps-réel est un système dont la spécification comporte des aspects temporels
exprimés en unités de temps. Par exemple, au lieu de s’intéresser à ce que la porte du
garage se ferme après que l’auto y soit entrée, on s’intéressera à ce qu’elle se ferme pas
plus de une minute après que l’auto y soit entrée.

De la même façon que plusieurs formalismes ont été proposés pour décrire les sys-
tème non-temporisés (automates, algèbres de processus, réseaux de Petri, structures de
Kripke, systèmes de transition étiquetés) plusieurs formalismes ont aussi été proposés
pour représenter les systèmes temps-réel (automates temporisés, algèbres de processus
temporisées, etc.). Une des caractéristiques les plus importantes des systèmes temporisés
est que les différentes actions pouvant être effectuées par le système ne sont plus consi-
dérées comme pouvant être effectuées en un simple tic d’horloge, mais en un certaine
nombre de tics d’horloge. Par exemple, si un système peut effectuer la suite d’actions
ouvrir−entrer−f ermer, et que ces actions prennent respectivement 5,10 et 5 secondes,
alors le système peut effectuer l’action f ermer après avoir effectué l’action ouvrir, mais
il ne peut pas l’effectuer dans un délai de 2 secondes, car il doit entre temps effectuer
l’action entrer, qui elle prend 10 secondes.

Nous ne nous étendrons pas ici sur les différents formalismes ayant été proposés
pour représenter les systèmes temps-réel, mais sachons seulement que c’est dans ce
contexte que les logiques temporisées sont d’abord apparues. Un des auteurs ayant le
plus participé au développement de la vérification des systèmes temps-réel est sans
doute Thomas A. Henzinger qui, dans sa thèse de doctorat [66], a proposé tout une
méthodologie pour représenter et vérifier les systèmes temps-réel.

Une de ses contributions, du point de vue des logiques temporelles, est d’avoir pro-
posé d’ajouter aux diverses logiques temporelles déjà existantes (LTL [56], CTL [67],
µ-calcul modal [67], et même ETL [66], dont nous ne parlons pas ici) la notion de chrono-
mètre. Un chronomètre est une variable qui s’unifie avec le temps de certains événements
à l’aide d’un opérateur spécial, appelé opérateur d’initialisation (reset). Une fois initia-
lisé, un chronomètre peut être utilisé pour spécifier les contraintes temporelles devant
être vérifiées entre diverses actions.
Chapitre 2. Logiques temporelles 69

σ(i) |=ε x.φ ssi σ(i) |=ε[σ2 (i)/x] φ


σ(i) |=ε π1 ≤ π2 ssi ε(π1 ) ≤ ε(π2 )
σ(i) |=ε π1 ≡d π2 ssi ε(π1 ) ≡d ε(π2 )

def def
avec ε(x + c) = ε(x) + c et ε(c) = c

Tab. 2.14 – Sémantique des opérateurs particuliers à T-LTL.

def
¦t φ = x.(true U y.(φ ∧ y < x + t))

Tab. 2.15 – Un opérateur défini de T-LTL.

Dans le cas de LTL, l’ajout de chronomètres donne la logique T-LTL [56], dont la
syntaxe est donnée à la table 2.13. La construction π représente une référence temporelle.
Le cas c est une référence temporelle absolue, et le cas x + c est une référence temporelle
relative (c unités de temps après x). La construction x.φ est celle permettant d’unifier
la variable x avec l’instant présent. Finalement, les constructions π1 ≤ π2 et π1 ≡d π2
sont des contraintes temporelles. La première signifie que π1 est plus petite ou égale à
π2 , alors que la seconde signifie que π1 est congrue à π2 modulo d. On remarque que
si T-LTL était définie pour un domaine temporel continu, cette dernière construction
n’aurait aucun sens.

La sémantique des opérateurs ainsi ajoutés à LTL est donnée à la table 2.14. On
remarque qu’il s’agit de la seule logique parmi celles présentées dans ce chapitre pour
laquelle on a besoin d’une notion d’environnement. Un environnement ε est une fonc-
tion partielle associant des instants à certains des chronomètres de la formule. Ceux-ci
sont mis à jour à l’aide de l’opérateur d’initialisation. La formule x.φ est donc satis-
faite par σ(i) sous un environnement ε si et seulement si φ est satisfaite par σ(i) sous
l’environnement ε mis à jour de façon à associer au chronomètre x l’instant de σ(i).
Les contraintes temporelles, quand à elles, sont satisfaites sous ε si les relations qu’elles
expriment sont vraies en remplaçant les chronomètres par les valeurs auxquelles ils sont
associés dans ε.

Nous ne pousserons pas plus loin cette présentation de T-LTL. Nous terminons en
présentant comment on peut définir, à l’aide des opérateurs de T-LTL, l’opérateur ¦t φ.
Chapitre 2. Logiques temporelles 70

Cette définition se trouve à la figure 2.15. Elle signifie que si x est l’instant associé à
l’événement présent, alors, à un certain instant y situé à moins de t unités de temps
dans le futur, φ doit être satisfaite.

2.2.2 Logique temporelle métrique

I ::= [m, n] | ≡d c
J
φ ::= p | ¬φ | φ1 ∨ φ2 | °I φ | φ1 UI φ2 | I φ | φ1 SI φ2

Tab. 2.16 – Syntaxe de MTL.

σ(i) |= °I φ ssi σ(i + 1) |= φ et σ2 (i + 1) ∈ σ2 (i) + I


σ(i) |= φ1 UI φ2 ssi ∃k ≥ i.σ(k) |= φ2 et σ2 (k) ∈ σ2 (i) + I
et ∀i ≤ j < k.σ(j) |= φ1
J
σ(i) |= I φ ssi σ(i − 1) |= φ et σ2 (i + 1) ∈ σ2 (i) − I
σ(i) |= φ1 SI φ2 ssi ∃k ≤ i.σ(k) |= φ2 et σ2 (k) ∈ σ2 (i) − I
et ∀i ≥ j > k.σ(j) |= φ1
def
σ2 (i) ± [m, n] = [m ± σ2 (i), n ± σ2 (i)]
def
σ2 (i) ± (≡d c) = =d (σ2 (i) ± c)
σ2 (i) ∈ [m, n] ssi m ≤ σ2 (i) ≤ n
σ2 (i) ∈ ≡d c ssi σ2 (i) =d c

Tab. 2.17 – Sémantique des opérateurs particuliers à MTL.

Pour exprimer les contraintes de temps-réel, Henzinger a choisi d’ajouter à LTL cer-
tains opérateurs. D’autres auteurs [68], contemporains à Henzinger, ont plutôt proposé
de modifier les opérateurs temporels déjà présents dans LTL, en les indexant direc-
tement par les contraintes. La logique ainsi obtenue s’appelle MTL (Metric Temporal
Logic).

La syntaxe de MTL est donnée à la table 2.16. Les contraintes temporelles sont de
deux types : intervalles ([m, n]) et relations de congruence (≡d ). Par exemple, l’opérateur
def
¦t , est défini dans MTL de la façon suivante : ¦t φ = true U[0,t] φ.

La sémantique des opérateurs de MTL est donnée à la table 2.17. Ils s’interprètent
Chapitre 2. Logiques temporelles 71

tous de la même façon que pour LTL, sauf que les états concernés sont restreints à la
contrainte temps-réel I. Par exemple, si σ2 (i) = 7, alors °[0,5] φ est satisfaite par σ(i)
ssi σ(i + 1) |= φ et σ2 (i + 1) ∈ [7, 12]. Même chose pour la relation de congruence. Si
σ2 (i) = 7, alors °≡5 φ est satisfaite par σ(i) ssi σ(i + 1) |= φ et σ2 (i + 1) ≡5 12.

Dans sa thèse, Henzinger affirme que les deux façons d’exprimer les contraintes
temps-réel sont équivalentes. Il affirme aussi que MTL, du point de vue de la décidabilité,
se prête mieux aux contraintes passées que T-LTL. Historiquement, il semblerait que
MTL ait gagné la préférence de la communauté, car au cours des recherches que nous
avons faites, nous avons trouvé un plus grand nombre d’auteurs utilisant MTL que
T-LTL.

2.2.3 TRIO

φ ::= ∀xi .φ | p(x1 , . . . , xn ) | ¬φ | φ1 ∨ φ2 | Dist(φ, t)

Tab. 2.18 – Syntaxe de TRIO.

σ(i) |= Dist(φ, t) ssi ∃j.σ(j) |= φ et σ2 (j) = σ2 (i) + t

Tab. 2.19 – Sémantique de l’opérateur particulier à TRIO.

Nous terminons ce chapitre avec une brève description de TRIO [69], une logique
temporelle du premier ordre avec quantificateurs. Nous verrons que lorsque l’on dispose
de la puissance d’expression de la logique du premier ordre, le nombre d’opérateurs
temporels dont on a besoin chute rapidement.

La syntaxe de TRIO est donnée à la table 2.18. Cette logique ne comporte qu’un
seul opérateur temporel, Dist, dont la sémantique est donnée à la table 2.19. La formule
Dist(φ, t) est vraie pour les états σ(i) ayant un état situé à une distance t (positive ou
négative, finie ou infinie) satisfaisant φ.

Grâce à la présence du quantificateur existentiel ∀, l’opérateur Dist permet d’expri-


mer toutes les relations temporelles de LTL, P-LTL, T-LTL ou MTL. À la table 2.20, on
Chapitre 2. Logiques temporelles 72

def
°φ = Dist(φ, t) ∧ ∀d.Dist(true, d) → d > t
J def
φ = Dist(φ, −t) ∧ ∀d.Dist(true, −d) → d > t
def
¤t φ = ∀d.0 ≤ d < t → Dist(φ, d)
def
φ1 U φ2 = ∃t.Dist(φ2 , t) ∧ ¤t φ1
def
¥t φ = ∀d.0 ≥ d > −t → Dist(φ, d)
def
φ1 S φ2 = ∃t.Dist(φ2 , −t) ∧ ¥t φ1

Tab. 2.20 – Quelques opérateurs définis de TRIO.

voit quelques exemples d’opérateurs temporels que l’on peut définir à partir de l’opéra-
teur Dist. Il est sous-entendu, dans ces définitions, que les variables t et d sont positives.
Par exemple, l’opérateur °φ signifie qu’il existe un état situé à une distance positive t
satisfaisant φ, et que tous les états situés à une distance positive sont situés plus loin.
Les autres opérateurs sont définis de façon semblable.

2.3 Conclusion

Dans ce chapitre, nous avons donné une présentation de sept logiques temporelles
définies à partir de trois modèles différents. Le premier choix à faire, face à un problème
de vérification, est celui du modèle. Une fois que celui-ci est choisi, on se penche alors
sur celui de la logique.

Dans les deux chapitres suivants, nous verrons comment le choix du modèle et de la
logique peut influencer la qualité du travail. D’une part, le modèle choisi doit à la fois col-
ler le mieux possible à la vision que nous avons du problème auquel nous nous attaquons,
et d’autre part, la logique doit comporter des opérateurs appropriés non-seulement aux
propriétés vérifiées, mais aussi au contexte de la vérification. Au chapitre 3 comme au
chapitre 4, le modèle que nous choisirons est un modèle du premier ordre. Des hypo-
thèses différentes sont cependant faites par rapport aux interprétations (la fonction I
de la table 2.1) faites de chaque état. De plus, la logique utilisée au chapitre 3 est
une logique temporisée, alors que celle du chapitre 4 peut être temporisée, mais n’a
absolument pas besoin de l’être pour pouvoir l’utiliser telle que nous la décrivons. Fi-
nalement, la principale différence entre les deux logiques que nous présentons est que
celle du chapitre 3 dispose d’opérateurs temporels futurs, alors que celle du chapitre 4
dispose d’opérateurs temporels passés. Nous verrons que dans un contexte de vérifica-
Chapitre 2. Logiques temporelles 73

tion en temps-réel, une logique définie à partir d’opérateurs passés est non-seulement
plus intuitive, mais aussi algorithmiquement avantageuse.
Chapitre 3

Détection d’intrusions et analyse


passive

L’analyse passive de réseaux regroupe un ensemble de techniques heuristiques per-


mettant d’acquérir passivement - c’est-à-dire sans générer de trafic - de l’information sur
un réseau informatique. L’acquisition passive s’oppose à l’acquisition active, qui consiste
en un ensemble de techniques permettant d’acquérir de l’information en injectant du
trafic sur le réseau et en attendant de voir sa réaction. Un exemple simple de technique
d’analyse active est l’utilisation du programme ping, qui permet de savoir si un hôte
donné est branché au réseau et prêt à communiquer. Le programme ping utilise de fa-
çon tout à fait normale le protocole ICMP, en envoyant à l’hôte concerné une requête
de type echo, à laquelle il doit normalement répondre en envoyant un paquet de type
reply. La réception du paquet reply permet, de façon générale, de conclure que l’hôte
distant est en ligne et prêt à recevoir de l’information. Il est à noter cependant que le
protocole ICMP n’est qu’un protocole de contrôle, n’ayant aucunement été conçu avec
des considérations de sécurité, et que rien ne permet de conclure que le paquet echo
n’a pas été intercepté par un tiers, qui pourrait soit avoir bloqué le paquet, soit avoir
envoyé un faux paquet reply.

Le protocole ICMP permet aussi, en plus de tester la connectivité avec un hôte


distant, de tester la qualité de la connection. Pour ce faire, le protocole prévoit un
champ données, de longueur variable et de contenu non-spécifié. L’hôte envoyant la
requête echo remplit ce champ avec des valeurs arbitraires, et l’hôte répondant doit
recopier les valeurs qu’il a reçues dans le paquet reply. Si, à l’arrivée, les valeurs du
champ données sont différentes de celles envoyées au départ, il faut conclure que la
connection entre les deux hôtes est de piètre qualité et que la communication est par
conséquent non-fiable. Cette fonctionnalité du protocole ICMP permet de l’utiliser afin
Chapitre 3. Détection d’intrusions et analyse passive 75

de détecter passivement le système d’exploitation de l’hôte envoyant la requête echo.


En effet, comme le protocole ne spécifie aucunement quel doit être le contenu du champ
données, celui-ci est laissé à la fantaisie du programmeur et il est possible de recon-
naı̂tre le système d’exploitation, ou du moins une famille de systèmes d’exploitation, en
inspectant la valeur de ce champ. Par exemple, certains programmeurs peuvent avoir
choisi d’envoyer abcdefg, alors que d’autres peuvent avoir choisi qwertyu. L’ensemble
des techniques permettant de détecter, activement ou passivement, le système d’exploi-
tation d’un hôte donné s’appelle, dans la littérature, la prise d’empreintes digitales de
système d’exploitation (Operating System Fingerprinting) [70, 71].

Il existe ainsi un bon nombre de techniques heuristiques permettant d’acquérir de


l’information sur un réseau informatique simplement en observant les particularités
d’implantation et les comportements, normaux ou non, des différents protocoles y cir-
culant. Cette information peut être utilisée soit malicieusement par un utilisateur mal
intentionné afin de mieux préparer son attaque, soit judicieusement par un analyste en
sécurité afin de mieux protéger un réseau informatique. En effet, cette information peut
entre autres, comme nous l’avons vu au chapitre 1 avec le logiciel NeVO, permettre
de découvrir des failles insoupçonnées dans le réseau et d’y rémédier avant qu’elles ne
soient exploitées par un utilisateur mal intentionné. Dans ce chapitre, nous présente-
rons une première approche intuitive permettant de mettre à profit l’analyse passive
en vue d’améliorer la détection d’intrusions. En fait, la détection d’intrusions sera ici
perçue comme un cas particulier d’analyse passive, où les intrusions ne seront qu’un cas
particulier d’information à acquérir.

Dans la section 3.1, nous donnons un aperçu général de la structure logicielle d’un
système de détection d’intrusions et d’analyse passive que nous avons développé. Dans
la section 3.2, nous présentons la syntaxe du langage de signatures du système déve-
loppé. Dans la section 3.3, nous montrons comment le langage peut être utilisé pour
détecter des scénarios d’attaque se déroulant sur plusieurs paquets. Dans la section 3.4,
nous montrons comment le langage peut être utilisé pour acquérir passivement de l’in-
formation sur un réseau informatique. Dans la section 3.5, nous présentons le langage
de signatures d’une façon formelle de même que l’algorithme de vérification en ligne
lui étant associé. Finalement, nous concluons en critiquant le système et le langage
développés en les comparant à ceux analysés au chapitre 1.
Chapitre 3. Détection d’intrusions et analyse passive 76

Modules d’affichage

Base de connaissances

Modules d’affichage Module de scénarios

Module de détection Module de détection

Préprocesseurs Préprocesseurs

Module de décodage Module de décodage

Libpcap Libpcap

Réseau Réseau

Fig. 3.1 – Architecture du système développée (à droite), et celle de Snort (à gauche).

3.1 Architecture du système

Nous avons conçu et implanté un système de détection d’intrusions basé sur un lan-
gage simple qui, dans le style de LAMBDA, permet de tenir à jour et d’utiliser une
base de connaissances en vue d’effectuer une détection plus précise. Ce système réuti-
lise une partie de l’architecture logicielle de Snort - principalement les préprocesseurs
effectuant un travail de formattage sur les paquets - mais utilise un nouvel engin de
détection permettant, en plus de détecter les paquets pouvant être considérés comme
offensifs, de détecter des suites de paquets pouvant être considérées comme offensives.
Nous avons aussi ajouté à l’architecture du système une base de connaissances qui
permet, en plus d’accumuler les connaissances acquises, d’inférer de nouvelles connais-
sances. Les règles d’inférence de cette base de connaissances sont spécifiées en Prolog,
ce qui fait que le système développé peut être vu comme un système à deux niveaux,
avec un engin permettant de détecter les suites de paquets, et un autre permettant d’in-
férer les connaissances. La base de connaissances, en plus de permettre une détection
d’intrusions plus précise, peut être consultée directement par l’administrateur réseau en
cas de besoin. Par exemple, il pourrait l’utiliser afin de savoir quels sont les hôtes du
réseau offrant le service Telnet. Finalement, elle permet aussi de vérifier des politiques
de sécurité haut niveau, telles que il ne doit y avoir qu’un seul routeur dans le réseau.
Chapitre 3. Détection d’intrusions et analyse passive 77

3.2 Langage

Nous commençons par présenter de façon informelle le langage que nous avons dé-
veloppé, de façon à mettre en évidence les objectifs poursuivis et l’intuition derrière les
choix que nous avons faits. Les aspects formels ne sont traités qu’à partir de la sec-
tion 3.5. La grammaire du langage développé, sous la forme BNF (Backus Naur Form),
est donnée à la table 3.1.

hspecificationi ::= hscenarioi*


hscenarioi ::= hstepi(hstepihtimeouti)*
hstepi ::= hheaderihfilterihoutputi*
hheaderi ::= step hidi : hsnortsigi
hfilteri ::= match : hmatchrulei*
hmatchrulei ::= hidi.hfieldihopihidi.hfieldi ; | hprologi ;
hopi ::= <|≤|>|≥|=
houtputi ::= output : hprologi
htimeouti ::= timeout : hidi.timestamp + htimeihoutputi*
htimei ::= hnumberihtimeuniti
htimeuniti ::= sec | hours | days | weeks | years

Tab. 3.1 – Syntaxe du langage de signatures.

Une spécification (non-terminal hspecificationi) est constituée d’un ensemble de scé-


narios (non-terminal hscenarioi) qui doivent être vus comme des suites d’étapes (non-
terminal hstepi) à franchir. Comme nous nous situons dans un contexte de détection
d’intrusions au niveau réseau, chaque étape doit être vue comme un paquet, identifié
dans l’entête de l’étape (non-terminal hheaderi) par son nom (hidi) et par une signature
Snort (hsnortsigi) permettant d’identifier certaines de ses caractéristiques (par exemple,
contenir une chaı̂ne de caractères donnée, être un paquet SYN, etc.). Comme les si-
gnatures Snort ne s’appliquent qu’à un seul paquet, on doit ajouter une construction
syntaxique supplémentaire permettant de lier ceux-ci entre eux (non-terminal hfilteri).
Les filtres sont un ensemble de règles d’appariement (non-terminal hmatchrulei) per-
mettant, dans un premier lieu, de comparer entre elles les valeurs de certains champs
(non-terminal hfieldi) des différents paquets constituant le scenario, et dans un second
lieu, de consulter la base de connaissances. En plus des conditions exprimées par les
règles d’appariement, chaque étape doit se produire dans un délai donné, relatif aux
étapes précédentes. Ce délai est exprimé par la clause htimeouti, permettant de spéci-
fier le délai d’attente maximal entre deux étapes. Comme la notion de délai ne peut pas
s’appliquer au premier paquet d’un scénario, la première étape n’est pas suivie d’une
Chapitre 3. Détection d’intrusions et analyse passive 78

clause htimeouti. Finalement, à chaque étape, il est possible de faire des ajouts ou des
retraits dans la base de connaissances. Cette interaction peut se faire soit à l’identi-
fication d’un paquet (dans le cas où le non-terminal houtputi suit immédiatement le
non-terminal hfilteri), soit à l’écoulement d’un délai (dans le cas où le non-terminal
houtputi suit immédiatement le non-terminal htimeouti). Dans l’implantation que nous
proposons, la base de connaissances est programmée en Prolog, car ce langage per-
met de traiter et de relier entre elles les connaissances. Le mot-clé output indique une
instruction qui doit être transmise telle quelle à la base de connaissances.

Les symboles hsnortsigi et hprologi ne sont pas définis car ils réfèrent à des langages
existants que nous nous dispenserons de décrire à nouveau ici [1, 72]. La syntaxe du
symbole hidi est la même que celle d’une variable dans le langage de programmation
C. Le symbole hfieldi devrait être décrit par énumération. Il désigne explicitement un
champ d’une entête donnée. Par exemple, les mots sip, dip, sport, dport désignent res-
pectivement les adresses IP de même que les ports source et destination. Dans le cas
d’un paquet ARP, les mots opcode, thdradr, tprotoadr, shdradr, sprotoadr désignent res-
pectivement : le type de paquet (requête ou réponse), les adresses matérielle et logicielle
de la cible de même que celles de la source.

3.3 Scénarios d’attaques complexes

Le balayage de ports est souvent une des premières étapes à effectuer pour attaquer
un système. Il permet de savoir quels sont les services offerts et de se faire ainsi une
première idée de l’approche à utiliser. En fait, bien que non-dommageable pour la vic-
time, le balayage des ports constitue tout de même une attaque en soi car il permet
à l’attaquant d’acquérir des informations privilégiées. Une des difficultés reliées à sa
détection relève du fait qu’il peut se faire en utilisant le protocole TCP de façon tout
à fait normale. C’est pourquoi un système à base de règles tel que Snort, fonctionnant
un paquet à la fois, ne peut à la base détecter ce type d’attaque. Pour ce faire, on
doit y aller par programmation, par exemple à l’aide des préprocesseurs, passant ainsi
par dessus le système de règles. Bien que très efficace, cette façon de faire ne donne
cependant pas beaucoup de souplesse à l’usager qui voudrait définir lui-même ce qu’il
considère comme un balayage.

Dans cette section, nous montrons comment le langage que nous proposons permet
de définir de façon claire et concise ce que nous considérons comme un balayage. Nous
voyons aussi comment notre approche permet de définir rapidement de nouveaux types
de balayages, une tâche autrement plus ardue lorsque l’on utilise les préprocesseurs.
Chapitre 3. Détection d’intrusions et analyse passive 79

step syn1 : tcp any any -> any any (flags :S ;)


step syn2 : tcp any any -> any any (flags :S ;)
match : syn1.sip=syn2.sip ; syn1.dip=syn2.dip ; syn1.dport !=syn2.dport ;
timeout : syn1.timestamp + 2sec ;
step syn3 : tcp any any -> any any (flags :S ;)
match : syn1.sip=syn3.sip ; syn1.dip=syn3.dip ;
syn1.dport !=syn3.dport ; syn2.dport !=syn3.dport ;
output : assert(alert(portscan,syn1.sip,syn1.dip)).
timeout : syn2.timestamp + 2sec ;

Fig. 3.2 – Balayage SYN.

La méthode de balayage TCP connect() est sans doute la plus répandue et la plus
facile. L’attaquant n’a qu’à effectuer une tentative de connection sur le port cible et
simplement vérifier si elle est acceptée ou pas. Bien que présentant certains avantages
de rapiditié et de simplicité, cette méthode n’est cependant pas à privilégier car elle se
détecte assez facilement par consultation des fichiers de log [7]. Pour éviter ce dernier
problème, on privilégiera une méthode plus subtile, comme par exemple le balayage
SYN. Dans un balayage SYN, en cas de réception d’un SYN-ACK, on enverra sim-
plement un RST au lieu d’un ACK comme on devrait le faire pour finir d’établir la
connection. Le système ne gardera ainsi généralement pas trace de la connection.

Dans un cas comme dans l’autre, dans les situations où l’attaquant voudra balayer
plusieurs ports sur une même machine, la signature montrée à la figure 3.2 devrait être
en mesure de détecter le comportement malicieux de l’attaquant. Elle spécifie que si trois
demandes de connection, addressées au même hôte et provenant du même hôte, mais
faites sur des ports cibles différents, survenaient à moins de deux secondes d’intervalle,
il faudrait alors ajouter à la base de connaissances qu’une attaque de portscan vient
d’avoir lieu.

La limite de trois connections, de même que le délai de deux secondes, sont tout à
fait arbitraires. En fait, il faut faire un compromis entre la possibilité de faux négatifs
et l’engorgement de la mémoire. L’important ici est de remarquer qu’une modification
mineure du fichier de signatures permet d’ajuster rapidement notre politique de sécurité.
Par exemple, on peut modifier la signature précédente afin de redéfinir la notion de
balayage de ports au niveau réseau. Certains attaquants peuvent en effet ne pas être
intéressés à attaquer une machine en particulier, mais plutôt un service donné, comme
dans le cas de certains serveurs warez qui exploitent des comptes FTP anonymous
laissés négligemment actifs par certains administrateurs réseau lors de l’installation du
système. Dans ce cas, la politique ne sera pas d’interdire la connection sur différents
Chapitre 3. Détection d’intrusions et analyse passive 80

ports du même hôte, mais plutôt sur le même port de différents hôtes.

De la même façon, on pourrait aussi définir un balayage Ping comme étant une
certaine quantité de requêtes ICMP echo provenant du même hôte vers des hôtes
différents dans un court laps de temps.

3.4 Acquisition passive d’information

Comme nous l’avons déjà dit, l’analyse passive regroupe un ensemble de techniques
permettant d’acquérir de l’information sur un réseau seulement en écoutant le trafic
circulant à quelques endroits stratégiques, par exemple à la sortie du routeur principal.
D’une certaine façon, on peut la considérer comme une généralisation de la détection
d’intrusions au niveau réseau. En détection d’intrusions, on est seulement intéressé à
savoir si oui ou non un certain type de comportement, considéré comme malicieux, sur-
vient. En analyse passive, on sera plutôt intéressé à déduire le maximum d’information
du trafic (normal aussi bien que malicieux) que l’on voit circuler sur le réseau. Cette
information peut concerner autant la configuration des hôtes que l’activité se déroulant
en général sur le réseau, comme par exemple les sessions TCP actives.

Encore une fois, chacune des techniques mentionnées dans cette section peut se faire
aussi par programmation. Par exemple, dans le cas de Snort, la détection des sessions
actives est déjà prise en compte par le préprocesseur Flow. Cependant, il ne faut pas
perdre de vue que l’idée ici est de se munir d’un langage de spécification qui soit assez
général pour : minimiser le plus possible le temps de développement, maximiser le plus
possible la lisibilité et la réutilisation, et permettre un maximum de corrélation entre
les composantes, via la base de connaissances.

3.4.1 Ports TCP ouverts et sessions actives

Les ports TCP ouverts des hôtes, de même que leurs sessions actives, constituent un
exemple simple mais important d’information qu’il est intéressant d’acquérir de façon
passive. En effet, on ne peut pas toujours se fier à la façon dont l’on croit avoir configuré
les hôtes de notre réseau, et cela pour plusieurs raisons. Il se peut (1) qu’un utilisateur
malveillant ou insouciant ait lui-même installé une application qui va à l’encontre de
nos politiques de sécurité, comme par exemple Kazaa, (2) qu’un utilisateur ait reçu par
courriel un virus ayant pour effet de démarrer un serveur illicite (cheval de Troie), (3)
Chapitre 3. Détection d’intrusions et analyse passive 81

step syn : tcp any any -> any any (flags :S ;)


step synack : tcp any any -> any any (flags :SA ;)
match : syn.sip=synack.dip ; syn.dip=synack.sip ;
syn.sport=synack.dport ; syn.dport=synack.sport ;
output : assert(port(syn.dip,syn.dport,"open")).
timeout : syn.timestamp+2sec ;
step ack : tcp any any -> any any (flags :A ;)
match : syn.sip=ack.sip ; syn.dip=ack.dip ; syn.sport=ack.sport ; syn.dport=ack.dport ;
output : assert(session(syn.sip,syn.dip,syn.sport,syn.dport)),
assert(session(syn.dip,syn.sip,syn.dport,syn.sport)).
timeout : synack.timestamp+2sec ;
output : assert(alert("syncan",syn.sip,syn.dip)).

Fig. 3.3 – Poignée de main TCP.

step syn : tcp any any -> any any (flags :S ;)


step rst : tcp any any -> any any (rst :R ;)
match : syn.sip=rst.dip ; syn.dip=rst.sip ; syn.sport=rst.dport ; syn.dport=rst.sport ;
output : assert(port(syn.dip,syn.dport,closed)).
timeout : syn.timestamp+2sec ;

Fig. 3.4 – Ports TCP fermés.

step fin : tcp any any -> any any (flags :F ;)


output : not(session(fin.sip,fin.dip,fin.sport,fin.dport)),
assert(alert("finscan",fin.sip)).
output : retract(session(fin.sip,fin.dip,fin.sport,fin.dport)).

step rst : tcp any any -> any any (flags :R ;)


output : retract(session(rst.sip,rst.dip,rst.sport,rst.dport)).
output : retract(session(rst.dip,rst.sip,rst.dport,rst.sport)).

Fig. 3.5 – Fermeture d’une session TCP.


Chapitre 3. Détection d’intrusions et analyse passive 82

qu’un visiteur avec un ordinateur portable, en dehors de notre contrôle, ne respecte pas
notre politique de sécurité.

Pour découvrir passivement quels sont les ports TCP ouverts sur nos hôtes, on n’a
qu’à écouter les demandes de connection. Lorsque les demandes sont acceptées par le
serveur, on peut conclure que le port est ouvert. Ce n’est cependant qu’à la troisième
étape de la connection, lors de la réception de confirmation, que l’on peut conclure que
réellement il y a une connection en cours. Dans le cas où cette confirmation n’a pas
lieu, il y a fortement lieu de penser qu’une attaque de type synscan est en cours. À la
figure 3.3, on peut voir comment notre langage supporte ces techniques d’acquisition
d’information. On remarque que l’ajout, dans la base de connaissances, de l’attaque
synscan se fait après l’écoulement du délai (timeout).

3.4.2 Ports TCP fermés

Pour détecter quels sont les ports TCP fermés, la technique est relativement sem-
blable à celle utlisée pour les ports ouverts. Dans ce cas-ci, c’est cependant le RST, au
lieu du SYN-ACK, qui nous permettra de conclure. Le scénario correspondant à cette
technique se trouve à la figure 3.4.

3.4.3 Sessions fermées et balayage FIN

Nous venons de montrer comment il est possible de détecter qu’une session est
active entre deux hôtes. Pour la considérer comme étant terminée, il faut attendre
qu’un paquet FIN soit envoyé par un des deux hôtes. La connection sera alors fermée
dans une direction, et il faudra attendre l’envoi d’un autre paquet FIN pour fermer
l’autre direction. Dans le cas où il n’y avait aucune connection active, il faut signaler
une alerte de finscan. En effet, la méthode de balayage FIN est encore plus subtile que
celles précédemment mentionnées. Il s’agit d’envoyer un paquet de fin de connection
sans en avoir établie auparavant. Le comportement normal d’un port ouvert est de ne
pas répondre, alors que celui d’un port fermé sera d’envoyer un paquet RST. Aussi, il
ne faut pas oublier que certaines connections peuvent aussi se terminer par un RST.
Dans ce cas, les deux sens de la connection sont fermés en même temps. Le scénario de
la figure 3.5 implante ces techniques d’acquisition.
Chapitre 3. Détection d’intrusions et analyse passive 83

step syn : tcp any any -> any any (flags :S ; tcpopts :MTW)
step synack : tcp any any -> any any (flags :SA ; tcpopts :M ; ttl :64 ; window :12)
match : syn.sip=synack.dip ; syn.dip=synack.sip ;
syn.sport=synack.dport ; syn.dport=synack.sport ;
output :assert(os(syn.dip, "Free BSD 3.0")),
assert(os(syn.dip, "FreeBSD 3.1")),
...
assert(os(syn.dip, "FreeBSD 4.3")).
timeout : syn.timestamp + 2sec ;

Fig. 3.6 – Détection d’un système d’exploitation FreeBSD 3.0 à 4.3.

3.4.4 Vulnérabilités et systèmes d’exploitation

La prise en compte du contexte dans lequel survient une attaque permet de dimi-
nuer le nombre de faux-positifs générés par un système de détection d’intrusions. Par
exemple, dans le cas de Snort, l’ajout du préprocesseur Flow a permis de diminuer
le nombre de faux-positifs en spécifiant que certains attaques doivent survenir dans
le contexte d’une session TCP active pour réussir. La prise en compte du contexte
peut cependant aller beaucoup plus loin que la simple vérification des sessions TCP.
Par exemple, la plupart des attaques utilisent des vulnérabilités connues de certains
systèmes. La connaissance de la présence éventuelle de ces vulnérabilités peut alors per-
mettre de diminuer le nombre de faux-positifs en vérifiant que le système attaqué est
bel et bien vulnérable à l’attaque identifiée.

La prise d’empreintes de systèmes d’exploitation regroupe un ensemble de techniques


exploitant les différences entre les différentes implantations de la suite de protocoles
TCP/IP dans le but d’identifier le système d’exploitation des machines connectées à un
réseau. Le logiciel nmap [71] utilise huit tests actifs (générant du trafic) différents per-
mettant de différencier plus de 500 systèmes d’exploitation différents. D’autres logiciels,
tels que POF [70] (pour Passive OS Fingerprinting) utilisent des tests comparables dans
un contexte passif (ne générant pas de trafic). Les tests utilisés par POF fonctionnent
tous sur la base d’un unique paquet, ne profitant ainsi pas de l’information contextuelle
permettant d’effectuer une détection plus précise.

À la figure 3.6, on montre comment on peut détecter, à l’aide du langage que nous
avons développé, un système d’exploitation FreeBSD. Cette signature a été rédigée à
partir d’une base de données de signatures [73] ayant été mise au point au Centre de
Recherches sur les Communications. La deuxième étape du scénario (le SYN-ACK) est
Chapitre 3. Détection d’intrusions et analyse passive 84

alert tcp any any -> any 21 (msg :"FTP EXPLOIT wu-ftpd 2.6.0 site
exec format string overflow FreeBSD" ; flow :to_server,established ;
content :"1|C0|PPP|B0| |CD 80|1|DB|1|C0|" ; reference :bugtraq,1387 ;sid :343 ;)

Fig. 3.7 – Règle Snort numéro 343.

step exploit : tcp any any -> any 21 (content :"1|C0|PPP|B0| |CD 80|1|DB|1|C0|" ;)
match : session(exploit.sip,exploit.dip,exploit.sport,exploit.dport) ;
os(exploit.dip, "Free BSD 4.3") ;
output :assert(alert("FTP EXPLOIT", exploit.sip, exploit.dip)).

Fig. 3.8 – Réduction des faux-positifs à l’aide de la détection de vulnérabilités.

un paquet pouvant être envoyé par 27 systèmes d’exploitation différents. Cependant,


lorsque le SYN-ACK est envoyé en réponse au SYN correspondant à la première étape,
seulement 11 des 27 systèmes d’exploitation continuent d’envoyer ce SYN-ACK. Le
langage que nous avons développé permet donc une détection de système d’exploitation
plus précise que celle faite par les outils n’utilisant qu’un seul paquet.

À la figure 3.7, se trouve une version simplifiée d’une règle Snort permettant de
détecter une tentative d’exploiter une vulnérabilité du serveur FTP de certaines versions
du système d’exploitation FreeBSD. Cette vulnérabilité est référenciée (entre autres) par
l’organisme Bugtraq [74] par le numéro 1387. En consultant le site web de Bugtraq, on
peut savoir quelles sont les versions de FreeBSD qui sont vulnérables à cette attaque.
Il est alors possible, comme on le montre à la figure 3.8, d’utiliser la connaissance
que nous avons du système d’exploitation de la machine attaquée pour vérifier s’il
est vraisemblable que l’attaque ait réussi. On voit aussi comment on peut replacer
l’utilisation du préprocesseur Flow pour vérifier la présence d’une session TCP active.

3.4.5 Adresses du routeur

Lorsque l’on voit passer plusieurs paquets ayant la même adresse MAC source, mais
des adresses IP source différentes, on peut conclure que l’hôte ayant cette adresse MAC
agit comme routeur. Une fois que l’on connaı̂t l’adresse MAC du routeur, on peut dé-
duire son adresse IP en regardant les réponses aux requêtes ARP qui lui sont adressées.
Ces techniques sont illustrées à la figure 3.9.
Chapitre 3. Détection d’intrusions et analyse passive 85

step anyip1 : ip any any -> any any


step anyip2 : ip any any -> any any
match : anyip1.smac=anyip2.smac ; anyip1.sip !=anyip2.sip ;
timeout : anyip1.timestamp + 10sec ;
step anyip3 : ip any any -> any any
match : anyip1.smac=anyip3.smac ; anyip1.sip !=anyip3.sip ; anyip2.sip !=anyip3.sip ;
output : assert(gatewaymac(anyip1.smac)).
timeout : anyip2.timestamp + 10sec ;

step arpreply : arp any any -> any any (opcode=2 ;)


match : gatewaymac(arpreply.smac) ;
output : assert(gatewayip(arpreply.tprotoadr)).

Fig. 3.9 – Adresses du routeur.

step arpreply : arp (opcode=2 ;)


output : assert(macip(arpreply.thdradr,arpreply.tprotoadr)).

gatewayip(ip) :- macip(X,ip),gatewaymac(X).

Fig. 3.10 – Inférence de nouvelles connaissances.

3.4.6 Inférence de nouvelles connaissances

L’exemple que nous venons tout juste de voir est généralisable. On peut le formuler
ainsi : si on sait que la machine ayant l’adresse MAC X a la caractéristique c, et que la
machine ayant l’adresse IP Y a l’adresse MAC X, alors la machine ayant l’adresse IP
Y a aussi la caractéristique c. Il s’agit presque d’une lapalissade, mais dans un contexte
où l’on a besoin de rechercher de l’information à partir de différentes clés, comme c’est
le cas lorsque l’on doit utiliser le peu d’information qui se trouve dans un paquet donné,
il est souhaitable d’avoir le plus de transparence possible. C’est pourquoi, au lieu de
la règle que nous avons définie ci-haut, il serait préférable, par exemple, d’utliser les
réponses aux requêtes ARP pour emmagasiner les associations entre les adresses IP et
les adresses MAC.

Par la suite, on peut définir l’adresse IP d’un routeur comme étant une adresse IP
associée à l’adresse MAC d’un routeur. Cette inférence est faite au niveau de la base de
connaissances, et c’est pourquoi la dernière ligne de la figure 3.10 est écrite en Prolog,
Chapitre 3. Détection d’intrusions et analyse passive 86

et non dans notre langage de signatures. Cette signature consitue une version améliorée
de la seconde signature présentée à la figure 3.9. En effet, la seconde signature de la
figure 3.9 implique que la détection de l’adresse MAC du routeur doit être faite avant
de détecter quelle est l’adresse IP lui étant associée. Avec la signature de la figure 3.10,
l’ordre dans lequel les deux connaissances sont acquises n’a pas d’importance.

Cependant, on voit que cette règle d’inférence n’est pas encore assez générale, car il
faudrait la redéfinir pour chacun des types d’information. Comme on le voit, la struc-
turation et la représentation de l’information à acquérir, dans une optique où on veut
en inférer de nouvelles, est une étape très importante et qui, en fait, constitue en soi un
défi sérieux à relever.

3.5 Présentation formelle du langage

Maintenant que nous avons une meilleure intuition des objectifs poursuivis par le
langage que nous avons développé, nous allons voir comment il est possible d’en faire
une étude formelle à l’aide des notions que nous avons vues au chapitre 1. Cette étude
nous permettra de mieux percevoir les limites de notre langage, et de le critiquer en
regard des critères dont nous avons donné la liste à la table 1.9. Quatre aspects du
langage seront donc traités dans cette section, soit le modèle sur lequel il travaille, sa
syntaxe, sa sémantique, et son algorithme de vérification.

3.5.1 Modèle

Le modèle sur lequel nous travaillons, celui d’une trace de trafic, est généralisable
à celui de trace d’enregistrements. Il s’agit essentiellement du même modèle que celui
utilisé par ASAX et LogWeaver. Le concept d’enregistrement se distingue de celui d’état,
utilisé par exemple avec LTL, de par le fait qu’il permet d’associer des valeurs à des
étiquettes, et ainsi de comparer les valeurs associées aux étiquettes de différents états.
Avec le concept d’état traditionnellement utilisé, qui consiste en un sous-ensemble d’un
ensemble de constantes propositionnelles P , la comparaison entre les différents états se
fait beaucoup moins naturellement (bien que théoriquement possible).

Notre modèle se distingue cependant de ceux de ASAX et LogWeaver par deux


hypothèses importantes que nous faisons relativement au temps : (1) il est toujours
possible d’associer un temps à un enregistrement donné, et (2) le temps est toujours
Chapitre 3. Détection d’intrusions et analyse passive 87

i τ sip dip tcpsport tcpdport syn ack


0 2.0 192.168.0.100 192.168.0.101 45678 23 1 0
1 3.5 192.168.0.101 192.168.0.100 23 45678 1 1
2 4.9 192.168.0.100 192.168.0.101 45678 23 0 1
3 7.0 192.168.0.108 192.168.0.109 56789 21 1 0
4 7.5 192.168.0.109 192.168.0.108 21 56789 1 1
5 9.7 192.168.0.112 192.168.0.114 22 34567 0 0
... ... ... ... ... ... ... ...

Fig. 3.11 – Exemple de trace.

croissant, c’est-à-dire que les enregistrements se situent dans la trace dans le même
ordre que celui des événements qu’ils représentent. Ces idées se formalisent donc dans
la définition suivante :

Définition 3.1 Un enregistrement est une fonction dont le domaine est un ensemble
d’étiquettes, aussi appelées champs, et dont le codomaine est un ensemble de valeurs
numériques (par exemple N). Une de ces étiquettes, τ , représente le temps. Une trace
σ est une suite infinie d’enregistrements. Par σ(i), on désigne le ieme enregistrement, et
par σ i , on désigne le suffixe de σ commençant à σ(i). Le temps est croissant, c’est-à-dire
que ∀i.σ(i).τ < σ(i + 1).τ .

À la figure 3.11, on peut voir un exemple de trace. La première colonne représente


l’index de l’enregistrement, la deuxième le temps qui lui est associé, les quatre suivantes
les adresses et les ports source et destination, et les deux dernières représentent les
drapeaux syn et ack. En réalité, un paquet peut contenir beaucoup plus de champs,
mais ceux-ci sont suffisants pour l’étude des exemples qui suivent.

3.5.2 Syntaxe

La syntaxe abstraite de notre langage se trouve à la table 3.2. La première partie


de la syntaxe permet d’exprimer des scénarios comme des séquences d’événements. Le
scénario le plus simple est celui où il ne se passe rien de spécial (tt). Le scénario
hid, φit ψ débute par un paquet id ayant la caractéristique φ, et se poursuit par le
scénario ψ. Le scénario ¬ψ est celui où le scénario ψ ne survient pas, et le scénario
ψ1 ∧ ψ2 est celui où les scénarios ψ1 et ψ2 surviennent tous les deux. La deuxième partie
de la syntaxe concerne les caractéristiques propres à un paquet. Il s’agit essentiellement
du calcul propositionnel, sauf que les constantes p (que nous appellerons désormais
Chapitre 3. Détection d’intrusions et analyse passive 88

ψ ::= tt | hid, φit ψ | ¬ψ | ψ1 ∧ ψ2 (Formules pour traces)


φ ::= p | ¬φ | φ1 ∧ φ2 (Formules pour paquets)
t ::= id.τ + δ | ∞ (Délais)

Tab. 3.2 – Syntaxe.

expressions) peuvent, comme nous le verrons, contenir des variables faisant référence aux
différents champs des enregistrements consituant les scénarios. La troisième partie de la
syntaxe permet d’exprimer des délais. Un délai peut être relatif à un événement id passé
(id.τ + δ), ou infini (∞). D’un point de vue expressivité, l’ajout de ces délais n’apporte
rien, car il aurait été possible de les représenter par une expression p. Cependant,
comme ils jouent un rôle crucial dans l’algorithme de vérification en ligne que nous
présenterons plus loin, nous avons jugé bon de spécifier ceux-ci au niveau même de
la syntaxe. Finalement, remarquons que les opérateurs temporels que nous utilisons
constituent des cas particuliers de ceux utilisés avec MTL.

hsyn, φ1 i∞ hsynack, φ2 isyn.τ +2sec hack, φ3 isynack.τ +2sec tt

avec

φ1 := snort(”tcp (flags :S ;)”)


φ2 := snort(”tcp (flags :SA ;)”) ∧syn.sip = synack.dip ∧ syn.dip = synack.sip
∧syn.sport = synack.dport ∧ syn.dport = synack.sport
φ3 := snort(”tcp (flags :A ;)”) ∧syn.sip = ack.sip ∧ syn.dip = ack.dip
∧syn.sport = ack.sport ∧ syn.dport = ack.dport

Fig. 3.12 – Formule représentant une poignée de main TCP.

À la figure 3.12, on peut voir comment le scénario représentant une poignée de


main TCP, étudié plus tôt dans le présent chapitre, se traduit dans la syntaxe abstraite
du langage. Il s’agit d’une suite de trois enregistrements, dont les caractéristiques sont
données par les formules φ1 , φ2 et φ3 . La formule snort(sig) s’évalue à vrai pour σ(i) si et
seulement si l’engin de détection de Snort évalue à vrai la signature sig dans le contexte
du paquet σ(i). On remarque que chacun des éléments du scénario ont été traduits
dans la formule, exception faite des clauses output :.... Ceci est dû au fait que la
sémantique de cette partie des scénarios est de nature impérative, et non déclarative.
On la retrouvera donc au niveau de l’algorithme de vérification du langage, mais elle
n’est pas directement traductible en logique temporelle.
Chapitre 3. Détection d’intrusions et analyse passive 89

σ |= tt
σ |= hid, φit ψ ssi ∃i.σ(i).τ <Eval(t) et Eval(φ[σ(i).n/id]) =vrai
et σ i+1 |= ψ[σ(i).n/id]
σ |= ¬ψ ssi σ 6|= ψ
σ |= ψ1 ∧ ψ2 ssi σ |= ψ1 et σ |= ψ2

Eval(p) ∈ {vrai,faux}
Eval(¬φ) = ¬Eval(φ)
Eval (φ1 ∧ φ2 ) = Eval(φ1 )∧Eval(φ2 )

Tab. 3.3 – Sémantique.

3.5.3 Sémantique

La sémantique du langage est donnée à la table 3.3. Les quatres premières règles
définissent la règle de satisfiabilité qui précise quand est-ce qu’une trace σ satisfait une
formule ψ. Toute trace satisfait la formule tt. Une trace σ satisfait la formule hid, φit ψ
si elle admet, avant que le délai t ne soit écoulé, un paquet σ(i) qui, une fois substituées
toutes les occurrences de la variable id par σ(i).n (le nom de σ(i)) satisfait φ, et que le
reste de la trace à partir de ce paquet, σ i+1 , satisfait la formule ψ dans laquelle toutes
les occurrences de la variable id sont aussi substituées par σ(i).n. Une trace satisfait la
formule ¬ψ si elle ne satisfait pas la formule ψ. Elle satisfait la formule ψ1 ∧ ψ2 si elle
satisfait à la fois ψ1 et ψ2 .

Les trois dernières règles étendent la relation de satisfiabilité pour préciser comment
évaluer, à l’aide de la fonction Eval, une formule de paquets φ. Le cas intéressant est
celui d’une expression p. Comme cette expression contient des variables s’identifiant à
des paquets, comme par exemple syn.sip = synack.dip, la fonction Eval substitue les
variables par les valeurs contenues dans le modèle, et évalue l’expression obtenue en
fonction des opérateurs (par exemple, des opérateurs de comparaison) de l’expression.
Les cas de la négation et de la conjonction se traitent comme d’habitude. L’évaluation
de la négation d’une formule retourne la valeur opposée de l’évaluation de cette formule,
et l’évaluation d’une conjonction de formules retourne la conjonction de l’évaluation de
ces formules.

Par exemple, la trace montrée à la figure 3.11 satisfait la formule de la figure 3.12
car, après substitution de syn par σ(0), de synack par σ(1) et de ack par σ(2), les
formules de paquet φ1 , φ2 et φ3 deviennent :
Chapitre 3. Détection d’intrusions et analyse passive 90

φ1 := snort(”tcp (flags :S ;)”)


φ2 := snort(”tcp (flags :SA ;)”) ∧192.168.0.100 = 192.168.0.100
∧192.168.0.101 = 192.168.0.101 ∧ 45678 = 45678 ∧ 23 = 23
φ3 := snort(”tcp (flags :A ;)”) ∧192.168.0.100 = 192.168.0.100
∧192.168.0.101 = 192.168.0.101 ∧ 45678 = 45678 ∧ 23 = 23

3.5.4 Algorithme

ψ ::= hid, φit tt | ¬hid, φit tt | hid, φit ψ (Formules pour traces)
φ ::= p | ¬φ | φ1 ∧ φ2 (Formules pour paquets)
t ::= id.τ + δ | ∞ (Délais)

Tab. 3.4 – Syntaxe du sous-ensemble associé au langage de signatures.

Le langage de scénarios que nous avons décrit à la section 3.2 ne permet pas de
représenter toutes les formules du langage abstrait que nous venons de définir. Plus
précisément, il permet seulement de représenter le sous-ensemble engendré par la syn-
taxe de la table 3.4. Les deux scénarios de base sont ceux où un paquet donné survient
(hid, φit tt) ou ne survient pas (¬hid, φit tt) avant un délai t donné. On compose en-
suite les scénarios plus complexes en leur ajoutant des étapes préalables (hid, φit ψ). Il
n’est pas possible, à l’aide du langage de scénarios, de représenter une conjonction de
scénarios ou encore l’absence de toute une suite de paquets.

Les variables id correspondent aux éléments hidi du langage de scénarios identifiant


les différentes étapes. Les formules de paquets φ correspondent aux clauses match. La
présence d’une clause output après une clause match indique que le scénario de base
utilisé est la présence d’un paquet (hid, φit tt), et la présence d’une clause output après
une clause timeout indique que le scénario de base utilisé est l’absence d’un paquet
(¬hid, φit tt). Les clauses output ne correspondent à rien dans le langage abstrait.
Elles interviennent seulement au niveau de l’algorithme de vérification, en nous disant
quelles sont les actions qui doivent être posées lorsque l’occurrence d’un scénario donné
a été identifiée. Étant donné un fichier contenant plusieurs signatures, nous générons
une spécification. C’est cette spécification que l’on donnera en entrée à l’algorithme de
vérification.

Définition 3.2 Une spécification est un ensemble de couples de la forme hψ ⇒ acti,


où ψ est une formule engendrée par la syntaxe de la table 3.4, et act est une action
représentant l’ajout ou le retrait d’une information dans la base de connaissances. Cette
action est posée à chaque fois que l’on se rend compte que ψ est satisfaite par la trace.
Chapitre 3. Détection d’intrusions et analyse passive 91

hsyn, φ1 i∞ hsynack, φ2 isyn.τ +2sec tt


⇒ assert(port(syn.dip, syn.dport, ”open”))
hsyn, φ1 i∞ hsynack, φ2 isyn.τ +2sec hack, φ3 isynack.τ +2sec tt
⇒ assert(session(syn.sip, syn.dip, syn.sport, syn.dport)),
assert(session(syn.dip, syn.sip, syn.dport, syn.sport))
hsyn, φ1 i∞ hsynack, φ2 isyn.τ +2sec ¬hack, φ3 isynack.τ +2sec tt
⇒ assert(alert(”syncan”, syn.sip, syn.dip))

avec

φ1 := snort(”tcp (flags :S ;)”)


φ2 := snort(”tcp (flags :SA ;)”) ∧syn.sip = synack.dip ∧ syn.dip = synack.sip
∧syn.sport = synack.dport ∧ syn.dport = synack.sport
φ3 := snort(”tcp (flags :A ;)”) ∧syn.sip = ack.sip ∧ syn.dip = ack.dip
∧syn.sport = ack.sport ∧ syn.dport = ack.dport

Fig. 3.13 – Spécification associée au scénario d’une poignée de main TCP.

Il est à noter qu’à un scénario donné peuvent correspondre plusieurs couples hψ ⇒


acti. Par exemple, le scénario représentant l’établissement d’une session TCP (figure 3.3,
page 81) donne les trois couples que nous pouvons voir à la figure 3.13.

L’algorithme de vérification, pour une trace et une spécification données, est montré
à la table 4.8. Les opérateurs + et − représentent l’ajout et le retrait d’un élément
d’un ensemble. L’ensemble k représente la base de connaissances, et e l’ensemble des
scénarios à reconnaı̂tre. Ce dernier évolue à mesure que les préfixes des formules φ de
la spécification originale sont reconnus.

Au début, le nombre de scénarios à reconnaı̂tre est donc égal au nombre d’éléments


de la spécification, mais comme la reconnaissance des enregistrements à la tête des scé-
narios entraı̂ne la duplication de ceux-ci (comme dans le cas des actions non-consuming
de STAT) la taille de l’ensemble augmente en cours de route. Les éléments de e sont
cependant retirés à mesure que les délais sont écoulés (lignes 8, 12 et 16).

La substitution textuelle, à mesure que les étapes sont reconnues, est appliquée
non-seulement au reste de la formule, comme spécifié par la sémantique, mais aussi
aux actions à prendre lors de la reconnaissance (lignes 9 et 13). Par exemple, l’action
assert(session(syn.sip, . . .) se transforme ainsi en assert(session(192.168.0.100, . . .).

À la figure 3.14, on peut voir un exemple de trace d’exécution de l’algorithme dans


le cas ou la trace utilisée est celle de la figure 3.11 (page 87) et la spécification est
Chapitre 3. Détection d’intrusions et analyse passive 92

01.procédure verif(ψ, act, s, k, e)


02.entrées : ψ : une formule, act : une action à prendre,
03. s : un enregistrement, k : la base de connaissances, e : un ensemble
04.sorties : k : la connaissance mise à jour, e : l’ensemble des scénarios mis à jour
05.début
06.selon la forme de φ :
07.cas hid, φit tt :
08. si s.τ >Eval(t) alors e := e − hψ ⇒ acti
09. sinon si Evalk (φ[s/id]) alors k := act[s/id](k)
10.cas ¬hid, φit tt :
11. si s.τ >Eval(t) alors
12. e := e − hψ ⇒ acti
13. k := act(k)
14. sinon si ¬Evalk (φ[s/id]) alors e := e − hψ ⇒ acti
15.cas hid, φit ψ1 :
16. si s.τ >Eval(t) alors e := e − hψ ⇒ acti
17. sinon si Evalk (φ[s/id]) alors e := e + hψ1 [s/id] ⇒ act[s/id]i
18.fin
19.
20.procédure moniteur(σ,spec)
21.entrées : σ : une trace, spec : une spécification
22.sortie : k : la connaissance acquise à partir de σ
23.début
24. k := ∅
25. e0 := ∅
26. pour chaque hψ ⇒ acti ∈ spec
27. e0 := e0 + hψ ⇒ acti
28. pour i allant de 0 à |σ|
29. e := e0
30. pour chaque hψ ⇒ acti ∈ e
31. verif(ψ, act, σ(i), k, e0 )
32.fin

Tab. 3.5 – Algorithme de vérification.


Chapitre 3. Détection d’intrusions et analyse passive 93

i actions posées |e|


0 6
1 assert(port(192.168.0.101, 23, ”open”)) 8
2 assert(session(192.168.0.100, 192.168.0.101, 45678, 23)), 4
assert(session(192.168.0.101, 192.168.0.100, 23, 45678))
3 6
4 assert(port(192.168.0.108, 21, ”open”)) 8
5 assert(alert(”syncan”, 192.168.0.108, 192.168.0.109)) 3
... ... ...

Fig. 3.14 – Exemple de trace d’exécution de l’algorithme.

celle de la figure 3.13. Dans la colonne du centre sont affichées les actions posées sur
la base de connaissances pendant le traitement de chaque paquet, et dans celle de
droite le nombre de scénarios en cours de reconnaissance. Au paquet 0, une demande de
connection TCP, ne provoque aucun changement sur la base de connaissances. Chacun
des scénarios est cependant dupliqué, en subsituant id1 par σ(0). L’ensemble e contient
donc six éléments. Lorsque, au paquet 1, la connection est acceptée, on acquière la
connaissance que le port 23 de la machine 192.168.0.101 est ouvert. Seulement deux
scénarios sont dupliqués, pour un total de 8. Au paquet 2, la connection est complétée
et la session est ouverte dans chacune des directions. Le délai des trois premiers scénarios
copiés est écoulé, et ils sont donc retirés de la base de l’ensemble e. De plus, la condition
pour un synscan ayant été violée, la copie du scénario correspondant est aussi retirée.
Aucun scénario n’est dupliqué, on se retrouve donc avec un ensemble e de taille 4.
Le paquet 3 n’amène aucun changement sur la base de connaissances. Cependant, la
demande de connection fait en srte que les trois scénarios de base sont dupliqués. Le
délai du scénario de reconnaissance d’une poignée de main TCP pendant étant écoulé,
celui-ci est retiré de e, qui se retrouve de nouveau avec 6 éléments. Au paquet 4, la
connection est acceptée. Deux scénarios sont ainsi dupliqués, et on apprend que le port
21 de la machine 192.168.0.109 est ouvert. Au paquet 5, tous les délais sont écoulés. On
apprend ainsi qu’une attaque de type synscan vient d’avoir lieu. Aucun scénario n’est
dupliqué.

Nous terminons cette section par la formulation et la démonstration du théorème


de validité de l’algorithme de vérification. Par version de act, nous entendons l’action
act à laquelle l’opérateur de substitution textuelle peut avoir été appliqué.
Chapitre 3. Détection d’intrusions et analyse passive 94

Théorème 3.3 (Validité de l’algorithme de vérification) Si pour toute expression


p et pour toutes bases de connaissances k1 et k2 , Evalk1 (p) = Evalk2 (p), alors, pour tout
couple hψ, acti d’une spécification donnée, σ |= ψ si et seulement si lors du traitement
(appel de la procédure verif) d’un certain σ(i), l’action act (ou une de ses versions) est
commise.

La condition Evalk1 (p) = Evalk2 (p) provient du fait que l’évaluation d’une expres-
sion p peut dépendre du contenu de la base de connaissances. Comme nous n’avons
pas tenu compte de la base de connaissances dans la définition de la syntaxe et de la
sémantique abstraites du langage, nous devons énoncer le théorème de validité comme
si celle-ci n’avait aucun effet sur l’exécution de l’algorithme. Si pour toute expression p
et pour toutes bases de connaissances k1 et k2 , Evalk1 (p) = Evalk2 (p), alors l’expression
Eval(p) est bien définie. On peut maintenant passer à la démonstration du théorème.

Démonstration :

On procède par induction sur la forme de φ :

cas hid, φit tt :

⇒Supposons que ∃i.σ(i).τ <Eval(t) et |= φ[σ(i)/id]. Alors, par l’hypothèse de crois-


sance posée sur le modèle, act est commise (ligne 9) avant que le couple hψ ⇒ acti ne
soit retiré de e (ligne 8).

⇐Réciproquement, si il n’existe pas de tel j, le couple hφ ⇒ acti est retiré de e (ligne 8)


sans que act ne soit commise (ligne 9).

cas ¬hid, φit tt :

⇒Supposons que @i.σ(i).τ <Eval(t) et Eval(φ[σ(i)/id]). Encore une fois, par l’hypo-
thèse de croissance, act sera commise (ligne 13) avant que le couple hψ ⇒ acti ne soit
retiré de e (ligne 14).

⇐Si, au contraire, il existe un tel i, alors la ligne 13 est exécutée avant la ligne 14.
L’action act ne sera donc pas commise.

cas hid, φit ψ :

⇒Supposons que ∃i.σ(j).τ <Eval(t) et Eval(φ[σ(i)/id]) = vrai et σ(i + 1) |= φ[σ(j)/id].


Alors, comme dans les autres cas, la ligne 17 sera exécutée avant la ligne 16. L’hypothèse
Chapitre 3. Détection d’intrusions et analyse passive 95

d’induction termine le travail.

⇐ Deux cas sont possibles. Soit que 6 ∃i.σ(i).τ <Eval(t) et Eval(φ[σ(i)/id]) = vrai, soit
qu’un tel i existe, mais que σ(i+1) 6|= ψ[σ(i)/id]. Dans le premier cas, la ligne 16 est exé-
cutée avant la ligne 17, et dans le second cas, c’est l’hypothèse d’induction qui entre en
jeu, ce qui termine la démonstration. ¤

3.6 Conclusion

Dans ce chapitre, nous avons vu comment il est possible d’améliorer la structure lo-
gicielle et le langage de signatures d’un système de détection d’intrusions tel que Snort
de façon à ajouter la possibilité de reconnaı̂tre des scénarios d’attaque se déroulant
sur plusieurs paquets et à pouvoir passivement acquérir de l’information. L’information
acquise peut être, en retour, utilisée par le système de détection d’intrusions pour ef-
fectuer une détection plus précise (moins de faux positifs, et moins de faux négatifs).
Elle peut aussi être consultée directement par l’administrateur réseau en cas de besoin.
Avec le langage actuel de Snort, la reconnaissance de scénarios impliquant plusieurs
paquets et l’acquisition passive d’information sont deux tâches impossibles, et doivent
être effectuées par programmation au niveau des préprocesseurs.

Un désavantage majeur, comparativement à Snort, est le temps de traitement d’une


trace et la quantité de mémoire utilisée. Avec son algorithme de reconnaissance de
patrons relativement simple, fonctionnant sur la base d’un seul paquet, Snort travaille
en temps linéaire en fonction de la taille de la trace et l’utilisation de la mémoire est
pratiquement constante. Notre algorithme de reconnaissance de scénarios présente le
même inconvénient que ceux de LogWeaver, Chronicles, Eagle, STAT, IDIOT et ASAX :
la reconnaissance d’étapes d’un scénario engendre (dans certain cas) une duplication de
celui-ci, et le nombre de scénarios en cours de reconnaissance peut donc, dans les cas
pathologiques, augmenter exponentiellement avec la taille de la trace (doubler à chaque
paquet ou enregistrement).

Le langage de signatures que nous avons proposé est basé sur une logique temporelle
avec des opérateurs temporels futurs. Comme l’ont fait les concepteurs de LogWeaver,
nous avons seulement conservé l’opérateur ♦, laissant ainsi tomber U et °. Une diffé-
rence importante entre notre logique et celle de LogWeaver est que nous avons restreint,
comme le proposaient les auteurs, la syntaxe aux scénarios purement linéaires, c’est-à-
dire ceux de la forme ♦p1 (∧♦p2 (∧♦p3 (∧ . . .))). Cette restriction présente l’avantage de
simplifier l’algorithme de vérification, tout en offrant suffisamment d’expressivité pour
Chapitre 3. Détection d’intrusions et analyse passive 96

les besoins liés à la détection d’intrusions. Nous avons cependant laissé la possibilité
d’exprimer le fait qu’il s’écoule, une fois une certaine suite d’événements reconnue, un
certain délai (de longueur finie) durant lequel un événement donné ne survient pas.
Le fait de pouvoir exprimer la non-occurence d’un événement donné durant un certain
laps de temps est un besoin courant en détection d’intrusions auquel il était possible de
subvenir à moindre coût. Dans un même ordre d’idées, une autre différence avec Log-
Weaver est le statut particulier que nous avons donné au temps. Nous avons travaillé
sous l’hypothèse qu’à chaque événement il était possible d’associer un temps et que ces
temps sont croissants suivant le fichier d’audit (ou la trace de trafic).

Un manque important au niveau de l’expressivité du langage de signatures est l’im-


possibilité de spécifier simplement des comportements répétitifs tels que, par exemple,
ceux associés à des attaques de balayage. Toutes les répétitions doivent être spécifiées
une à une, et si, par exemple, le nombre de tentatives de connections TCP à observer
avant de conclure à une attaque de balayage est 50, on doit spécifier un scénario de 50
étapes. Si on exige, de plus, que les ports destinations soient différents pour chacune
des tentatives de connection, le nombre total d’expressions de la forme port1 6= port2
sera de l’ordre de 502 /2 = 1250.

De plus, outre les inconvénients déjà cités, se trouvent aussi ceux reliés au choix d’une
suite d’enregistrements comme modèle de trace. Bien que ce choix de modèle présente
l’avantage, comparativement au modèle propositionnel, de permettre de comparer les
différents événements entre eux, il ne reflète cependant pas adéquatement, comme nous
l’avons déjà remarqué pour ASAX et LogWeaver, le fait que certains champs peuvent
être absents de certains enregistrements ou encore avoir des valeurs multiples.

Finalement, notre architecture à deux niveaux, logique temporelle pour exprimer


les scénarios et base de connaissances pour stocker les connaissances et en déduire de
nouvelles, nous amène à faire les mêmes reproches que nous avons faits à STAT, IDIOT,
BSML et P-BEST quand à la nature déclarative du langage. Les clauses output sont
de nature clairement impérative, et c’est pourquoi nous n’en avons pas tenu compte
dans notre présentation formelle du langage. En y pensant bien, le rôle de la base de
connaissances est entre autres de servir de mémoire pour les scénarios ayant déjà été
reconnus. Elle sert donc à regarder ce qui s’est passé avant. Au chapitre suivant, nous
verrons comment l’utilisation d’opérateurs temporels passés permet d’arriver à un lan-
gage purement déclaratif, et ainsi de faire abstraction, du point de vue du langage de
signatures, de la base de connaissances. Nous verrons aussi comment l’introduction de
l’unification, de même que des tableaux et des opérateurs d’approximants (une ver-
sion faible des opérateurs de points fixes) permet de régler la plupart des problèmes
mentionnés tout en conservant les avantages acquis.
Chapitre 4

Logique d’acquisition de
connaissances

Dans le chapitre précédent, nous avons vu comment il est possible de définir et de


détecter des scénarios d’attaque complexes en utilisant un langage de signatures basé
sur une logique temporelle future. Nous avons aussi vu comment, lors de l’implantation,
il est possible d’utiliser une base de connaissances afin d’acquérir passivement de l’infor-
mation sur le réseau et ainsi d’améliorer la détection d’intrusions. L’ajout de cette base
de connaissances impliquait cependant de faire un compromis sur la nature déclarative
du langage de détection, en ajoutant des clauses output correspondant à des actions à
prendre lors de la détection de certains scénarios ou certaines parties de scénarios. Dans
la section 4.1, nous verrons comment l’opérateur temporel passé S permet de rendre
purement déclaratif le langage de signatures et ainsi de faire abstraction de la base de
connaissances. Mieux encore, nous verrons qu’avec cet opérateur, l’utilisation de l’opé-
rateur ¦ devient superflue. Dans la section 4.2, nous verrons comment l’introduction de
l’unification permet de régler le problème des champs absents ou à valeurs multiples.
Dans la section 4.3, nous verrons comment l’introduction de tableaux, de même que
d’opérateurs d’approximants, permet d’exprimer de façon concise des scénarios à com-
portement répétitif. Finalement, dans la section 4.4, nous présenterons l’algorithme de
vérification en ligne pour la logique développée dans ce chapitre et le démontrerons
comme étant complet et cohérent.
Chapitre 4. Logique d’acquisition de connaissances 98

4.1 Un paradigme passé

Au chapitre précédent, nous avons vu comment il est possible d’utiliser une logique
temporelle future pour exprimer des scénarios d’attaque se déroulant sur plusieurs pa-
quets. L’utilisation d’opérateurs temporels futurs comporte cependant un inconvénient
majeur en ce qui regarde la vérification en ligne : comme nous ne savons jamais ce que
l’avenir nous réserve, nous sommes pratiquement toujours face à une formule en cours
de vérification, ce qui nous amène, au niveau de l’algorithme, à construire des ensembles
de formules partiellement satisfaites et à tenir à jour ces ensembles du mieux que l’on
peut. De plus, à mesure que les scénarios évoluent, nous devons dupliquer les scénarios
(lignes 17 et 20 de l’algorithme de la table 4.8, page 120), utilisant ainsi une quantité
de mémoire sans cesse grandissante. Un autre inconvénient de l’utilisation d’opérateurs
temporels futurs dans un contexte de vérification en ligne réside au niveau de la sé-
mantique donnée à ces opérateurs et de la décidabilité des formules exprimables. Que
penser, par exemple, de la décidabilité de la formule ¦p, qui dit qu’à un certain mo-
ment dans le futur, la proposition p doit être satisfaite ? Si, effectivement, p survient,
on peut affirmer que ¦p est satisfaite, mais tant qu’elle ne survient pas, on ne peut rien
dire. De ce point de vue, la formule ¦p est donc indécidable dans un contexte en ligne
(à moins de travailler sous l’hypothèse que les traces sont finies, comme l’ont fait les
auteurs de [31]). Au chapitre précédent, nous avons contourné le problème en utilisant
l’opérateur ¦t p, permettant de spécifier que p doit survenir avant que le délai t ne se
soit écoulé. Nous avons cependant été obligés de permettre que, au moins à la première
étape, le délai t soit de valeur infinie afin que la vérification puisse toujours continuer.
Par exemple, pour exprimer que il ne doit pas y avoir de p suivi, dans les 2 secondes,
d’un q, on doit utiliser la formule ¬ ¦∞ (p ∧ ¦2 q). Cette permission spéciale laisse un bien
mauvais arrière goût et nous aimerions bien être capables de nous en sortir autrement.

Ces problèmes, engendrés par l’utilisation d’opérateurs futurs, se résolvent tous en


utilisant des opérateurs temporels passés. De plus, l’utilisation de l’opérateur S, plutôt
que de l’opérateur ¨, permet de résoudre les problèmes liés à l’utilisation impérative
de la base de connaissances. En fait, plutôt que d’utiliser tel quel l’opérateur S, nous
utiliserons la construction [φ1 , φ2 ] qui, par définition, est équivalente à ¬φ2 S φ1 (φ1 a
été vraie dans le passé, et φ2 n’a pas été vraie depuis).

Dans cette section, la présentation que nous donnons de la logique temporelle que
nous proposons d’utiliser pour le langage de signatures se borne au modèle proposition-
nel. Le passage au premier ordre se fera à la section suivante.
Chapitre 4. Logique d’acquisition de connaissances 99

φ ::= p | ¬φ | φ1 ∧ φ2 | [φ1 , φ2 ]

Tab. 4.1 – Syntaxe pour le cas propositionnel.

4.1.1 Modèle

Pour le cas propositionnel, le modèle à utiliser est des plus conventionnels :

Définition 4.1 (Trace) Étant donné un ensemble P de constantes propositionnelles,


un événement (ou un état) est un sous-ensemble de P . Une trace, habituellement notée
σ, est une suite, finie ou infinie, d’événements. Par σ(i), on dénote le ieme événement
de la trace.

4.1.2 Syntaxe

La syntaxe de la logique est présentée à la table 4.1. Le seul opérateur temporel


qu’elle comporte est l’opérateur [φ1 , φ2 ], utilisé pour représenter des connaissances qui
ne sont vraies que pour une certaine période de temps. Cette période est caractérisée par
son début (φ1 ) et par sa fin (φ2 ). La formule [φ1 , φ2 ] doit donc être interprétée comme
étant vraie entre φ1 et φ2 . Si φ2 n’est jamais vraie, alors [φ1 , φ2 ] est vraie à partir du
moment où φ1 est vraie.

Un cas particulier important est celui de [φ1 , ff], où ff est définie comme étant une
constante propositionnelle qui n’est jamais vraie. La formule [φ1 , ff] doit donc être com-
prise comme φ1 a déjà été vraie. Il est alors possible de spécifier des séquences d’événe-
ments, en utilisant des formules de la forme r ∧ [q ∧ [p, ff], ff]. Cette formule sera vraie
aux événements pour lesquels non-seulement r est vraie, mais aussi q a déjà été vraie,
et avant cela p. Elle spécifie donc la séquence p − q − r.

HandShake := ack ∧ [synack ∧ [syn, ff], ff]


Session := [HandShake, fin ∨ rst]
V alidAttack := Attack ∧ Session

Fig. 4.1 – Attaque dans le contexte d’une session TCP active.


Chapitre 4. Logique d’acquisition de connaissances 100

σ(i) |=σ p ssi p ∈ σ(i)


σ(i) |=σ ¬φ ssi σ 6|=σ φ
σ(i) |=σ φ1 ∧ φ2 ssi σ(i) |=σ φ1 et σ(i) |=σ φ2
σ(i) |=σ [φ1 , φ2 ] ssi ∃j.j < i et σ(j) |=σ φ1
et 6 ∃k.j < k < i.σ(k) |=σ φ2

Tab. 4.2 – Sémantique pour le cas propositionnel.

À la figure 4.1, on voit comment un tel séquencement peut être utilisé pour spécifier
une poignée de main TCP. La formule doit se lire de l’intérieur vers l’extérieur. Le
premier événement qui doit survenir est un paquet syn. Une fois que l’on a vu ce paquet
syn, on doit voir un paquet synack. La poignée de main n’est complétée que lorsque le
dernier acquiescement, ack, est envoyé. La deuxième formule montre comment on peut
utiliser cette poignée de main pour spécifier une session TCP active. Elle dit qu’une
session TCP est active à partir du moment où la poignée de main est complétée, jusqu’au
moment où on voit passer un paquet fin ou un paquet rst. Finalement, on peut utiliser
cette information pour valider l’occurrence d’une attaque donnée survenant au-dessus
de TCP, comme le fait le préprocesseur Flow de Snort. Si la formule Attack représente la
complétion d’une attaque au-dessus de TCP, alors la formule Attack ∧ Session signifie
qu’une poignée de main doit avoir été complétée avant que l’attaque n’ait été observée,
et que la session ne doit pas avoir été close par un paquet rst ou un paquet fin.

4.1.3 Sémantique

La sémantique de la logique proposée, pour le cas propositionnel, est présentée à


la table 4.2. Elle dit comment, étant donné un état σ(i) appartenant à une trace σ,
doivent être évaluées les formules. La première ligne, comme d’habitude, dit que σ(i)
satisfait une constante propositionnelle p ssi p appartient à σ(i) (on se souvient que, par
définition, un événement est un ensemble de constantes propositionnelles). La deuxième
ligne dit que σ(i) satisfait la négation d’une formule ssi celle-ci n’est pas satisfaite par
σ(i). La troisième ligne dit que σ(i) satisfait une conjonction de formules ssi σ(i) satisfait
les deux propositions conjointes. La quatrième ligne, la plus importante, dit que σ(i)
satisfait [φ1 , φ2 ] ssi φ1 a été satisfaite par un événement précédent σ(i), et qu’aucun
autre événement n’a satisfait φ2 depuis.

Remarquons que la relation de satisfaction n’est définie que pour les différents états
de la trace, et non pour la trace au complet. Informellement, nous pouvons dire qu’une
trace satisfait une formule si et seulement si tous ses états la satisfont. Cependant, étant
Chapitre 4. Logique d’acquisition de connaissances 101

donné le problème auquel nous nous attaquons, le fait de savoir si une trace satisfait ou
non une formule ne présente que peu d’intérêt. Ce qui nous intéresse vraiment, c’est de
savoir quels sont les paquets (états) qui doivent être considérés comme offensifs. C’est
pourquoi la sémantique est définie par rapport à ces derniers plutôt que par rapport à
la trace au complet.

Aux figures 4.2 et 4.3, se trouve un exemple de relation de satisfaction pour la


formule Session définie à la figure 4.1. Les lignes représentent les événements de la
trace, et les colonnes les formules. À la figure 4.2, se trouvent les différentes constantes
propositionnelles (constituant la trace), et à la figure 4.3, les formules composées. Le

symbole indique que la formule à la tête de la colonne est satisfaite par l’événement i.

i syn synack ack fin rst



0

1

2
3
4

5
6
... ... ... ... ... ...

Fig. 4.2 – Exemple de trace pour le cas propositionnel.

i [syn, ff] [synack ∧ [syn, ff], ff] HandShake Session


0

1
√ √
2

3

4

5
6
... ... ... ... ...

Fig. 4.3 – Exemple de relations de satisfaction pour le cas propositionnel.


Chapitre 4. Logique d’acquisition de connaissances 102

i p(x, y) q(v)
0 p(2, 3) q(3)
p(2, 4) q(0)
1 p(5, 4)
2
... ... ...

Fig. 4.4 – Exemple de trace du premier ordre.

4.2 Unification

Dans la section précédente, nous avons présenté une version propositionnelle de la


logique que nous proposons d’utiliser pour spécifier les scénarios d’attaque et d’acqui-
sition d’information. Comme nous l’avons déjà dit, une logique propositionnelle est, en
général, insuffisante pour exprimer des scénarios d’attaque car elle ne permet pas de
comparer les événements (ou les paquets, dans le cas d’une trace de trafic) entre eux.

Il est cependant intéressant, avant de passer à l’étude du cas du premier ordre, de


remarquer ce que les programmeurs de Snort ont récemment fait avec le module de
détection flowbits. Il s’agit d’un module permettant de définir, pour chaque flot TCP,
un ensemble de drapeaux qui peuvent être soit vrais, soit faux. Il est alors possible de
spécifier, lors de la reconnaissance d’une signature donnée, des actions à prendre sur
les valeurs des drapeaux. Explicitement, dans la version 2.1.2 de Snort, il est possible
de prendre les actions set, unset et toggle, donnant respectivement aux drapeaux
la valeur vrai, faux, et contraire de celle actuelle. Les mots-clés isset et isnotset
permettent, par la suite, de vérifier les valeurs attribuées aux drapeaux. Bien que sim-
pliste en apparence, ce mécanisme donne à Snort exactement la même expressivité que
le cas propositionnel de la logique que nous proposons. La formule φ1 correspond à la
signature donnant la valeur vrai au drapeau (set), et la formule φ2 à celle lui donnant
la valeur faux (unset). La formule [φ1 , φ2 ] est donc vraie si et seulement si le drapeau
correspondant a la valeur vrai. Ce qu’il faut conclure de cette observation est le fait
suivant : lorsque des hypothèses sont faites sur les relations entre les événements (par
exemple, appartenir au même flot TCP), le cas propositionnel peut tout de même se
révéler d’une certaine utilité.
Chapitre 4. Logique d’acquisition de connaissances 103

φ ::= p(x1 , . . . , xn ) | ¬φ1 | φ1 ∧ φ2 | [φ1 , φ2 ]

Tab. 4.3 – Syntaxe pour le cas du premier ordre.

4.2.1 Modèle

Au chapitre précédent, nous avons remarqué que le modèle consistant en une suite
d’enregistrements est insuffisant pour les besoins de la détection d’intrusions. En fait,
le problème est le suivant : étant donné un prédicat p(x), le modèle d’enregistrements
nous force à considérer qu’il existe une et une seule valeur de x telle que p(x) est vraie,
alors qu’en réalité, il peut n’y en avoir aucune ou plusieurs. C’est pour remédier à ce
problème que nous introduisons la notion d’unification au coeur même du modèle et de
la sémantique.

Définition 4.2 (Trace) Étant donnés P , un ensemble de prédicats du premier ordre,


et V , un ensemble de valeurs, un événement est une fonction associant à chaque pré-
dicat p(x1 , . . . , xn ) un sous-ensemble de V n (c’est-à-dire un ensemble de n-tuples à
valeurs dans V ). Une trace, habituellement notée σ, est une suite, finie ou infinie,
d’événements. Par σ(i), on dénote le ieme événement de la trace.

Par abus de langage, si le tuple hv1 , . . . , vn i fait partie de l’ensemble associé à


p(x1 , . . . , xn ) par σ(i), nous dirons parfois que p(v1 , . . . , vn ) est dans σ(i). À la figure 4.4,
on peut voir un exemple de trace selon cette nouvelle définition. À chacun des événe-
ments de la trace, chacun des prédicats peut être vrai pour un nombre indéterminé de
tuples. Par exemple, à l’événement 0, le prédicat p(x, y) est vrai pour deux tuples, à
l’événement 1, il est vrai pour un seul tuple, et à l’événement 2, il n’est vrai pour aucun
tuple.

4.2.2 Syntaxe

La syntaxe de la logique avec prédicats du premier ordre se trouve à la table 4.3. Il


s’agit essentiellement de la même syntaxe que dans le cas propositionnel, sauf qu’au lieu
d’une constante propositionnelle p, on retrouve un prédicat du premier ordre p(x1 , . . . , xn ).

La suppression de la notion d’enregistrement nous libère du même coup du besoin


d’utiliser une variable id pour référer aux différents champs d’un événement. Tout se fait
Chapitre 4. Logique d’acquisition de connaissances 104

maintenant par unification. Par exemple, dans le cas de la formule [p(s, t)∧¬q(t), p(5, t)],
la variable t est unifiée lors de l’événement satisfaisant la sous-formule p(s, t) ∧ ¬q(t).
Pour satisfaire l’autre sous-formule, p(5, t), on doit attendre de voir un événement per-
mettant d’associer la même valeur à la variable t.

La notion de délai, dans le cadre de cette logique, s’introduit comme au chapitre


précédent, avec l’utilisation d’un prédicat τ (x). Cependant, comme l’algorithme de vé-
rification que nous présenterons n’utilise pas explicitement les temps associés aux évé-
nements, et que ceux-ci ne sont pas non-plus utilisés dans la sémantique, l’hypothèse de
croissance n’est ici plus nécessaire. Elle le sera cependant lorsque viendra le temps de
s’assurer que les spécifications que l’on rédige expriment bien ce que l’on veut exprimer.
Remarquons enfin que même l’existence du temps n’est plus nécessaire pour que les
algorithmes que nous présenterons fonctionnent et que la théorie que nous développons
soit utile.

La formule τ (x) signifie donc que l’événement courant est survenu au temps x. Alors,
la formule [φ ∧ τ (x1 ), τ (x2 ) ∧ x2 − x1 > δ] est vraie pour une période de δ unités de
temps après que φ ait été vraie. Cette formule étant souvent utilisée, nous l’abrégerons
par
[φ, δ].
La formule q ∧ [p, 3] est donc vraie pour les événements où q est vraie après que p ait
été vraie, et que la période entre les deux n’est pas plus longue que 3 unités de temps.

À la figure 4.5, on montre comment on peut utiliser cette façon de spécifier des
délais pour donner une meilleure spécification d’une poignée de main TCP que celle
de la figure 4.1. Un problème important avec cette spécification est qu’elle permet aux
trois étapes d’être arbitrairement séparées dans le temps. Par exemple, le ack pourrait
survenir une heure après le synack. En pratique, on sait que le système d’exploitation de
la machine ayant émis le synack aurait abandonné l’attente du ack depuis longtemps,
et que le fait de considérer la poignée de main comme étant réussie serait alors une
erreur. De plus, comme nous l’avons déjà dit, si cet acquiescement ne survient toujours
pas après une période de temps raisonnable, il y a lieu de penser que le paquet syn était
falsifié [7] et qu’une attaque de balayage est en cours.

De façon générale, la formule exprimant le fait que si une formule φ1 vient à être
vraie, alors une autre formule φ2 doit être vraie dans un délai de δ unités de temps est
la suivante :

[φ1 ∧ τ (x1 ), ff] ∧ τ (x2 ) ∧ x2 − x1 > δ ⇒ [φ2 ∧ τ (x3 ), ff] ∧ x3 − x1 < δ

Elle dit que si φ1 a été vraie à plus de δ unités de temps dans le passé, alors, durant la
Chapitre 4. Logique d’acquisition de connaissances 105

tcpFlow(sa,sp,da,dp) :=
ip.sadr(sa) ∧ tcp.sport(sp) ∧ ip.dadr(da) ∧ tcp.dport(dp)

handShake(sa,sp,da,dp) :=
ack ∧ tcpFlow(sa,sp,da,dp) ∧
[synack ∧ tcpFlow(da,dp,sa,sp) ∧
[syn ∧ tcpFlow(sa,sp,da,dp),
3sec],
2sec]

sessionClosing(sa,sp,da,dp) :=
(fin ∨ rst) ∧ (tcpFlow(sa,sp,da,dp) ∨ tcpFlow(da,dp,sa,sp))

session(sa,sp,da,dp) :=
[handShake(sa,sp,da,dp),
sessionClosing(sa,sp,da,dp)]

Fig. 4.5 – Suivi des sessions TCP.

période de δ unités de temps suivant le moment où φ1 a été vraie, φ2 doit avoir été vraie.
Étant donné que cette formule peut être un peu lourde à utiliser, nous l’abrégerons par
un nouvel opérateur défini, noté
δ
φ1 ,→ φ2 ,
dont la définition est exactement la formule ci-haut. Il est alors possible d’utiliser cet
opérateur pour spécifier, par exemple, que lorsque les deux premières étapes d’une
poignée de main TCP ont été observées, la troisième doit suivre dans un délai de 2
unités de temps. En version simplifiée, cela pourrait s’écrire de la façon suivante :
2
synack ∧ [syn, 3] ,→ ack,

ce qui signifie que si un synack a été observé dans les trois unités de temps suivant un
syn, et que 2 unités de temps se sont écoulées depuis, alors on doit avoir observé un
ack après ce synack.
Chapitre 4. Logique d’acquisition de connaissances 106

σ(i) |=σ,e p(x1 , . . . , xn ) ssi {x1 , . . . , xn } ⊆ dom(e) et p(e(x1 ), . . . , e(xn )) ∈ σ(i)


σ(i) |=σ,e ¬φ1 ssi var(φ1 ) ⊆ dom(e) et σ(i) 6|=σ,e φ1
σ(i) |=σ,e φ1 ∧ φ2 ssi σ(i) |=σ,e φ1 et σ(i) |=σ,e φ2
σ(i) |=σ,e [φ1 , φ2 ] ssi ∃j < i.σ(j) |=σ,e φ1 et
@j < k < i, e0 . e|var(φ1 ) .σ(k) |=σ,e0 φ2
var(p(x1 , . . . , xn )) = {x1 , . . . , xn }
var(¬φ1 ) = var(φ1 )
var(φ1 ∧ φ2 ) = var(φ1 ) ∪ var(φ2 )
var([φ1 , φ2 ]) = var(φ1 )

Tab. 4.4 – Sémantique opérationnelle pour le cas du premier ordre.

4.2.3 Sémantique

Maintenant que nous avons cette banque d’exemples en main, nous pouvons détailler
la sémantique de la version du premier ordre de la logique, présentée à la table 4.4. Cette
sémantique repose d’abord sur la notion d’environnement :

Définition 4.3 (Environnement) Étant donné X un ensemble de variables et V un


ensemble de valeurs, un environnement est une fonction partielle e : X → V . L’en-
semble des environnements est muni d’un ordre partiel ., défini de la façon suivante :
e0 . e ssi dom(e0 ) ⊇ dom(e) et, ∀x ∈ dom(e).e0 (x) = e(x).

La première ligne de la table 4.4 indique qu’un prédicat p(x1 , . . . , xn ) est satisfait, par
un événement σ(i) donné, sous les environnements e tels que le n-tuple he(x1 ), . . . , e(xn )i
est un élément de l’ensemble associé à p(x1 , . . . , xn ) dans σ(i). La seconde ligne dit que la
négation d’une formule est satisfaite par un état σ(i) sous les environnements qui, bien
que comportant la définition des variables nécessaires, ne satisfont pas la formule don-
née. La troisième ligne dit qu’une conjonction est satisfaite, par un événement σ(i), sous
les environnements sous lesquels σ(i) satisfait chacune des deux propositions conjointes.
Finalement, la quatrième ligne nous dit qu’une formule temporelle est satisfaite, par un
événement σ(i), sous les environnements sous lesquels φ1 a déjà été satisfaite, et que
depuis il a été impossible d’étendre de façon à satisfaire φ2 .

La notation e|S (e restreint à S) désigne l’environnement e0 tel que dom(e0 ) =


dom(e) ∩ S et ∀x ∈ dom(e0 ).e0 (x) = e(x). La restriction aux variables de φ1 , pour
la désactivation d’une formule temporelle, est introduite pour répondre au fait intuitif
qu’une connaissance ne peut être retirée de la base de connaisances que sur la base de ce
qui a été appris au moment de l’acquisition de cette connaissance. Cet aspect particulier
Chapitre 4. Logique d’acquisition de connaissances 107

i p(s, t) q(t) p(s, t) ∧ ¬q(t) [p(s, t) ∧ ¬q(t), p(5, t)]


0 {s 7→ 2, t 7→ 3} {t 7→ 3} {s 7→ 2, t 7→ 4}
{s 7→ 2, t 7→ 4} {t 7→ 0}
1 {s 7→ 5, t 7→ 4} {s 7→ 5, t 7→ 4} {s 7→ 2, t 7→ 4}
2 {s 7→ 5, t 7→ 4}
... ... ... ... ...

Fig. 4.6 – Exemple de relations de satisfaction pour le cas du premier ordre.

de la sémantique de la logique, joint au fait que nous n’utilisons pas de quantificateurs


universels ou existentiel, nous amène à faire quelques commentaires sur la portée des
différentes variables utilisées dans une formule. Si ce n’était de la présence de l’opéra-
teur de restriction dans la sémantique de l’opérateur temporel, les formules devraient
toutes être vues comme étant simplement encadrées par des quantificateurs universels
s’appliquant à toutes les variables. Cependant, l’opérateur de restriction a pour effet de
déquantifier, dans les formules de la forme [φ1 , φ2 ], les variables de φ2 n’apparaissant
pas dans φ1 . Par exemple, dans la formule [p(x), q(y)] ∧ r(y), les deux occurences de
la variable y n’ont aucunement besoin d’être associées à la même valeur. Pour ne pas
commettre d’erreur, il est plus simple de considérer que pour les formules φ ayant une
sous-formule de la forme [φ1 , φ2 ], toutes les occurrences des variables apparaissant φ2
mais non dans φ1 doivent être dans φ2 . Cette restriction syntaxique ne diminue en rien
l’expressivité de la logique. La formule [p(x), q(y)] ∧ r(y), par exemple, peut être rem-
placée par la formule équivalente [p(x), q(y)] ∧ r(z). En se pliant à cette restriction, il
est alors possible d’ignorer l’opérateur de restriction et de considérer que les formules
sont encadrées par des quantificateurs universels.

À la figure 4.6, on voit un exemple de relation de satisfaction, dans le cas où la


trace σ est celle de la figure 4.4, (page 102). Chaque case du tableau contient les plus
petits environnements sous lesquels la formule à la tête de la colonne est satisfaite
par l’événement σ(i). Pour les prédicats primitifs, les environnements sont ceux pour
lesquels les tuples formés des valeurs associés aux variables de ces prédicats font partie
de l’ensemble associé à ce prédicat dans σ(i). À l’événement 0, le prédicat p(s, t) est
satisfait sous l’environnement {s 7→ 2, t 7→ 3}, mais pas la formule ¬q(t), car q(3) est
dans σ(0). La conjonction n’est donc satisfaite que sous l’environnement {s 7→ 2, t 7→ 4}.
Comme cette conjonction active la formule temporelle, celle-ci est donc satisfaite à partir
de σ(1) sous cet environnement. Comme, à σ(1), on retrouve un couple où la première
valeur d’un tuple associé à p(x, y) est unifiable avec 5 et la seconde avec la valeur associée
à t, la formule temporelle n’est plus satisfaite sous l’environnement {s 7→ 2, t 7→ 4} à
partir de σ(2). Elle l’est cependant sous l’environnement {s 7→ 5, t 7→ 4}.
Chapitre 4. Logique d’acquisition de connaissances 108

Nous terminons cette section avec la présentation de trois résultats nous donnant une
meilleure intuition de ce que représente l’ensemble var(φ). Le premier nous dit qu’un
environnement doit absolument définir toutes les variables contenues dans var(φ) pour
pouvoir satisfaire φ. Le second nous dit que ces variables sont suffisantes, et que le fait
d’en ajouter plus ne change rien. Le troisième nous fournit un cas particulier du second
dont nous aurons besoin plus loin dans ce chapitre.

Proposition 4.4 Si σ(i) |=σ,e φ, alors var(φ) ⊆ dom(e).

Démonstration :

On procède par induction sur φ. Les cas p(x1 , . . . , xn ) et ¬φ1 découlent directement de
la sémantique. Si σ(i) |=σ,e φ1 ∧ φ2 , alors σ(i) |=σ,e φ1 et σ(i) |=σ,e φ2 . Par induction,
var(φ1 ) ⊆ dom(e) et var(φ2 ) ⊆ dom(e), donc, var(φ1 ) ∪ var(φ2 ) ⊆ dom(e), c’est-à-dire,
d’après la définition de var, var(φ1 ∧ φ2 ) ⊆ dom(e). Si σ(i) |=σ,e [φ1 , φ2 ], alors il existe
j < i.σ(j) |=σ,e φ1 . Par l’hypothèse d’induction, dom(e) ⊇ var(φ1 ) = var([φ1 , φ2 ]). ¤

Proposition 4.5 Soit e1 tel que var(φ) = dom(e1 ). Pour tout e2 . e1 , σ(i) |=σ,e1 φ si
et seulement si σ(i) |=σ,e2 φ.

Démonstration :

On procède par induction sur φ.

cas p(x1 , . . . , xn ) :

(⇒)

σ(i) |=σ,e1 p(x1 , . . . , xn )


⇒ h définition de la relation |= i
{x1 , . . . , xn } ⊆ dom(e1 ) et p(e1 (x1 ), . . . , e1 (xn )) ∈ σ(i)
⇒ h par définition de la relation .,
{x1 , . . . , xn } ⊆ dom(e2 ), et e2 (xk ) = e1 (xk ) i
{x1 , . . . , xn } ⊆ dom(e2 ) et p(e2 (x1 ), . . . , e2 (xn )) ∈ σ(i)
⇒ h définition de la relation |= i
σ(i) |=σ,e2 p(x1 , . . . , xn )

(⇐)
Chapitre 4. Logique d’acquisition de connaissances 109

σ(i) |=σ,e2 p(x1 , . . . , xn )


⇒ h définition de la relation |= i
{x1 , . . . , xn } ⊆ dom(e2 ) et p(e2 (x1 ), . . . , e2 (xn )) ∈ σ(i)
⇒ h par hypothèse sur e1 , {x1 , . . . , xn } ⊆ dom(e1 ),
et par définition de la relation ., e1 (xk ) = e2 (xk ) i
{x1 , . . . , xn } ⊆ dom(e1 ) et p(e1 (x1 ), . . . , e1 (xn )) ∈ σ(i)
⇒ h définition de la relation |= i
σ(i) |=σ,e1 p(x1 , . . . , xn )

cas ¬φ1 :

(⇒)

premièrement,

σ(i) |=σ,e1 ¬φ1


⇒ h définition de la relation |= i
σ(i) 6|=σ,e1 φ1
⇒ h direction ⇐ de l’hypothèse d’induction i
σ(i) 6|=σ,e2 φ1

deuxièmement,

e2 . e1 et var(φ) = dom(e1 )
⇒ h définition de l’ensemble var i
e2 . e1 et var(φ1 ) = dom(e1 )
⇒ h définition de la relation . i
dom(e2 ) ⊆ dom(e1 ) et var(φ) = dom(e1 )
⇒ h théorie des ensembles i
var(φ1 ) ⊆ dom(e2 )

donc,

σ(i) 6|=σ,e2 φ1 et var(φ1 ) ⊆ dom(e2 )


⇒ h définition de la relation |= i
σ(i) |=σ,e2 ¬φ1
Chapitre 4. Logique d’acquisition de connaissances 110

(⇐)

premièrement

σ(i) |=σ,e2 ¬φ1


⇒ h définition de la relation |= i
σ(i) 6|=σ,e2 φ1
⇒ h direction ⇒ de l’hypothèse d’induction i
σ(i) 6|=σ,e1 φ1

deuxièmement,

var(φ) ⊆ dom(e1 )
⇒ h définition de l’ensemble var, var(φ) = var(φ1 ) i
var(φ1 ) ⊆ dom(e1 )

donc,

σ(i) 6|=σ,e1 φ1 et var(φ1 ) ⊆ dom(e1 )


⇒ h définition de la relation |= i
σ(i) |=σ,e1 ¬φ1

cas φ1 ∧ φ2 :

(⇒)

σ(i) |=e1 φ1 ∧ φ2
⇒ h definition de la relation |= i
σ(i) |=σ,e1 φ1 et σ(i) |=σ,e1 φ2
⇒ h direction ⇐ de l’hypothèse d’induction,
en posant e3 = e1 |var(φ1 ) et e4 = e1 |var(φ2 ) i
σ(i) |=σ,e3 φ1 et σ(i) |=σ,e4 φ2
⇒ h direction ⇒ de l’hypothèse d’induction i
σ(i) |=σ,e2 φ1 et σ(i) |=σ,e2 φ2
⇒ h définition de la relation |= i
σ(i) |=σ,e2 φ1 ∧ φ2
Chapitre 4. Logique d’acquisition de connaissances 111

(⇐)

σ(i) |=σ,e2 φ1 ∧ φ2
⇒ h définition de la relation |= i
σ(i) |=σ,e2 φ1 et σ(i) |=σ,e2 φ2
⇒ h direction ⇒ de l’hypothèse d’induction,
en posant e3 = e1 |var(φ1 ) et e4 = e1 |var(φ2 ) i
σ(i) |=σ,e3 φ1 et σ(i) |=σ,e4 φ2
⇒ h direction ⇐ de l’hypothèse d’induction i
σ(i) |=σ,e1 φ1 et σ(i) |=σ,e1 φ2
⇒ h définition de la relation |= i
σ(i) |=σ,e1 φ1 ∧ φ2

cas [φ1 , φ2 ] :

(⇒)

σ(i) |=σ,e1 [φ1 , φ2 ]


⇒ h définition de la relation |= i
∃j < i.σ(j) |=σ,e1 φ1 et 6 ∃j < k < i, e01 . e1 |var(φ1 ) .σ(k) |=σ,e01 φ2
⇒ h direction ⇒ de l’hypothèse d’induction i
∃j < i.σ(j) |=σ,e2 φ1 et 6 ∃j < k < i, e01 . e1 |var(φ1 ) .σ(k) |=σ,e01 φ2
⇒ h var(φ1 ) = dom(e1 ) et e2 . e1 impliquent e2 |var(φ1 ) = e1 |var(φ1 ) i
∃j < i.σ(j) |=σ,e2 φ1 et 6 ∃j < k < i, e02 . e2 |var(φ1 ) .σ(k) |=σ,e02 φ2
⇒ h définition de la relation |= i
σ(i) |=σ,e2 [φ1 , φ2 ]

(⇐)

σ(i) |=σ,e2 [φ1 , φ2 ]


⇒ h définition de la relation |= i
∃j < i.σ(j) |=σ,e2 φ1 et 6 ∃j < k < i, e02 . e2 |var(φ1 ) .σ(k) |=σ,e02 φ2
⇒ h direction ⇐ de l’hypothèse d’induction i
∃j < i.σ(j) |=σ,e1 φ1 et 6 ∃j < k < i, e02 . e2 |var(φ1 ) .σ(k) |=σ,e02 φ2
⇒ h var(φ1 ) = dom(e1 ) et e2 . e1 impliquent e2 |var(φ1 ) = e1 |var(φ1 ) i
∃j < i.σ(j) |=σ,e1 φ1 et 6 ∃j < k < i, e01 . e1 |var(φ1 ) .σ(k) |=σ,e01 φ2
⇒ h définition de la relation |= i
σ(i) |=σ,e1 [φ1 , φ2 ]
Chapitre 4. Logique d’acquisition de connaissances 112

Ce dernier cas termine l’étape d’induction sur i et complète la démonstration. ¤

Corollaire 4.6 Si e1 . e2 et σ(i) |=σ,e2 φ, alors σ(i) |=σ,e1 φ.

Démonstration :

Par définition de l’opérateur de restriction, dom(e2 |var(φ) ) = var(φ) ∩ dom(e2 ). Comme,


par la proposition 4.4, var(φ) ⊆ dom(e2 ), on a dom(e2 |var(φ) ) = var(φ). Donc, par la
proposition 4.5, en posant e1 = e2 |var(φ) , on obtient σ(i) |=σ,e2 φ. Maintenant, comme
e1 . e2 . e2 |var(φ) , par la transitivité de la relation ., on a e1 . e2 |var(φ) . On peut finalement
appliquer l’autre direction de la proposition 4.5 pour conclure que σ(i) |=σ,e1 φ. ¤

4.3 Récursivité

4.3.1 Approximants

Comme nous l’avons déjà remarqué, il existe certains types de scénarios d’attaque
ou d’acquisition d’information pour lesquels la notion de répétition est fondamentale.
On n’a qu’à penser, par exemple, aux attaques de balayage, de force brute, ou encore
à certaines attaques de déni de service. Les formules exprimant de tels comportements
répétitifs sont de la forme :

φ := [φ1 ∧ [φ1 ∧ [φ1 ∧ [. . .], φ2 ], φ2 ], φ2 ]

signifiant que φ1 est satisfaite un certain nombre de fois, sans que φ2 ne le soit. Dans le
cas d’une attaque de force brute ayant pour objectif de deviner un mot de passe Telnet,
par exemple, φ1 pourrait représenter une demande de connexion suivie d’un refus, alors
que φ2 pourrait représenter l’écoulement d’un délai.

On sent alors que la formule φ1 dont on a besoin devrait en quelque sorte respecter
la propriété :
φ := [φ1 ∧ φ, φ2 ]
et nous voudrions être capables d’écrire de telles formules. Pour répondre à ce besoin,
nous introduisons la notation montrée à la table 4.5, inspirée des approximants. Nor-
malement, les approximants sont définis pour introduire les points fixes, ceux-ci étant
définis comme des approximants limites, mais il s’avère que dans le cas qui nous occupe,
les points fixes ne sont pas nécessaires et que les approximants suffisent.
Chapitre 4. Logique d’acquisition de connaissances 113

def
ν 0 X.φ = tt
def
ν n X.φ = φ[ν n−1 X.φ/X]

Tab. 4.5 – Approximants.

def
ν 3 X.syn ∧ [X, 2] = syn ∧ [ν 2 X.syn ∧ [X, 2], 2]
def
= syn ∧ [syn ∧ [ν 1 X.syn ∧ [X, 2], 2], 2]
def
= syn ∧ [syn ∧ [syn ∧ [tt, 2], 2], 2]

Fig. 4.7 – Utilisation des approximants.

À la figure 4.7, on voit comment on peut utiliser les approximants pour écrire la
formule ν 3 X.syn ∧ [X, 2], représentant 3 demandes de connexion TCP séparées par au
plus 2 unités de temps.

4.3.2 Tableaux

Les approximants ne sont cependant pas seulement utiles pour exprimer des com-
portements répétitifs. Ils peuvent aussi servir à exprimer toutes sortes de formules où la
récursivité peut intervenir. Supposons par exemple, que l’on définisse un nouveau type
tableau, tel que si tab est une variable de type tableau, alors tab[0], tab[1], tab[2],
. . . sont des variables ordinaires, et supposons que l’on veuille définir une formule all-
Different(tab,i) qui s’évalue à vrai si et seulement si les i premiers éléments du
tableau tab sont différents. Ce sera vrai si tab[0] est différent de tous les autres (de 1
à i-1), et si tab[1] est différent de tous ceux qui restent (de 2 à i-1), et ainsi de suite.

À la table 4.6, on voit comment il est possible de généraliser la notion d’approximant


de façon à pouvoir les définir sur un intervalle donné. La formule

ν j:[0,i] X.(X ∧ ν k:[j+1,i] Y.(Y ∧ tab[j] 6= tab[k]))

signifie alors que les i premiers éléments du tableau tab ont des valeurs deux à deux
différentes. À la figure 4.8, on voit comment interpréter cette formule dans le cas où
i = 3. À la figure 4.9, on voit comment il est possible de définir un balayage de ports en
Chapitre 4. Logique d’acquisition de connaissances 114

def
ν i:[f,f ] X.φ = tt
def
ν i:[d,f ] X.φ = φ[ν i:[d+1,f ] X.φ/X, i/d] (d < f )

Tab. 4.6 – Approximants avec intervalles.

def
ν j:[0,3] X.(X ∧ ν k:[j+1,3] Y.(Y ∧ tab[j] 6= tab[k])) =
def
ν j:[1,3] X.(X ∧ ν k:[j+1,3] Y.(Y ∧ tab[j] 6= tab[k]) ∧ ν k:[1,3] Y.(Y ∧ tab[0] 6= tab[k]) =
def
... =
def
ν j:[1,3] X.(X ∧ ν k:[j+1,3] Y.(Y ∧ tab[j] 6= tab[k]) ∧ tab[0] 6= tab[2] ∧ tab[0] 6= tab[1] =
def
... =
tab[1] 6= tab[2] ∧ tab[0] 6= tab[2] ∧ tab[0] 6= tab[1]

Fig. 4.8 – Utilisation des approximants avec intervalles.

utilisant la récursivité et les tableaux. Il est particulièrement intéressant de remarquer


comment la variable d’itération i et les tableaux sont utilisés pour spécifier que les ports
destination des demandes de connexion doivent être tous différents. Lorsque le premier
syn est identifié, la variable dp[0] est unifiée à son port destination, et la taille du
tableau dp est alors de 1. Lorsque le second syn est identifié, la variable dp[1] est unifiée
à son port destination, et dp devient alors un tableau de taille 2. À chaque itération, la
formule allDifferent, que nous avons déjà définie, est utilisée pour vérifier que tous
les ports destination sont bel et bien différents. Il n’y a aucune restriction sur les ports
source. Cependant, ceux-ci doivent toujours être emmagasinés dans un tableau plutôt
que dans une simple variable, sinon la formule signifierait que tous les ports source
doivent être égaux, l’unification ayant été faite une fois pour toutes à la réception de la
première demande de connexion.

4.4 Algorithmes

Bien que la sémantique de la logique permette que l’opérateur [φ1 , φ2 ] réfère à un


état qui soit arbitrairement loin dans le passé, l’algorithme que nous présentons ne
nécessite pas de mémoriser la trace, et encore moins de parcourir celle-ci autrement
Chapitre 4. Logique d’acquisition de connaissances 115

portScan(sa,da) :=
ν i:[0,5] X.
syn ∧
tcpFlow(sa,sp[i],da,dp[i]) ∧
allDifferent(dp,i) ∧
[X, 2sec]

Fig. 4.9 – Balayage de ports TCP.

qu’événement par événement, dans l’ordre où ils surviennent. De plus, la sémantique
de la logique étant purement passée, après chaque traitement d’un événement σ(i), il
est possible de dire si oui ou non σ(i) |= φ. Nul n’est besoin d’attendre à l’événement
suivant, ou encore de tenir à jour une liste de scénarios partiels. En ce sens, l’algorithme
que nous présentons est donc en ligne (voir table 1.9, page 52).

Nous présentons deux versions de l’algorithme, une pour le cas propositionnel, et


une pour le cas du premier ordre. Il s’agit essentiellement du même algorithme, la seule
différence se situant au niveau des mécanismes d’unification.

Nous donnons aussi, dans chacun des cas, une démonstration de la validité (com-
plétude et cohérence) de l’algorithme, ainsi qu’une analyse de complexité. Entre autres,
dans le cas propositionnel, nous verrons que l’espace mémoire nécessaire ne dépasse pas
quelques bits. La validité, dans le cas du premier ordre, ne sera obtenue qu’à condition
de faire quelques concessions sur la forme des formules à vérifier. Ces concessions ne
sont cependant pas plus restrictives que celles qui doivent être faites lors de l’utilisation
du langage de programmation Prolog.

4.4.1 Cas propositionnel

L’algorithme de vérification, pour le cas propositionnel, est montré à la table 4.7. La


boucle principale de l’algorithme, verification, prend en entrée une formule φ et une
trace σ de longueur finie. Pour le cas infini, l’algorithme fonctionne exactement de la
même façon, il faut juste prévoir un accès en lecture au tableau sat en cours d’exécution
(puisque l’instruction retourner sat ne sera jamais exécutée).

Les ensembles k et k 0 matérialisent l’acquisition d’information. L’ensemble k re-


présente la connaissance acquise jusqu’à maintenant, et l’ensemble k 0 représente cette
connaissance, en cours de modification par le traitement de l’état courant. La connais-
Chapitre 4. Logique d’acquisition de connaissances 116

fonction verif(φ, s, k, k 0 )
entrées : φ : une formule, s : un état, k : la connaissance passée
sortie : k 0 : la connaissance mise à jour
valeur de retour : un booléen indiquant si s |= φ dans le contexte k
variables locales : v1 , v2 : des booléens
début
selon la forme de φ :
cas p :
retourne p ∈ s
cas ¬φ1 :
retourne ¬verif(φ1 , s, k, k 0 )
cas φ1 ∧ φ2 :
v1 :=verif(φ1 , s, k, k 0 )
v2 :=verif(φ2 , s, k, k 0 )
retourne v1 ∧ v2
cas [φ1 , φ2 ] :
v1 :=verif(φ1 , s, k, k 0 )
v2 :=verif(φ2 , s, k, k 0 )
si v1 alors k 0 := k 0 ∪ {φ}
sinon si v2 alors k 0 := k 0 \ {φ}
retourne φ ∈ k
fin

fonction verification(φ, σ)
entrées : φ : une formule, σ : une trace
sortie : sat, un tableau de booléens tel que sat[i] ssi σ(i) |= φ
variables locales : k,k 0 : des ensembles
début
k 0 := ∅
pour i allant de 0 à |σ|
k := k 0
sat[i] :=verif(φ, σ(i), k, k 0 )
retourner sat
fin

Tab. 4.7 – Algorithme de vérification pour le cas propositionel.


Chapitre 4. Logique d’acquisition de connaissances 117

sance est mise à jour juste avant le traitement de chaque événement. Il aurait été
possible d’optimiser en n’ayant qu’un seul ensemble, en s’assurant que les sous-formules
sont traitées dans le bon ordre, mais il était plus commode, du point de vue de l’analyse,
de garder deux ensembles.

Théorème 4.7 (Validité) Après chaque appel de verif(φ, σ(i), k, k 0 ), l’ensemble


k 0 est l’ensemble de toutes les sous-formules de φ de la forme [φ1 , φ2 ] qui sont telles que
σ(i + 1) |= [φ1 , φ2 ]. De plus, la fonction retourne vrai ssi σ(i) |= φ.

Démonstration :

On procède par induction sur i :

Pour i = 0, on procède par induction sur φ :

cas p : l’expression p ∈ s concorde exactement avec la sémantique.

cas ¬φ1 : Par l’hypothèse d’induction sur φ, verif(φ1 , σ(i), k, k 0 ) retourne vrai
si et seulement si σ(i) |= φ1 . On retourne donc vrai si et seulement si σ(i) 6|= φ1 .

cas φ1 ∧ φ2 : Comme dans le cas précédent, on utilise l’hypothèse d’induction sur φ


pour les deux sous-formules et le reste concorde avec la sémantique.

cas [φ1 , φ2 ] : Comme k = ∅, on retourne faux, ce qui est correct car comme il n’existe
pas de j < 0, σ(0) 6|= [φ1 , φ2 ]. Pour ce qui est de la mise à jour de k, par définition de
la sémantique, σ(1) |= [φ1 , φ2 ] si et seulement si σ(0) |= φ1 . Par l’hypothèse d’induction
sur φ, [φ1 , φ2 ] est ajouté à k 0 si et seulement si σ(0) |= φ1 .

Pour i > 0, les trois premiers cas d’induction sur φ sont identiques au cas où i = 0.

Pour le cas [φ1 , φ2 ], par l’hypothèse d’induction sur i, la valeur retournée est correcte.
Il reste à montrer que k est mis à jour correctement. Par définition de la sémantique,
σ(i + 1) |= [φ1 , φ2 ] si et seulement si σ(i) |= φ1 ou (σ(i) |= [φ1 , φ2 ] et σ(i) 6|= φ2 ).1

Si σ(i) |= φ1 , alors, par l’hypothèse d’induction sur φ, [φ1 , φ2 ] sera dans k 0 (il y est
ajouté s’il n’y est pas déjà).
Si, au contraire, σ(i) 6|= φ1 , alors par l’hypothèse d’induction sur i, [φ1 , φ2 ] ∈ k 0 si et
seulement si σ(i) |= [φ1 , φ2 ]. Par l’hypothèse d’induction sur φ, [φ1 , φ2 ] est retiré de k 0
si et seulement si σ(i) |= φ2 , ce qui termine la démonstration. ¤
1
La forme du reste de la démonstration est (p ⇔ q ∨ r) ⇔ ((q ⇒ p) ∧ (¬q ⇒ (p ⇔ r))).
Chapitre 4. Logique d’acquisition de connaissances 118

Nous venons donc de démontrer que l’algorithme de la table 4.7 est complet et cohé-
rent. Il est complet car pour chaque i tel que σ(i) |= φ, l’appel de la fonction verif(φ,
σ(i), k, k 0 ) retourne vrai, et il est cohérent car si la valeur vrai est retournée, alors
σ(i) |= φ.

Avant de passer à l’analyse de la complexité, nous avons besoin de définir |φ|, la taille
d’une formule. Intuitivement, la taille d’une formule est son nombre de sous-formules.

Définition 4.8 (Taille d’une formule)

def
|p| = 1
def
|¬φ| = 1 + |φ|
def
|φ1 ∧ φ2 | = 1 + |φ1 | + |φ2 |
def
|[φ1 , φ2 ]| = 1 + |φ1 | + |φ2 |

Une propriété intéressante de notre algorithme est que l’espace mémoire qu’il utilise
est constant en fonction de |σ|, et que le temps qu’il met à s’exécuter est linéaire en
fonction de |σ|.

Proposition 4.9 (Complexité) La taille de k ne dépasse jamais |φ|, et le nombre


d’appels de la fonction verif est |φ||σ|.

Démonstration :

L’analyse de la taille de k est directe : comme cet ensemble ne contient que des sous-
formules de φ, celui-ci ne peut être plus grand que le nombre de sous-formules. Le nombre
d’appels de la fonction verif, pour chaque événement, est égal à φ (peu importe i). Le
nombre total d’appels est donc |φ||σ|. ¤

4.4.2 Cas du premier ordre

L’algorithme de vérification, pour le cas du premier ordre, est présenté à la table 4.8.
Le tableau sat, au lieu de contenir des variables booléennes, contient un ensemble d’en-
vironnements sous lesquels chaque événement de la trace satisfait la formule φ. Dans le
cas général, sat ne contient pas tous ces environnements, mais nous verrons dans cette
section qu’il est possible de définir un ensemble de formules pour lequelles σ(i) |=σ,e φ
ssi il existe e0 ∈ sat[i] tel que e . e0 . De même l’ensemble k ne contient plus des sous-
formules de φ, mais des environnements sous lesquels les sous-formules de φ de la forme
Chapitre 4. Logique d’acquisition de connaissances 119

[φ1 , φ2 ] sont satisfaites. L’expression kp(x1 , . . . , xn )kE


s est considérée comme une primi-
tive qui, étant donné un ensemble d’environnements E, retourne l’ensemble minimal
d’environnements e0 étendant un environnement de E tels que σ(i) |=σ,e0 p(x1 , . . . , xn ).

Formules croissantes

Ce qui complique particulièrement l’analyse de cet algorithme est (entre autres)


le cas de la conjonction, où on calcule d’abord les environnements sous lesquels φ1
est satisfaite, et ensuite les environnements étendant ces derniers sous lesquels φ2 est
satisfaite. Cette séquentialité n’est pas toujours possible, et nous passerons pratiquement
le reste de ce chapitre à déterminer dans quels cas elle l’est. En fait, nous verrons que
c’est au moins le cas pour les formules croissantes, que nous définissons à l’instant :

Définition 4.10 Étant donné un ensemble X de variables, on définit récursivement


l’ensemble des formules croissantes pour X de la façon suivante :

1. p(x1 , . . . , xn ) est croissante pour X,


2. ¬φ1 est croissante pour X si var(φ1 ) ⊆ (X)
et φ1 est croissante pour X,
3. φ1 ∧ φ2 est croissante pour X si φ1 est croissante pour X
et φ2 est croissante pour X ∪ var(φ1 ),
4. [φ1 , φ2 ] est croissante pour X si φ1 est croissante pour X
et φ2 est croissante pour var(φ1 ).

Une formule φ est dite croissante si elle est croissante pour ∅.

La clause 1 de la définition nous dit qu’un prédicat seul est toujours croissant. La
clause 2 peut paraı̂tre surprenante au premier abord, mais elle prend tout son sens
lorsqu’on la met en lien avec la clause 3. La clause 2 nous dit, entre autres, que toutes
les variables de φ1 doivent être dans X pour que ¬φ1 puisse être considérée comme
croissante, et la 3 nous dit que φ2 doit être croissante au moins pour les variables de
φ1 . Par exemple, les formules p(x) ∧ q(x) et p(x) ∧ ¬q(x) sont croissantes, mais les
formules ¬p(x) ∧ q(x), ¬p(x) ∧ ¬q(x) et ¬(p(x) ∧ q(x)) ne le sont pas. Ces deux clauses,
ensemble, proviennent du fait que nous avons l’intention d’effectuer une unification de
gauche à droite. Comme dans le cas d’une unification faite avec Prolog (du moins avec
l’implémentation de Amzi 7.11), les prédicats situés le plus à gauche servent à construire
des environnements candidats, et les négations situées à droite servent à écarter les
Chapitre 4. Logique d’acquisition de connaissances 120

fonction verif(φ, s, k, E, k 0 )
entrées : φ : une formule, s : un état, k : la connaissance passée,
E : un ensemble d’environnements
sortie : k 0 : la connaissance mise à jour
valeur de retour : un ensemble d’environnements
variables locales : v1 , v2 : des ensembles d’environnements
début
selon la forme de φ :
cas p(x1 , . . . , xn ) :
retourne kp(x1 , . . . , xn )kEs
cas ¬φ1 :
E1 :=verif(φ1 , s, k, E, k 0 )
pour chaque e ∈ E
si ∃e0 ∈ E1 .e0 . e alors E := E − e
retourne E
cas φ1 ∧ φ2 :
E1 :=verif(φ1 , s, k, E, k 0 )
E2 :=verif(φ2 , s, k, E1 , k 0 )
retourne E2
cas [φ1 , φ2 ] :
E2 :=verif(φ2 , s, k, E, k 0 )
pour chaque e tel que hφ, ei ∈ k
si ∃e0 ∈ E2 .e0 . e alors k 0 := k 0 \ {hφ, ei}
E1 :=verif(φ1 , s, k, ∅, k 0 )
pour chaque e ∈ E1
k 0 := k 0 ∪ {hφ, e|var(φ1 ) i}
retourne {e1 ∪ e2 tels que hφ, e1 i ∈ k, e2 ∈ E
et 6 ∃x ∈ dom(e1 ) ∩ dom(e2 ).e1 (x) 6= e2 (x)}
fin

fonction verification(φ, σ, sat)


entrées : φ : une formule, σ : une trace
sortie : sat, un tableau d’ensembles d’environnements
variables locales : k,k 0 : des ensembles
début
k 0 := ∅
pour i allant de 0 à |σ|
k := k 0
sat[i] :=verif(φ, σ(i), k, {∅}, k 0 )
fin

Tab. 4.8 – Algorithme de vérification pour le cas du premier ordre.


Chapitre 4. Logique d’acquisition de connaissances 121

kp(x1 , . . . , xn )keσ,i = {e0 . e.p(e0 (x1 ), . . . , e0 (xn )) ∈ σ(i) et


x ∈ dom(e0 ) ⇒ x ∈ dom(e) ou x ∈ {x1 , . . . , xn }}
k¬φ1 keσ,i = {e} si kφ1 keσ,i = ∅, ∅ sinon
kφ1 ∧ φ2 keσ,i = kφ2 kE
σ,i avec E1 = kφ1 kσ,i
1 e

k[φ1 , φ2 ]keσ,0 = ∅
e0 |
k[φ1 , φ2 ]keσ,i (i > 0) = kφ1 keσ,i−1 ∪ {e0 ∈ [φ1 , φ2 ]keσ,i−1 tels que kφ2 kσ,i−1
var(φ1 )
= ∅}

Tab. 4.9 – Sémantique dénotationnelle pour le cas du premier ordre.

candidats construits. Dans notre cas, les candidats sont cependant aussi construits par
les formules temporelles, qui représentent la base de connaissances. La clause 4 de la
définition dit que pour qu’une formule temporelle soit croissante, la formule d’activation
doit être croissante, mais la formule de désactivation, elle, ne doit être croissante que
par rapport aux variables de la formule d’activation. Encore une fois, ce renforcement
(plus X est petit, plus il est difficile d’être croissant pour X) de la contrainte provient
du fait, que nous avons déjà noté lors de la présentation de la sémantique, que le retrait
d’une connaissance ne doit être effectué que sur la base des connaissances acquises lors
de l’acquisition de cette connaissance.

Avant de passer à la présentation de la sémantique dénotationnelle, notons que


certaines précautions doivent être prises lors des comparaisons faites avec le langage
Prolog. La principale précaution à faire est la suivante : la base de faits de Prolog ne
doit pas être associée à notre base de connaissances, mais plutôt à chaque événement.
Dans Prolog, l’unification des prédicats se fait avec la base de faits, et dans notre cas,
elle se fait avec chacun des événements. C’est donc comme si nous étions en train de
définir un Prolog dont la base de faits change à chaque événement.

Sémantique dénotationnelle

Pour nous aider dans notre analyse, nous avons extrait de l’algorithme présenté à la
table 4.8 une sémantique dénotationnelle abstraite, présentée à la table 4.9. Désormais,
c’est à partir de cette sémantique que nous raisonnerons. Étant donné une formule φ,
une trace σ et un environnement e, on montre comment calculer kφkeσ,i , un ensemble
d’environnements e0 tels que e0 étend e juste ce qu’il faut pour pouvoir prétendre à
satisfaire φ dans l’état i de la trace σ. Le juste ce qu’il faut en question est celui de
la proposition 4.5, qui nous dit qu’il suffit que l’environnement soit défini sur var(φ).
Chapitre 4. Logique d’acquisition de connaissances 122

Prenons, par exemple, la première ligne de la table 4.9. Pour un prédicat p(x1 , . . . , xn ),
l’ensemble kp(x1 , . . . , xn )keσ,i est l’ensemble des environnements étendant e en définis-
sant exactement les variables qui lui manquent pour pouvoir satisfaire p(x1 , . . . , xn ). Si
aucune telle extension n’est possible, alors l’ensemble kp(x1 , . . . , xn )keσ,i est ∅. Dans le
cas de la négation, aucune extension n’est nécessaire pour pouvoir prétendre à satisfaire
¬φ1 . Au contraire, dans le cas où il est possible de calculer une extension permettant de
satisfaire φ1 , alors l’ensemble retourné est ∅. Si il n’est pas possible de calculer une telle
extension, alors e peut prétendre à satisfaire ¬φ1 et l’ensemble retourné est seulement
le singleton {e}. Le cas de la conjonction est le plus délicat. Les tentatives d’unifica-
tion sont faites de gauche à droite, sans retour en arrière. L’exploration de tous les
arbres possibles nous semblait un gruge-temps additionnel non-nécessaire du point de
vue de l’expressivité (souvenons-nous que nous travaillons dans un contexte en-ligne).
On commence donc par calculer les extensions pouvant prétendre à satisfaire φ1 , puis
on regarde comment il est possible d’étendre ces extensions de façon à satisfaire φ2 .
Lorsque E est un ensemble d’environnements, l’expression kφkE σ,i est un raccourci pour
e
∪ kφkσ,i . Finalement, on traite le cas de la formule [φ1 , φ2 ] par déploiement récursif.
e∈E
Les extensions prétendant à satisfaire [φ1 , φ2 ] sont celles, premièrement, qui dans l’état
précédent prétendaient à satisfaire φ1 , et deuxièmement, qui, en plus de déjà prétendre
à satisfaire [φ1 , φ2 ] dans l’état précédent, ne prétendaient en aucune façon pouvoir me-
ner à la satisfaction de φ2 (toujours en se restreignant aux variables de φ1 ).

Nous avons maintenant les outils nécessaires pour énoncer tout de suite le théorème
de validité, que nous démontrerons plus loin :

Théorème 4.13 (Validité) Si φ est une formule croissante pour dom(e) et e1 . e,


alors σ(i) |=σ,e1 φ si et seulement si il existe e2 ∈ kφkeσ,i tel que e1 . e2 .

Pour illustrer la nécessité de l’hypothèse de croissance, prenons, par exemple, la


formule p(x) ∧ ¬q(x) dans un état σ(i) = {p(1), q(2)}, nous avons

{∅}
kp(x)kσ,i = {{x 7→ 1}}, et
{{x7→1}}
k¬q(x)kσ,i = {{x 7→ 1}}
par contre, si on considère la formule (équivalente) ¬q(x) ∧ p(x), l’unification de gauche
à droite donne
{∅}
k¬q(x)kσ,i = ∅, et
kp(x)k∅σ,i = ∅

Une fois que nous aurons démontré ce thorème, nous pourrons en déduire le corollaire
Chapitre 4. Logique d’acquisition de connaissances 123

suivant, qui est le cas particulier qui nous intéresse. Il nous dit que si la formule à
vérifier respecte les conditions syntaxiques d’une formule croissante, alors il est possible
de calculer, pour chaque événement, un ensemble d’environnements représentant tous
les environnements sous lesquels φ est satisfaite.

Corollaire 4.14 Si φ est croissante, alors pour tout environnement e, σ(i) |=σ,e si et
{∅}
seulement si il existe e0 ∈ kφkσ,i tel que e . e0 .

Les deux lemmes dont nous avons besoin pour la démonstration du théorème servent
à justifier la définition que nous avons donné des formules croissantes. Le lemme 4.11
nous dit que la vérification d’une formule croissante par rapport à un environnement
donné nous donne un ensemble d’environnements dont le domaine contient les variables
de cette formule, et le lemme 4.12 nous dit que ces environnements sont des extensions
du premier environnement.

Lemme 4.11 Si φ est croissante pour dom(e) et e0 ∈ kφkeσ,i , alors dom(e0 ) = dom(e) ∪
var(φ).

Démonstration :

On procède par induction sur i :

Pour le cas i = 0, on procède par induction sur φ :

Cas p(x1 , . . . , xn ) : Conséquence directe de la définition de la sémantique dénotation-


nelle.

Cas ¬φ1 : Si ¬φ1 est croissante pour dom(e), alors var(φ1 ) ⊆ dom(e), et par défi-
nition de la sémantique dénotationnelle, e0 ∈ k¬φ1 keσ,i implique e0 = e. Donc, dom(e0 ) =
dom(e) = dom(e) ∪ var(φ1 ) = dom(e) ∪ var(¬φ1 ).

00
Cas φ1 ∧ φ2 : Soit e00 ∈ kφ1 keσ,i tel que e0 ∈ kφ2 keσ,i (un tel e00 existe par définition de la
sémantique dénotationnelle). Comme φ1 est croissante pour dom(e), on peut appliquer
l’hypothèse d’induction pour dire que dom(e00 ) = dom(e) ∪ var(φ1 ). Alors, par défini-
tion d’une formule croissante, φ2 est croissante pour dom(e00 ), et on peut appliquer une
seconde fois l’hypothèse d’induction sur φ pour dire que dom(e0 ) = dom(e00 ) ∪ var(φ2 ) =
dom(e) ∪ var(φ1 ) ∪ var(φ2 ) = dom(e) ∪ var(φ1 ∧ φ2 ).

Cas [φ1 , φ2 ] : Comme il n’existe pas de e0 ∈ k[φ1 , φ2 ]keσ,0 , la condition du lemme ne


s’applique pas et ce cas est démontré.
Chapitre 4. Logique d’acquisition de connaissances 124

Pour le cas i > 0, les trois premiers cas d’induction sur φ sont identiques. Pour le
cas [φ1 , φ2 ], soit que e0 ∈ kφ1 keσ,i−1 , soit que e0 ∈ k[φ1 , φ2 ]keσ,i−1 . Dans le premier cas,
c’est l’hypothèse d’induction sur φ qui permet de conclure, et dans le second cas, c’est
celle sur i qui finit le travail, complétant ainsi la démonstration du lemme. ¤

Lemme 4.12 Si e0 ∈ kφkeσ,i , alors e0 . e.

Démonstration :

On procède par induction sur i :

Pour le cas i = 0, on procède par induction sur φ :

Cas p(x1 , . . . , xn ) : Conséquence directe de la définition de la sémantique dénotation-


nelle.

Cas ¬φ1 : Conséquence directe de la réflexivité de . et de la définition de la sémantique


dénotationnelle.

Cas φ1 ∧ φ2 : Conséquence directe de la transitivité de . et de la définition de la


sémantique dénotationnelle.

Cas [φ1 , φ2 ] : Comme il n’existe pas de e0 ∈ k[φ1 , φ2 ]keσ,0 , la condition du lemme ne


s’applique pas et ce cas est démontré.

Pour le cas i > 0, les trois premiers cas d’induction sur φ sont identiques. Pour le
cas [φ1 , φ2 ], soit que e0 ∈ kφ1 keσ,i−1 , soit que e0 ∈ k[φ1 , φ2 ]keσ,i−1 . Dans le premier cas,
c’est l’hypothèse d’induction sur φ qui permet de conclure, et dans le second cas, c’est
celle sur i qui finit le travail, complétant ainsi la démonstration du lemme. ¤

Nous avons maintenant tous les outils nécessaires pour passer à la démonstration
du théorème de validité.

σ(i) |=σ,e1 φ si et seulement si il existe e2 ∈ kφkeσ,i tel que e1 . e2

Démonstration du théorème:

On procède par induction sur i.

Cas i = 0 : On procède par induction sur φ.


Chapitre 4. Logique d’acquisition de connaissances 125

Cas p(x1 , . . . , xn ) :

(⇒)

σ(i) |=σ,e1 p(x1 , . . . , xn )


⇒ h définition de la relation |= i
{x1 , . . . , xn } ⊆ dom(e1 ) et p(e1 (x1 ), . . . , en (xn )) ∈ σ(i)
⇒ h définition de la sémantique dénotationnelle et e1 . e i
e ∪ e1 |{x1 ,...,xn } ∈ kφkeσ,i
⇒ h e1 . e ⇒ e1 . e ∪ e1 |{x1 ,...,xn } ,
on peut donc poser e2 = e ∪ e1 |{x1 ,...,xn } i
∃e2 ∈ kφkeσ,i .e1 . e2

(⇐)

e1 . e2 et e2 ∈ kp(x1 , . . . , xn )keσ,i
⇒ h définition de la sémantique dénotationnelle i
e1 . e2 et {x1 , . . . , xn } ⊆ dom(e2 ) et p(e2 (x1 ), . . . , e2 (xn )) ∈ σ(i)
⇒ h définition de la relation . i
{x1 , . . . , xn } ⊆ dom(e2 ) ⊆ dom(e1 ) et e1 (xl ) = e2 (xl ) pour l = 1 . . . n
et p(e2 (x1 ), . . . , e2 (xn )) ∈ σ(i)
⇒ h théorie des ensembles i
p(e1 (x1 ), . . . , e1 (xn )) ∈ σ(i)
⇒ h définition de la relation |= i
σ(i) |=σ,e1 p(x1 , . . . , xn )

Cas ¬φ1 :

(⇒)

σ(i) |=σ,e1 ¬φ1


⇒ h Par définition d’une formule croissante, φ1 est croissante pour dom(e).
On peut donc appliquer la direction ⇐ de l’hypothèse d’induction sur
φ. i
6 ∃e01 ∈ kφ1 keσ,i .e1 . e01
⇒ h e1 . e i
e
e 6∈ kφ1 kσ,i
Chapitre 4. Logique d’acquisition de connaissances 126

⇒ h Comme var(φ1 ) ⊆ dom(e), le lemme 4.11 nous dit que e0 ∈ kφkeσ,i


implique dom(e0 ) = dom(e). D’autre part, le lemme 4.12 nous dit que
e0 ∈ kφkeσ,i implique e0 . e. Comme dom(e0 ) = dom(e) et e0 . e équivaut
à e0 = e, on a donc e0 ∈ kφkeσ,i implique e0 = e. i
kφ1 keσ,i = ∅
⇒ h définition de la sémantique dénotationnelle i
e
k¬φ1 kσ,i = {e}
⇒ h e1 . e (e est donc le e2 voulu) i
∃e2 ∈ kφkeσ,i .e1 . e2

(⇐)

e1 . e2 et e2 ∈ k¬φ1 keσ,i tel que


⇒ h définition de la sémantique dénotationnelle i
e
kφ1 kσ,i = ∅
⇒ h Par définition d’une formule croissante, φ1 est croissante pour dom(e).
On peut donc appliquer la direction ⇒ de l’hypothèse d’induction sur
φ. i
6 ∃e0 . e.σ(i) |=σ,e0 φ1
⇒ h e1 . e i
σ(i) 6|=σ,e1 φ1
⇒ h var(φ1 ) ⊆ dom(e1 ) et définition de la relation |= i
σ(i) |=σ,e1 ¬φ1

Cas φ1 ∧ φ2 :

(⇒)

σ(i) |=σ,e1 φ1 ∧ φ2
⇒ h Comme φ1 est croissante pour dom(e), σ(i) |=σ,e1 φ1 , et e1 . e, on
peut appliquer la direction ⇒ de l’hypothèse d’induction sur φ. i
∃e1 ∈ kφ1 keσ,i .e1 . e01
0

⇒ h Comme σ(i) |=σ,e1 φ2 , que e1 . e01 , et que par le lemme 4.11, φ2 est
croissante pour dom(e01 ), on peut appliquer la direction ⇒ de l’hypo-
thèse d’induction sur φ. i
e0
∃e2 ∈ kφ2 kσ,i1 .e1 . e2
⇒ h définition de la sémantique dénotationnelle i
∃e2 ∈ kφ1 ∧ φ2 keσ,i .e1 . e2
Chapitre 4. Logique d’acquisition de connaissances 127

(⇐)

e1 . e2 et e2 ∈ kφ1 ∧ φ2 keσ,i
⇒ h définition de la sémantique dénotationnelle i
e0
e1 . e2 et ∃e01 ∈ kφ1 keσ,i .e2 ∈ kφ2 kσ,i1
⇒ h Comme φ1 ∧ φ2 est croissante pour dom(e), par définition, φ1 est
croissante pour dom(e). On peut donc appliquer la direction ⇐ de
l’hypothèse d’induction. i
e0
e1 . e2 et σ(i) |=σ,e01 φ1 et e2 ∈ kφ2 kσ,i1
⇒ h Par le lemme 4.11, φ2 est croissante sous dom(e01 ). On peut donc
appliquer encore une fois la direction ⇐ de l’hypothèse d’induction. i
e1 . e2 et σ(i) |=σ,e01 φ1 et σ(i) |=σ,e2 φ2
⇒ h Par le lemme 4.12, e2 . e01 , on peut donc appliquer le corollaire 4.6. i
e1 . e2 et σ(i) |=σ,e2 φ1 et σ(i) |=σ,e2 φ2
⇒ h définition de la relation |= i
e1 . e2 et σ(i) |=σ,e2 φ1 ∧ φ2
⇒ h corollaire 4.6 i
σ(i) |=σ,e1 φ1 ∧ φ2

Cas [φ1 , φ2 ] :

(⇔) Par définition de la sémantique opérationnelle, comme il n’existe pas de j < 0,


il n’existe pas de e1 tel que σ(i) |=σ,e1 [φ1 , φ2 ]. Comme la condition ne s’applique pas,
ce cas du théorème est vérifié.

Cas i > 0 : Les trois premiers cas de l’induction sur φ sont identiques au cas où i = 0.
Il reste à montrer le cas où φ = [φ1 , φ2 ].

(⇒) Si σ(i) |=σ,e1 [φ1 , φ2 ], alors, par définition de la sémantique opérationnelle, soit
que σ(i − 1) |=σ,e1 φ1 , soit que σ(i − 1) |=σ,e1 [φ1 , φ2 ] et 6 ∃e01 . e1 |var(φ1 ) .σ(i − 1) |=σ,e01 φ2 .

Premier cas :

σ(i − 1) |=σ,e1 φ1
⇒ h Comme φ1 est croissante par rapport à dom(e1 ), on peut appliquer
la direction ⇒ de l’hypothèse d’induction sur φ. i
∃e2 ∈ kφ1 keσ,i−1 .e1 . e2
⇒ h définition de la sémantique dénotationnelle i
Chapitre 4. Logique d’acquisition de connaissances 128

∃e2 ∈ k[φ1 , φ2 ]keσ,i .e1 . e2

Second cas :

σ(i − 1) |=σ,e1 [φ1 , φ2 ] et 6 ∃e01 . e1 |var(φ1 ) .σ(i − 1) |=σ,e01 φ2


⇒ h direction ⇒ de l’hypothèse d’induction sur i i
∃e2 ∈ k[φ1 , φ2 ]keσ,i−1 .e1 . e2 et 6 ∃e01 . e1 |var(φ1 ) .σ(i − 1) |=σ,e01 φ2
⇒ h Comme, par le lemme 4.11, var(φ1 ) ⊆ dom(e2 ), et que φ2 est crois-
sante pour var(φ1 ), φ2 est croissante pour dom(e2 )|var(φ1 ) . Alors, étant
donné que e2 |var(φ1 ) = e1 |var(φ1 ) , on peut appliquer la direction ⇐ de
l’hypothèse d’induction sur φ. i
e2 |dom(φ1 )
∃e2 ∈ k[φ1 , φ2 ]keσ,i−1 .e1 . e2 et kφ2 kσ,i−1 =∅
⇒ h définition de la sémantique dénotationnelle i
∃e2 ∈ k[φ1 , φ2 ]keσ,i .e1 . e2

(⇐) Si e2 ∈ k[φ1 , φ2 ]keσ,i , alors e2 ∈ kφ1 keσ,i−1 ou e2 ∈ [φ1 , φ2 ]keσ,i−1 et est tel que
e2 |
var(φ1 )
kφ2 kσ,i−1 = ∅.

Premier cas :

e1 . e2 et e2 ∈ kφ1 keσ,i−1
⇒ h Par définition d’une formule croissante, φ1 est croissante pour dom(e),
on peut donc appliquer la direction ⇐ de l’hypothèse d’induction sur
φ. i
σ(i − 1) |=σ,e1 φ1
⇒ h i − 1 < i et @k.i − 1 < k < i i
∃j < i.σ(j) |=σ,e1 φ1 et @j < k < i, e01 . e1 |var(φ1 ) .σ(k) |=σ,e01 φ2
⇒ h définition de la relation |= i
σ(i) |=σ,e1 [φ1 , φ2 ]

Second cas :

e2 |
e1 . e2 et e2 ∈ k[φ1 , φ2 ]keσ,i−1 et kφ2 kσ,i−1
var(φ1 )
=∅
⇒ h direction ⇒ de l’hypothèse d’induction sur i (deux fois) i
e1 . e2 et σ, i − 1 |=σ,e1 [φ1 , φ2 ] et @e02 . e2 |var(φ1 ) .σ(i − 1) |=σ,e02 φ2
⇒ h e1 . e2 . e2 |var(φ1 ) et transitivité de . i
Chapitre 4. Logique d’acquisition de connaissances 129

σ, i − 1 |=σ,e1 [φ1 , φ2 ] et @e01 . e1 .σ(i − 1) |=σ,e01 φ2


⇒ h définition de la relation |= i
σ(i) |=σ,e1 [φ1 , φ2 ]

Les deux cas étant démontrés, on peut utiliser le corollaire 4.6 pour conclure que
σ(i) |=σ,e1 [φ1 , φ2 ]. Ceci complète le dernier cas d’induction et par le fait même la
démonstration du théorème. ¤

4.5 Conclusion

Dans ce chapitre, nous avons vu comment l’utilisation d’une logique passée permet
de modéliser l’acquisition passive d’information dans un paradigme qui soit complète-
ment déclaratif. Le problème, avec une logique future, est que l’on ne peut pas expri-
mer la condition si tel comportement a déjà été observé. Au chapitre précédent, nous
avons résolu ce problème en permettant de gérer, au niveau de l’implantation, une base
de connaissances à laquelle il était possible de dynamiquement ajouter et retirer des
connaissances. Ces ajouts et ces retraits, dans le contexte d’une logique passée, sont
modélisés par l’utilisation de l’opérateur [φ1 , φ2 ]. De plus, nous avons vu que cet opéra-
teur permet aussi bien de modéliser les scénarios à reconnaı̂tre que ceux d’une logique
future, les rendant ainsi superflus. Nous avons donc vu que l’opérateur [φ1 , φ2 ] est suf-
fisant pour modéliser à la fois l’acquisition de connaissances et les scénarios d’attaque
s’étendant sur plusieurs paquets.

Nous avons aussi vu comment un modèle et une sémantique basés sur un concept
d’unification permettent de mieux modéliser le fait que différents paquets contiennent
différentes entêtes de protocoles, que certains champs puissent être optionnels, ou encore
être associés à plusieurs valeurs différentes. De plus, l’utilisation du concept d’unifica-
tion, lié à l’ajout d’opérateurs passés, permet d’oublier une fois pour toutes le concept de
formule en cours de reconnaissance, qui non-seulement était présent dans l’approche que
nous avons proposée utilisant une logique future, mais aussi dans LogWeaver, Eagle et
Chronicles. De plus, les autres approches, utilisant d’autres formalismes pour les scéna-
rios, tels que les systèmes de transition avec STATL, ou les réseaux de Petri avec IDIOT,
ont eux aussi cette notion de scénario en cours de reconnaissance. En fusionnant les
concepts de scénario et de connaissance, tout devient de la connaissance acquise. On
n’a plus besoin de tenir à jour un ensemble de formules partiellement satisfaites, mais
simplement une base de connaissances. Nous croyons qu’une implémentation judicieuse
des algorithmes que nous avons présentés devrait tenir compte de la nature globale de
Chapitre 4. Logique d’acquisition de connaissances 130

la connaissance acquise pour offrir une factorisation efficace à la fois du code exécutable
et des spécifications.

L’ajout d’opérateurs d’approximants dans la syntaxe nous a permis d’exprimer sim-


plement des propriétés de répétition. Comme ces opérateurs peuvent être définis syn-
taxiquement à partir des opérateurs existant déjà, cet ajout ne complique en rien les
algorithmes de vérification. Aussi, le langage que nous avons développé permet d’expri-
mer assez simplement des contraintes de temps-réel sans faire d’hypothèse particulière
sur le modèle des données ni modifier en quelque façon que ce soit l’algorithme de
vérification.

Finalement, nous avons vu que dans le cas propositionnel, il existe un algorithme


de vérification dont le temps d’exécution augmente linéairement avec la taille de la
trace à analyser, et utilise une quantité constante d’espace mémoire. Nous avons aussi
prouvé que cet algorithme est complet et cohérent par rapport à la sémantique de la
logique. De plus, il s’agit d’un algorithme en-ligne, c’est-à-dire qui n’a jamais besoin
de retourner en arrière pour consulter les événements passés. Dans le cas du premier
ordre, l’algorithme que nous avons présenté est lui aussi en-ligne, ainsi que complet
et cohérent, à condition de respecter certaines conditions syntaxiques sur les formules
à vérifier. Cependant, nous ne croyons pas que ces conditions soient particulièrement
restrictives.

L’analyse de ce dernier algorithme n’a pas encore été faite. L’analyse de la com-
plexité en espace est le cas le moins compliqué. En fait, on peut tout de suite dire
qu’elle est de l’ordre de |φ||var(φ)||V|, où V est le domaine des valeurs unifiables à une
variable. Le raisonnement à effectuer pour arriver à ce résultat est le même que dans le
cas propositionnel. L’analyse en temps ne s’avère cependant pas aussi simple. Le pro-
blème est qu’il nous semble difficile, étant donnée la façon dont l’algorithme est décrit,
de s’arrêter sur le choix d’une instruction élémentaire. Pour le cas propositionnel, nous
avons choisi le nombre d’appels à la fonction verif. Étant donné que les opérations
effectuées à l’intérieur de chacun de ces appels étaient facilement associables à des opé-
rations pouvant s’effectuer en temps constant, cette mesure nous semblait raisonnable.
Dans le cas du premier ordre, les opérations effectuées à l’intérieur de chaque appel sont
des opérations d’unification, et nous n’avons donné que la spécification de celles-ci, sans
entrer dans les détails. Même si la taille des événements (le nombre de tuples associés à
chaque prédicat élémentaire) était toujours la même, ou du moins toujours bornée par
une certaine constante (ce qui est tout de même une hypothèse raisonnable) le problème
se poserait toujours. En fait, le problème se situe au niveau de la base de connaissances.
Il est clair qu’à mesure que le temps avance, la quantité d’information accumulée avance
elle aussi et que les tentatives d’unification avec la base de connaissances prendront de
Chapitre 4. Logique d’acquisition de connaissances 131

plus en plus de temps. D’un autre côté, en supposant borné le domaine des valeurs
auxquelles les variables sont unifiables, il est raisonnable de penser que la taille de la
base de connaissances viendra à se stabiliser. Comme nous l’avons déjà dit, d’un point
de vue théorique, le même raisonnement que pour le cas propositionnel nous amène
à dire que la quantité d’espace mémoire utilisé est bornée. Cependant, d’un point de
vue pratique, une base de connaissance pleine à craquer, bien que ne demandant pas un
temps d’exécution grandissant, peut tout de même demander un temps qui soit inaccep-
tablement trop grand. Autrement dit, dire que l’algorithme s’exécute en temps O(|σ|)
est théoriquement correct, mais nous savons bien que la constante cachée peut être, en
pire cas, d’une taille assomante. D’un autre côté, avec des spécifications bien choisies, il
est fort possible que le pire cas n’arrive pas si souvent que ça. Pour ces raisons, et aussi
à cause du fait que nous ne disposons pas d’une opération élémentaire à utiliser comme
mesure, l’analyse de l’algorithme dans le cas du premier ordre n’a pas été faite.
Chapitre 5

Travaux futurs

Dans ce travail, nous avons développé un nouveau langage de signatures pour les
systèmes de détection d’intrusions. Le focus a principalement été mis sur l’expressivité
de ce langage, de même que sur les aspects algorithmiques reliés à son implémentation.
L’utilisation d’un paradigme basé sur une logique temporelle nous a permis, dans le
cadre du travail effectué, de développer un langage qui soit purement déclaratif, au sens
où l’utilisateur n’a pas à se soucier de la façon dont l’algorithme de vérification s’exécute
pour spécifier ses signatures. L’utilisation d’une logique temporelle présente cependant
d’autres avantages, et ouvre la voie pour nos futurs travaux de recherche. Dans cette
section, nous donnons quelques exemples de directions de recherche que nous prévoyons
explorer dans les mois à venir.

5.1 Satisfiabilité

Un des avantages reliés à l’utilisation d’un paradigme basé sur une logique est de
permettre la vérification de la cohérence de la base de règles. La sémantique du langage
développé s’applique aux états d’une trace, et il est sous-entendu qu’une trace σ satisfait
une politique φ donnée si et seulement si pour chaque événement σ(i), on a σ(i) |=σ φ.
Il existe cependant des formules qui ne sont satisfaites par aucune trace. De telles
formules sont appelées contradictions. À la figure 5.1, on trouve quatre exemples de
contradictions. La première est une contradiction au niveau propositionnel, c’est-à-
dire que non-seulement n’est-elle pas satisfiable par aucune trace, mais elle ne l’est
pas non-plus pour aucun état d’aucune trace. La deuxième formule est un exemple de
contradiction qui se situe au niveau temporel. Elle est satisfaite par les états qui ont été
Chapitre 5. Travaux futurs 133

p ∧ ¬p
[p, faux]
[vrai, p]
p → [q, faux] ∧ q → [p, faux] ∧ (p ∨ q)

Fig. 5.1 – Exemples de contradictions.

précédés par un état où p était vrai. Certainement, cette formule n’est jamais satisfaite
par le premier état d’une trace. Conséquemment, elle n’est satisfiable par aucune trace
et il s’agit d’une contradiction. La troisième est une contradiction pour la même raison.
Bien que la formule vrai soit satisfaite par tous les états, il n’existe pas d’état précédent
le premier état d’une trace. En particulier, aucun état précédent σ(0) ne satisfait vrai.
La quatrième formule est une contradiction au niveau des contraintes temporelles. Elle
dit que q doit survenir avant p, et que p doit survenir avant q. Sans la partie p ∨ q, cette
formule pourrait être satisfaite par les traces où p et q ne surviennent jamais.

Dans un même ordre d’idées, les formules qui sont satisfaites par tous les états de
toutes les traces sont appelées des tautologies. À priori, les tautologies et les contradic-
tions ne sont pas des politiques de sécurité pertinentes, puisque l’on sait à l’avance si
elles seront satisfaites. Il serait donc utile de pouvoir les détecter à l’avance. Une telle
détection constituerait une première étape en vue de la validation d’une politique. À la
table 5.1, on peut voir un système de preuves à base de tableaux permettant de vérifier
si un état d’une trace donnée satisfait une formule. Les trois premières règles d’infé-
rence sont celles du calcul propositionnel, et les trois dernières concernent l’opérateur
temporel [φ1 , φ2 ]. Ce système de preuves ne permet cependant pas de vérifier si une
formule donnée est une tautologie, une contradiction, ou ni l’une ni l’autre. Une avenue
possible pour la détection des tautologies et des contradictions pourrait être d’étendre
ou de modifier ce système de preuves de façon à tenir compte des noeuds déjà traversés
dans l’arbre de preuves.

Un peu plus loin dans ce chapitre, nous proposerons cependant une autre façon de
détecter les tautologies et les contradictions qui utilise seulement les trois premières
règles de ce système de preuves. Juste avant, nous verrons comment il pourrait être
possible d’améliorer la sémantique du langage que nous avons développé de façon à en
faire non-pas un simple langage de détection, mais aussi un langage de contrôle. Nous
utilisons les terme contrôle plutôt que réaction pour mettre en évidence le fait que la
seule réaction permise par l’extension que nous proposons est de bloquer les actions
malicieuses. L’extension ne permet pas, telle quelle, d’engendrer des actions.
Chapitre 5. Travaux futurs 134


p ∈ σ(i) φ, σ(i) φ1 , σ(i) φ2 , σ(i)
p, σ(i) ¬φ, σ(i) φ1 ∧ φ2 , σ(i)

⊥ φ1 , σ(i − 1) [φ1 , φ2 ], σ(i − 1) ¬φ2 , σ(i − 1)


[φ1 , φ2 ], σ(0) [φ1 , φ2 ], σ(i) [φ1 , φ2 ], σ(i)

Tab. 5.1 – Système de preuves à base de tableaux.

5.2 Filtrage dynamique et sémantique de symptôme

Une tendance dans l’implantation de systèmes de détection d’intrusions au niveau


réseau est d’incorporer ceux-ci à des composantes du réseau qui ont le contrôle du trafic
pouvant entrer et sortir du réseau [75, 76, 77, 78]. Les routeurs et les pare-feux sont
des exemples de telles composantes. Par exemple, on peut intégrer Snort au système
d’exploitation d’un routeur et bloquer tous les paquets générant une alarme.

Le bien-fondé d’une telle méthode de travail repose cependant sur l’hypothèse que
les signatures représentent bel et bien les attaques, et non l’effet de celles-ci. En effet,
une tendance en détection d’intrusions est d’écrire des signatures qui ne lanceront des
alarmes que si l’effet de l’attaque est détecté, et non l’attaque elle-même. Cette tech-
nique est principalement motivée par le besoin de diminuer le nombre de faux positifs.
Prenons, par exemple, la propagation d’un vers dans un réseau local. Supposons qu’un
utilisateur A reçoive par un courrier électronique envoyé par son meilleur ami un petit
jeu rigolo permettant la fois de punir son politicien préféré à l’aide d’un tue-mouche et
d’infecter son ordinateur d’un virus qui essayera alors de se propager aux stations de
travail avoisinantes. Le virus utilise alors chaque machine infectée comme relais pour
en infecter d’autres, et ainsi de suite. Supposons, de plus, que ce virus exploite une
vulnérabilité du système d’exploitation Windows contre laquelle 117 stations des 120
du réseau local sont prémunies. Une signature concernant le comportement offensif gé-
nérerait alors des alarmes pour les 120 machines attaquées, alors qu’une signature pour
l’effet de l’attaque ne génère des alarmes que pour les trois machines qui sont réellement
affectées. Le responsable de la sécurité du réseau peut alors réagir plus efficacement car
il sait exactement quelles sont les machines infectées.

Dans le cas du langage que nous avons développé, certaines des signatures que nous
Chapitre 5. Travaux futurs 135

V
Φ ::= (attack|symptom)φj

σ |= Φ ssi σ(0) |=σ Φ


σ(i) |=σ Φ ssi ∀j.σ(i) |=σ φj
et si ∃φj de type attack tel que σ(i) 6|=σ φj
alors σ(i + 1) |=σ−σ(i) Φ
et si 6 ∃φj de type attack tel que σ(i) 6|=σ φj
alors σ(i + 1) |=σ Φ

Tab. 5.2 – Sémantique de symptôme.

avons proposées, notamment celles où on exprime l’écoulement d’un délai, l’événement
permettant de détecter l’attaque n’est pas du tout lié à celui correspondant au compor-
2
tement offensif en tant que tel. Prenons, par exemple, la formule synack∧[syn, 3] ,→ ack
dont nous avons discuté à la page 105. L’événement permettant la détection de l’attaque
est le premier suivant l’écoulement du délai, et cet événement ne doit en aucun cas être
considéré comme offensif. Il n’est qu’un symptôme permettant la détection de l’attaque,
et ne fait pas partie l’attaque elle-même. C’est pourquoi un système de détection d’in-
trusions muni de fonctionnalités de contrôle bloquant cet événement commettrait alors
une erreur.

Une avenue possible pour s’attaquer à ce problème pourrait être de typer les formules
constituant une politique de sécurité de façon à différencier les symptômes des attaques.
Dans le cas du langage LAMBDA [29], Cuppens et al. ont proposé de partitionner
les actions en actions malicieuses et suspicieuses. Les actions suspicieuses ne sont pas
offensives en tant que telles, mais peuvent contribuer au succès d’une attaque. Les
actions malicieuses, elles, entrent directement en conflit avec la politique de sécurité.
L’approche que nous proposons ici a en commun avec celle de Cuppens le fait qu’elle
permet de mieux décider quelles sont les actions qui doivent être bloquées.

À la table 5.2, on voit comment on peut formaliser la rédaction de politiques de


sécurité pour un système de détection d’intrusions avec capacités de contrôle en utilisant
la dichotomie attaques/symptômes. Une politique de sécurité Φ est une conjonction de
règles φj qui sont soit de type attack, soit de type symptom. Les règles φj sont rédigées
dans le langage que nous avons déjà développé. Une trace d’événements satisfait la
politique Φ si elle est satisfaite depuis le premier événement. Un événement de la trace
satisfait la politique Φ si, en premier lieu, il satisfait toutes les règles. En second lieu, si
une règle de type attack a été violée, l’événement courant doit être retiré de la trace
Chapitre 5. Travaux futurs 136

q → ¬[p, faux] ∧ r → [q, faux] p, q, r, . . .

Fig. 5.2 – Exemple de conflit.

avant de continuer la détection à partir de l’événement suivant. Dans le cas où aucune
attaque n’a été repérée, l’événement courant est conservé dans la trace et on continue la
détection à partir de l’événement suivant. L’action de retrait d’un événement de la trace
modélise la capacité de contrôle du système de détection d’intrusions. Si l’événement
est retiré de la trace, alors son effet ne doit pas être pris en compte dans la poursuite
de la détection.

Une telle sémantique permet de gérer certains conflits au niveau de la politique


de sécurité. Oublions-la pour quelques instants et considérons la formule montrée à la
figure 5.2. Elle dit que si q survient, alors il ne doit pas y avoir eu de p auparavant,
et que si r survient, alors il doit y avoir eu un q auparavant. La trace p, q, r, . . . ne
satisfait pas cette formule, car elle présente un q précédé d’un p. En supprimant ce q,
on obtient la trace p, r, . . ., qui ne satisfait pas non-plus la formule, car elle présente
un r qui n’est pas précédé d’un q. On a donc un conflit entre la satisfaction de la
sous-formule q → ¬[p, faux] et la sous-formule r → [q, faux]. Maintenant, revenons à
notre sémantique de symptômes et supposons que l’on ait la chance de travailler dans
un contexte où la première sous-formule représente un symptôme et la seconde une
attaque. En conservant l’événement q, on obtient une trace où seulement un événement
contrevient à la politique, alors qu’en supprimant cet événement, la politique était violée
à deux endroits. De plus, nous sommes certains que nous avons bien fait de conserver q
puisque sa sémantique n’est pas offensive.

5.3 Synthèse de contrôleur

Au chapitre d’introduction de ce mémoire, nous avons dit que le travail que nous
allions effectuer s’inscrivait dans un contexte de validation, au sens où le problème
auquel nous nous attaquions concernait la vérification de certaines traces d’exécution
d’un système donné, et non celle de toutes ses traces d’exécution possibles. Cependant,
lorsque l’on commence à donner la possibilité au programme de vérification de modifier
le modèle en cours d’exécution, il ne s’agit plus d’un problème de validation tant que
d’un problème de synthèse. Un tel programme de vérification, que l’on appellera dès
lors contrôleur, peut être vu comme partie intégrante du système en cours de validation,
Chapitre 5. Travaux futurs 137

et le problème de la synthèse du contôleur pourra alors s’énoncer comme suit :

Problème 5.1 Étant donné un programme P , une propriété φ, et une loi de composi-
tion de programmes ×, générer un programme C tel que C × P |= φ.

Ce problème a déjà été largement exploré dans la littérature [79, 80, 81, 82], et
son niveau de difficulté varie dépendemment de l’algèbre utilisée pour représenter P
et C et de la logique utilisée pour exprimer φ. Or, il y a fortement lieu de penser que
lorsque φ est exprimée avec le cas propositionnel de la logique que nous avons utilisée
pour développer notre langage, et que l’algèbre utilisée est celle des automates finis, la
résolution de ce problème dépend uniquement de φ.

A ::= 0 | a.A1 | A1 + A2 | A1 × A2

Tab. 5.3 – Syntaxe de l’algèbre des automates.

P
(0) = {stop}
P P
(a.A1 ) = a. (A1 )
P P P
(A + A2 ) = (A1 ) ∪ (A1 )
P 1 P P
(A1 × A2 ) = (A1 ) ∩ (A2 )

P P
avec a. (A) = {a.σ | σ ∈ (A)}

Tab. 5.4 – Sémantique de l’algèbre des automates.

Avant d’aller plus loin dans l’annonce des conjectures auxquelles nous comptons
nous attaquer, définissons précisément de quels automates nous parlons. La syntaxe de
l’algèbre des automates est présentée à la table 5.3. Un automate est soit l’automate ne
pouvant faire aucune action (0), soit un automate pouvant faire une action a avant de
devenir un autre automate (a.A1 ), soit un automate pouvant arbitrairement adopter le
comportement de deux autres automates (A1 + A2 ), soit le produit synchrone de deux
automates (A1 ×A2 ). Nous n’avons pas besoin, pour la portée de notre propos, de définir
le parallélisme entre deux automates ni de s’attarder à définir une relation d’équiva-
lence. C’est pourquoi nous ne parlons pas ici d’algèbre de processus, mais simplement
d’automates. Aussi, comme tout notre intérêt tourne autour des traces d’exécution de
ces automates, la sémantique que nous leur accordons est directement exprimée en fonc-
tion de ces traces. L’automate 0 ne peut faire que l’action stop et s’arrêter. L’automate
Chapitre 5. Travaux futurs 138

T CP ::= rst.T CP + syn.S2


S2 ::= rst.T CP + synack.S3
S3 ::= rst.T CP + ack.Session
Session ::= rst.T CP + f in.T CP

Fig. 5.3 – Automate TCP.

Contr ::= rst.Contr + syn.C2


C2 ::= synack.C3
C3 ::= rst.Contr + ack.Session
Session ::= rst.Contr + f in.Contr

Fig. 5.4 – Contrôleur pour l’automate TCP relativement à ¬(rst ∧ [synack, ack]).

a.A1 peut faire l’action a, suivie de toutes les traces que peut effectuer l’automate A1 .
L’automate A1 + A2 peut faire toutes les traces de A1 , de même que toutes celles de
A2 . Finalement, l’automate A1 × A2 peut faire les traces que A1 et A2 peuvent faire.

À la figure 5.3, on voit comment on peut utiliser cette algèbre pour représenter une
version très simplifiée de l’automate TCP. Cet automate peut compléter une poignée de
main (syn.synack.ack) pour devenir l’automate représentant une session. Une session
peut se terminer en posant une action f in avant de retourner à l’état initial. Aussi, peu
importe dans quel état il se trouve, l’action rst retourne à l’état initial. On remarque
que cet automate est déterministe, mais pas complètement défini. Il aurait aussi bien
pu ne pas être déterministe.

Maintenant que nous avons défini notre algèbre, nous sommes en mesure de définir
ce qu’est, dans notre cas, un contrôleur pour un automate relativement à une formule φ :

Définition 5.2 Un automate A1 contrôle un automate A2 relativement à une formule


P P
φ si pour toute trace σ ∈ (A2 ), σ ∈ (A1 × A2 ) ⇔ σ |= φ.

Supposons, par exemple, que l’on veuille contrôler la propriété : l’action rst ne doit
pas être posée entre un synack et un ack. Dans notre logique, cette propriété se formule
Chapitre 5. Travaux futurs 139

S := 2[φ] , avec [φ] = {[φ1 , φ2 ] ∈ sub(φ)}

Act := 2P

s0 := ∅

a
s1 → s2 ssi ϕ(s1 ) ∧ ϕ(a) ` φ
et σ(i) |= ϕ(s1 ) ∧ ϕ(a) implique σ(i + 1) |= ϕ(s2 )
V V
ϕ(s) := φi ∧ ¬φi
φi ∈s1 φi 6∈s1

V V
ϕ(a) := p∧ ¬p
p∈a p6∈a

Tab. 5.5 – Algorithme de synthèse de contrôleur.

de la façon suivante : ¬(rst ∧ [synack, ack]). En effet, cet automate engendre toutes
les mêmes traces que celui de l’automate TCP, sauf celles où un rst survient entre un
synack et un ack.

Nous avons construit cet automate de façon ad-hoc, sans vraiment suivre de méthode
particulière. Rien ne nous pousse à croire que cette solution était l’unique solution. En
particulier, si le contrôleur que nous avons défini avait été capable d’engendrer des traces
que l’automate TCP n’était pas capable d’engendrer, celle-ci auraient été supprimées
par l’opération de produit. Donc, si on était capable, étant donnée une formule φ, de
donner un automate engendrant toutes les traces σ telles que σ |= φ, le problème de la
synthèse du contrôleur tel que nous l’avons énoncé serait résolu une fois pour toutes.
Ceci nous amène à définir la notion de contrôleur universel :

Définition 5.3 Un automate A est un contrôleur universel pour une formule φ si pour
P
toute trace σ, σ ∈ (A) ⇔ σ |= φ. Si A est un contrôleur universel pour φ, on dit que
A implémente φ.

Comme nous avons déjà donné un algorithme de vérification pour notre logique, on
devrait s’attendre à pouvoir transformer cet algorithme en automate et ainsi de pouvoir
définir un contrôleur universel pour n’importe quelle formule φ. Cette transformation
donne exactement l’automate défini à la table 5.5. L’ensemble des états correspond
exactement à l’ensemble des valeurs pouvant être prises par l’ensemble k de l’algorithme
Chapitre 5. Travaux futurs 140

L(C) = ∅
L(C1 ) = {[synack, ack]}

C := synack.C1 + ack.C + rst.C


+syn.C + f in.C
C1 := synack.C1 + ack.C
+syn.C1 + f in.C1

Fig. 5.5 – Contrôleur universel pour ¬(rst ∧ [synack, ack]).

de vérification présenté à la table 4.7 (page 116). L’ensemble des actions pouvant être
posées correspond à l’ensemble des événements (une action ou un événement est un
ensemble de constantes propositionnelles). Une action a effectuée à partir d’un état
s1 mène à un état s2 si le fait de poser cette action, dans l’état s1 , ne mène pas à
une violation de la propriété φ et si, une fois l’action posée, la connaissance accumulée
correspond à celle représentée dans l’état s2 . Comme l’évolution de la connaissance est
définie de façon unique, il ne peut y avoir qu’un seul état s2 défini pour s1 et a. De plus,
comme toute l’information pertinente concernant les actions posées jusqu’à maintenant
est accumulée dans l’état s1 , les règles d’inférences du calcul propositionnel (les trois
premières de la table 5.1) suffisent pour décider si le fait de poser l’action a viole le
propriété φ. Finalement, si le fait de poser l’action a mène à une violation de φ, l’état
s1 n’a pas de transition sortante pour a.

À la figure 5.5, se trouve un contrôleur universel pour la formule ¬(rst∧[synack, ack]).


Il possède deux états, celui où la formule [synack, ack] est vraie, et celui où elle ne l’est
pas. Les actions synack et ack font passer d’un état à l’autre. Lorsque la formule
[synack, ack] est vraie, il est impossible de poser l’action rst. Toutes les autres actions
sont possibles et ne changent pas l’état courant.

Nous énonçons maintenant deux résultats que nous contons démontrer dans les mois
à suivre. Le premier dit que l’automate défini à la table 5.5 implémente bien la propriété
φ pour laquelle il est défini, et le second nous dit comment on peut utiliser cet automate
pour trouver des tautologies et des contradictions.

Conjecture 5.4 Pour toute formule φ, l’automate défini à la table 5.5 implémente φ.

Conjecture 5.5 Une formule φ est une contradiction (n’est pas satisfiable) si l’auto-
mate défini à la table 5.5 n’engendre pas de traces, et elle est une tautologie si il est
complètement défini.
Chapitre 5. Travaux futurs 141

Une fois ces résultats démontrés, il faudra voir comment on peut adapter ces idées
à la logique du premier ordre de la logique que nous avons utilisée pour notre langage.
Aussi, il faudra voir comment on peut incorporer les concepts de symptôme et d’attaque
au problème de synthèse du contrôleur. Certainement, l’algèbre des automates, avec son
produit complètement synchrone, ne suffira plus. Il faudra voir comment on peut définir
une loi de composition permettant un contrôle partiel, au sens où la violation d’un
certain sous-ensemble de la politique ne bloque pas complètement le système contrôlé,
mais seulement certaines de ses actions. Certains travaux ont déjà été effectués dans
cette direction dans [83].
Conclusion

Dans ce travail, nous avons établi les bases d’un nouveau langage de signatures
d’attaques pour un système de détection d’intrusions et d’acquisition passive d’infor-
mation. Nous avons commencé par effectuer un état de l’art des langages de signatures
déjà existants. Nous avons donné un nouveau système de classification des langages
de signatures, que nous avons répartis en cinq catégories, en fonction des paradigmes
utilisés pour définir les langages. Nous avons aussi, suite à cette étude, dressé une liste
de dix propriétés identifiées comme souhaitables pour le langage de signatures d’un
système de détection d’intrusions.

Une des cinq catégories selon lesquelles nous avons classé les langages des systèmes à
base de signatures est celle de ceux basés sur des logiques temporelles. Comme cette voie
nous semblait être la plus prometteuse, nous avons ensuite approfondi nos connaissances
en logiques temporelles. Nous avons trouvé que plusieurs logiques avaient été dévelop-
pées pour répondre à plusieurs besoins. Les différences les plus importantes entre ces
logiques, étant donnés nos besoins, sont la direction des opérateurs temporels (passés
ou futur), et la capacité d’exprimer des contraintes de temps-réel.

Une fois réalisée l’étude des logiques, nous avons défini et implémenté un langage de
signatures ayant la capacité d’acquérir passivement de l’information dans le principal
but d’effectuer une détection d’intrusions plus précise. Le système résultant présentait
une architecture à deux niveaux, avec une séparation claire entre les scénarios et la
connaissance acquise. La partie du langage servant à représenter les scénarios pouvait
être vus comme un sous-ensemble d’une logique temporelle future, à laquelle nous avons
ajouté la capacité de référer à certains événements passés. Bien que le système mis
en oeuvre ne comportait pas encore toutes les caractéristiques recherchées, le langage
développé a permis de clarifier nos idées quand à la façon dont devrait se formaliser
l’acquisition passive d’information.

Nous avons alors défini un second langage, cette fois basé sur une logique temporelle
passée. Il s’est avéré qu’en plus que d’être beaucoup mieux adapté à la surveillance de
Conclusion 143

système en temps-réel et à l’acquisition passive d’information, la syntaxe du langage


obtenu était beaucoup plus simple (du moins de notre point de vue) et surtout, beau-
coup plus souple au niveau des différentes façons de composer les opérateurs entre eux.
Nous avons aussi, dans cette seconde partie du travail, réalisé l’importance du choix du
modèle. Il était clair depuis le début qu’un modèle propositionnel ne pourrait pas suffire
à la tâche. Nous avons alors opté pour un modèle d’enregistrements, modèle souvent
utilisé dans les systèmes que nous avons étudiés. Ce modèle imposait cependant une
certaine vision de la réalité : tous les événements sont caractérisables par un ensemble
donné de champs attribut-valeur. Nous avons réalisé après coup que cette vision ne col-
lait pas d’assez près à la réalité, et nous avons dû redéfinir un modèle basé cette fois sur
un principe d’unification. Bien que les mécanismes d’unification alourdissent quelque
peu les algorithmes de vérification, nous trouvons qu’il vaut la peine d’opter pour ce
choix, puisqu’il permet de rédiger des signatures sans se soucier de la présence ou non
de certains attributs.

Finalement, nous avons vu comment le choix d’un langage basé sur une logique
temporelle ouvrait la voie à d’autres travaux de nature théorique orientés vers l’appli-
cation. Entre autres, ce choix permet de définir un algorithme permettant de vérifier
la cohérence de la base de signatures, il permet aussi d’étendre le langage de façon à
permettre au système de détection d’intrusions de bloquer uniquement les actions ma-
licieuses, et il permet finalement de générer automatiquement un contrôleur pour une
politique donnée.

Au moment de la rédaction de ce mémoire, l’implémentation d’une version inter-


prétée du second langage que nous avons spécifié est en cours de développement en
collaboration avec le Centre de Recherches sur les Communications. Nous prévoyons
faire fonctionner ce prototype de pair avec le système de détection d’intrusions Snort
et le logiciel de capture et de décodage de paquets Ethereal. Nous envisageons aussi
d’implémenter une version compilée de ce langage. Les signatures seront traduites vers
un autre langage, probablement C ou C++. Finalement, une autre voie d’extension sera
de la faire fonctionner au niveau hôte, en utilisant comme source d’événements un outil
tel que strace.
Bibliographie

[1] C. Green and M. Roesch, “Snort users manual 2.1.0 - the snort project,” Dec. 2003.
[2] C. Michel and L. Mé, “Adele : an attack description language for knowledge-based
intrusion detection,” in Proceedings of the 16th International Conference on
Information Security (IFIP/SEC 2001), June 2001, pp. 353–365. [Online].
Available : citeseer.ist.psu.edu/michel01adele.html
[3] D. Spinellis and D. Gritzalis, “Panoptis : Intrusion detec-
tion using a domain-specific language,” Journal of Compu-
ter Security, vol. 10, pp. 159–176, 2002. [Online]. Available :
http://www.dmst.aueb.gr/dds/pubs/jrnl/2002-JCS-Panoptis/html/paper.html
[4] V. Jacobson, C. Leres, and S. McCanne, “tcpdump man page,”
http://www.tcpdump.org/tcpdump man.html.
[5] ——, “libpcap man page,” http://www.tcpdump.org/pcap3 man.html, Nov. 2003.
[6] C. Giovanni, “Fun with packets : Designing a stick,”
http://packetstormsecurity.nl/distributed/stick.htm.
[7] Fyodor,“The art of port scanning,”http://www.insecure.org/nmap/nmap doc.html,
1997.
[8] R. Deraison, R. Gula, and T. Hayton, “Passive vulnerability scanning- an intro-
duction to nevo,” http://www.tenablesecurity.com/papers.html, august 2003.
[9] R. Gula, “Correlating ids alerts with vulnerability information,”
http://www.tenablesecurity.com/papers.html, december 2002.
[10] H. D. B. Morin, L. Mé and M.Ducassé, “M2d2 : A formal data model for ids
alert correlation,” in 5th International Conference on Recent Advances in Intrusion
Detection (RAID 2002), ser. LNCS, vol. 2516. Zurich : Springer, October 2002,
pp. 177–198.
[11] H. Anderson,“Introduction to nessus,”http://www.securityfocus.com/infocus/1741,
october 2003.
[12] ——, “Nessus, part 2 : Scanning,” http://www.securityfocus.com/infocus/1753, de-
cember 2003.
Bibliographie 145

[13] ——, “Nessus, part 3 : Analysing reports,”


http://www.securityfocus.com/infocus/1759, february 2004.
[14] N. Habra, B. L. Charlier, A. Mounji, and I. Mathieu, “ASAX : Software
architecture and rule- based language for universal audit trail analysis,” in
European Symposium on Research in Computer Security (ESORICS), 1992, pp.
435–450. [Online]. Available : citeseer.ist.psu.edu/habra92asax.html
[15] ——, “Preliminary report on advanced security audit trail analysis on unix
(asax also called satx),” Institut D’Informatique, FUNDP, rue Grangagnage
21, 5000 Namur, Belgium, Tech. Rep., September 1994. [Online]. Available :
citeseer.ist.psu.edu/366932.html
[16] A. Mounji, “Languages and tools for rule-based distributed intrusion detection,”
Ph.D. dissertation, Facultés Universitaires Notre-Dame de la Paix, Namur, Bel-
gique, 1997.
[17] V. Paxson, “Bro : a system for detecting network intruders in real-time,” Computer
Networks (Amsterdam, Netherlands : 1999), vol. 31, no. 23–24, pp. 2435–2463,
1999. [Online]. Available : citeseer.ist.psu.edu/paxson98bro.html
[18] S. Eckmann, G. Vigna, and R. Kemmerer, “Statl : An attack lan-
guage for state-based intrusion detection,” 2000. [Online]. Available :
citeseer.ist.psu.edu/article/eckmann00statl.html
[19] K. Ilgun, “USTAT : A real-time intrusion detection system for UNIX,” in Procee-
dings of the 1993 IEEE Symposium on Research in Security and Privacy, Oakland,
CA, 1993, pp. 16–28. [Online]. Available : citeseer.ist.psu.edu/ilgun92ustat.html
[20] G. Vigna and R. A. Kemmerer, “Netstat : A network-based intrusion
detection approach,” in ACSAC, 1998, pp. 25–. [Online]. Available :
citeseer.ist.psu.edu/vigna98netstat.html
[21] S. Kumar and E. Spafford, “An Application of Pattern Matching in Intrusion
Detection,” Department of Computer Sciences, Tech. Rep. 94-013, 1994. [Online].
Available : citeseer.ist.psu.edu/article/kumar94application.html
[22] S. Kumar, “Classification and detection of computer intru-
sions,” Ph.D. dissertation, Purdue, IN, 1995. [Online]. Available :
citeseer.ist.psu.edu/kumar95classification.html
[23] S. Kumar and E. Spafford, “A software architecture to support misuse intrusion
detection,” in Proceedings of the 18th National Information Security Conference,
1995, pp. 194–204. [Online]. Available : citeseer.ist.psu.edu/kumar95software.html
[24] R. Sekar, Y. Guang, S. Verma, and T. Shanbhag, “A high-
performance network intrusion detection system,” in ACM Symposium
on Computer and Communication Security, 1999. [Online]. Available :
http://seclab.cs.sunysb.edu/seclab/pubs/papers.htm
Bibliographie 146

[25] R. Sekar and P. Uppuluri, “Synthesizing fast intrusion detection/prevention


systems from high-level specifications,” in USENIX Security Symposium, 1999.
[Online]. Available : http://seclab.cs.sunysb.edu/seclab/pubs/papers.htm
[26] U. Lindqvist and P. A. Porras, “Detecting computer and network misuse through
the production-based expert system toolset (p-best),” in Proceedings of the
1999 IEEE Symposium on Security and Privacy. Oakland, California : IEEE
Computer Society Press, Los Alamitos, California, may 1999, pp. 146–161.
[Online]. Available : http://www.sdl.sri.com/papers/pbest-sp99-cr/
[27] Anderson, Lunt, Javits, Tamaru, and Valdes, “NIDES : software
users manual — beta-update release,” dec 1994. [Online]. Available :
http://www.sdl.sri.com/papers/7sri/
[28] Anderson, Frivold, and Valdes, “NIDES : A summary,” may 1995. [Online].
Available : http://www.sdl.sri.com/papers/4sri/
[29] F. Cuppens and R. Ortalo, “Lambda : A language to model a database for detection
of attacks,” in Proceedings of the Third International Workshop on Recent Advances
in Intrusion Detection. Springer-Verlag, 2000, pp. 197–216.
[30] F. Sadri and R. A. Kowalski, “Variants of the event calculus,” in International
Conference on Logic Programming, 1995, pp. 67–81. [Online]. Available :
citeseer.ist.psu.edu/522892.html
[31] G. Rosu and K. Havelund, “Synthesizing dynamic programming algorithms from
linear temporal logic formulae,” 2000.
[32] M. Roger and J. Goubault-Larrecq, “Log auditing through model checking,” in
Proc. 14th IEEE Computer Security Foundations Workshop (CSFW’01), Cape
Breton, Nova Scotia, Canada, June 2001. IEEE Comp. Soc. Press, 2001, pp.
220–236. [Online]. Available : citeseer.ist.psu.edu/article/roger01log.html
[33] P. Wolper, “Temporal logic can be more expressive,” Information and Control, vol.
56(1–2), pp. 72–99, 1983.
[34] H. Barringer, A. Goldberg, K. Havelund, and K. Sen, “Rule-based runtime verifi-
cation,” 2004. [Online]. Available : citeseer.ist.psu.edu/barringer04rulebased.html
[35] P. Naldurg, K. Sen, and P. Thati, “A temporal logic based framework for intrusion
detection.” [Online]. Available : citeseer.ist.psu.edu/naldurg04temporal.html
[36] K. Sen, G. Rosu, and G. Agha, “Online efficient predictive sa-
fety analysis of multithreaded programs,” 2004. [Online]. Available :
citeseer.ist.psu.edu/sen04online.html
[37] C. Dousson, “Suivi d’évolutions et reconnaissance de chroniques,” Ph.D.
dissertation, Université Paul Sabatier de Toulouse, september 1994. [Online].
Available : http://dli.rd.francetelecom.fr/abc/diagnostic/
Bibliographie 147

[38] B. Morin and H. Debar, “Correlation of intrusion symptoms : an application of


chronicles,” in 6th International Conference on Recent Advances in Intrusion De-
tection (RAID 2003), ser. LNCS, vol. 2820. Pittsburg : Springer, September 2003.
[39] H. Debar, D. Curry, and B. Feinstein, “Intrusion detection exchange format,”
http://www.ietf.org/internet-drafts/draft-ietf-idwg-idmef-xml-12.txt, july 2004,
internet-Draft.
[40] R. Deraison, “The nessus attack scripting language reference guide,”
http://www.nessus.org/doc/nasl.html, april 2000.
[41] I. Secure Networks, “Custom attack simulation language (casl),”
http://www.sockpuppet.org/tqbf/casl.html, 1997.
[42] J.-P. Pouzol and M. Ducassé, “Formal specification of intrusion signatures and
detection rules,” in Proceedings of the 15th IEEE Computer Security Foundations
Workshop (CSFW’02). IEEE Computer Society, 2002, p. 64.
[43] Lin, Wang, and Jajodia, “Abstraction-based misuse detection : High-level
specifications and adaptable strategies,” in PCSFW : Proceedings of The 11th
Computer Security Foundations Workshop. IEEE Computer Society Press, 1998.
[Online]. Available : citeseer.ist.psu.edu/lin98abstractionbased.html
[44] L. Mé, “Gassata, a genetic algorithm as an alternative tool for security audit trails
analysis,” First International Workshop on Recent Advances in Intrusion Detection
(RAID ’98), 1998.
[45] M. Crosbie and E. H. Spafford, “Applying genetic programming to intrusion
detection,” in Working Notes for the AAAI Symposium on Genetic Programming,
E. V. Siegel and J. R. Koza, Eds. MIT, Cambridge, MA, USA : AAAI, 10–12
1995, pp. 1–8. [Online]. Available : citeseer.ist.psu.edu/crosbie95applying.html
[46] D. Song, M. I. Heywood, and A. N. Zincir-Heywood, “A linear genetic program-
ming approach to intrusion detection,” in Genetic and Evolutionary Computation –
GECCO-2003, ser. LNCS, vol. 2724. Chicago : Springer-Verlag, 12-16 July 2003,
pp. 2325–2336.
[47] R. Lippmann, D. Fried, I. Graf, J. Haines, K. Kendall, D. McClung, D. Weber,
S. Webster, D. Wyschogrod, R. Cunningham, and M. Zissman, “Evaluating
intrusion detection systems : The 1998 DARPA off-line intrusion detection
evaluation,” in Proceedings of the DARPA Information Survivability Conference
and Exposition. Los Alamitos, CA : IEEE Computer Society Press, 2000.
[Online]. Available : citeseer.ist.psu.edu/lippmann00evaluating.html
[48] R. Lippmann, J. W. Haines, D. J. Fried, J. Korba, and K. Das, “The 1999 darpa off-
line intrusion detection evaluation,” Comput. Networks, vol. 34, no. 4, pp. 579–595,
2000.
[49] D. V. McDermott, “A temporal logic for reasoning about processes and plans,”
Cognitive Science, pp. 101–155, 1982.
Bibliographie 148

[50] J. F. Allen and G. Ferguson, “Actions and events in interval temporal logic, Tech.
Rep. TR521, 1994. [Online]. Available : citeseer.ist.psu.edu/allen94actions.html
[51] F. Bacchus, J. Tenenberg, and J. Koomen, “A non-reified temporal logic,” in Pro-
ceedings of the First International Conference on Principles of Knowledge Repre-
sentation and Reasoning, R. Brachman, H. Levesque, and R. Reiter, Eds. Toronto,
Ontario, Canada : Morgan Kaufmann, May 1989, pp. 2–10.
[52] O. Lichtenstein and A. Pnueli, “Checking that finite state concurrent programs sa-
tisfy their linear specification,” in Proceedings of the 12th ACM SIGACT-SIGPLAN
symposium on Principles of programming languages, 1985.
[53] D. Kozen, “Results on the propositional mu-calculus,” Theoretical Computer
Science, vol. 27, pp. 333–354, 1983.
[54] E. A. Emerson and J. Y. Halpern, “Sometimes and not never revisited : on bran-
ching versus linear time temporal logic,” Journal of the ACM, vol. 33, no. 1, pp.
151–178, 1986.
[55] E. A. Emerson and C. L. Lei, “Modalities for model checking : Branching time
logics strikes back,” in Proceedings of the First Symposium on Logic in Computer
Science, Cambridge, June 1986, pp. 267–278.
[56] R. Alur and T. A. Henzinger, “A really temporal logic,” in IEEE Symposium on
Foundations of Computer Science, 1989.
[57] J. Ma and B. Knight, “Reified temporal logics : An overview,” Artificial Intelligence
Review, vol. 15, no. 3, pp. 189–217, May 2001.
[58] T. Wilke, “Ctl+ is exponentially more succinct than ctl,” in 19th Conf. Found.
of Software Technology and Theor. Comp. Sci., ser. Lecture Notes in Computer
Science, vol. 1738. Springer, 1999, pp. 110–121.
[59] G. J. Holzmann, “The model checker SPIN,” IEEE Trans. Softw. Eng., vol. 23,
no. 5, pp. 279–295, 1997.
[60] Z. Manna and A. Pnueli, “Verification of concurrent programs : The temporal
framework, in the correctness problem in computer science,” International Lecture
Series in Computer Science, 1981.
[61] D. M. Gabbay, “The declarative past and imperative future : Executable temporal
logic for interactive systems,” in Conference on Temporal Logic in Specification,
ser. Lecture Notes in Computer Science, H. B. Behnam Banieqbal and A. Pnueli,
Eds., vol. 398. Springer, 1989, pp. 409–448.
[62] P. Thati and G. Roşu, “Monitoring algorithms for metric temporal logic specifica-
tions,” in Runtime Verification 2004 (RV04), 2004.
[63] F. Laroussine and P. Schnoebelen, “A hierarchy of temporal logics with past,”
Theoritical Computer Science, vol. 48, no. 2, pp. 303–324, 1995.
Bibliographie 149

[64] N. M. François Laroussine and P. Schnoebelen, “Temporal logic with forgettable


past,” in Proceeding of the 17th Symposium on Logic in Computer Science (LICS
2002), I. C. S. Press, Ed., Copenhagen, Denmark, July 2002, pp. 383–392.
[Online]. Available : http://www.lsv.ens-cahan.fr/Publis/
[65] A. Tarski, “A lattice-theoretical fixpoint theorem and its applications,” Pacific
Journal of Mathematics, vol. 5, pp. 285–309, 1955.
[66] T. Henzinger, “The temporal specification and verification of real-time systems,”
Ph.D. dissertation, Stanford University, 1991.
[67] T. A. Henzinger, X. Nicollin, J. Sifakis, and S. Yovine, “Symbolic model checking
for real-time systems,” in 7th. Symposium of Logics in Computer Science, 1992.
[68] R. Koymans, “Specifying real-time properties with metric temporal logic,” Real-
Time Syst., vol. 2, no. 4, pp. 255–299, 1990.
[69] C. Ghezzi, D. Mandrioli, and A. Morzenti, “Trio : A logic language for executable
specifications of real-time systems,” J. Syst. Softw., vol. 12, no. 2, pp. 107–123,
1990.
[70] M. Zalewski, “Passive os fingerprinting,” http://lcamtuf.coredump.cx/p0f.shtml,
2000–2003.
[71] Fyodor, “Remote os detection via tcp/ip stack fingerprinting,”
http://www.insecure.org/nmap/nmap-fingerprinting-article.html, 1998.
[72] S. Stevenson, “Bibliography on prolog,” Department of Computer Science, Clemson
University, Clemson, SC 29634-1906, USA.
[73] A. DeMontigny-Leboeuf, “A multi-packet signature approach to passive opera-
ting system detection,” CRC/DRDC joint Technical Report CRC-TN-2005-001 /
DRDC-Ottawa-TM-2005-018, December 2004.
[74] “Bugtraq homepage,” http://www.securityfocus.org/.
[75] The NSS Group, “Intrusion prevention systems (ips),”
http://www.nss.co.uk/WhitePapers/intrusion prevention systems.htm, january
2004.
[76] Cisco Systems, “Cisco ios ips,” http://www.cisco.com/, 2004.
[77] Top Layer, “Network intrusion prevention solutions for the enterprise,”
http://www.toplayer.com/pdf/sbEnterprise.pdf.
[78] J. Buzbee, “Snort on the wrt54g,” http://www.batbox.org/wrt54g.html, September
2003.
[79] H. Bherer, “Synthèse de contrôleurs,” Département d’informatique, Université La-
val,” Rapport d’examen de synthèse, july 2001.
[80] R. Khédri, “Concurrence, bisimulations et équation d’interface : une approche rela-
tionnelle,” thèse de doctorat, Département d’informatique, Université Laval, august
1998.
Bibliographie 150

[81] M. Barbeau, F. Kabanza, and R. St.-Denis, “A method for the synthesis of control-
lers to handle safety, liveness, and real-time constraints,” IEEE Transactions on
Automatic Control, vol. 43, no. 11, pp. 1543–1559, Novembre 1998.
[82] R. St-Denis, “Designing reactive systems : integration of abstraction techniques
into a synthesis procedure,” J. Syst. Softw., vol. 60, no. 2, pp. 103–112, 2002.
[83] A. Lacasse, M. Mejri, and B. Ktari, “Formal implementation of network security
policies,”in Second Annual Conference on Privacy, Security and Trust,, Wu Centre,
University of New Brunswick, Fredericton, New Brunswick, Canada, October 2004.

S-ar putea să vă placă și