- Initialisation
- Lorsque le préprocesseur est déclaré dans "snort.conf", il analyse les arguments qui lui
sont passés et reconnait:
- src:         s'il doit se fier à l'adresse IP source.
- dest:        s'il doit se fier à l'adresse IP de destination.
- to:[xx]     avec [xx] définissant combien de temps il garde en mémoire une
IP inactive.
- nb:[xx]     avec [xx] définissant le nombre d'erreurs tolérées.
- ti:[xx]     avec [xx] définissant l'interval de temps durant lequel
les [nb] erreurs sont tolérées.
La ligne de snort.conf ressemble donc à ceci:
preprocessor http_404: -src -to:60 -nb:10 -ti:60
Remarques :
- Les options -src et -dest ne peuvent êtres utilisées en même temps;
- Pour détecter les floods côté client et surtout serveur, il est préférable d'utiliser -dest.
L'option -src était en fait surtout utile pour tester plusieurs adresses IP avec un seule machine.
- [to] et [ti] sont exprimés en secondes.
- Les paramètres [nb] et [ti] sont des ordres de grandeurs. Etant donné la nature des compteurs,
il est impossible de garantir que [nb] erreurs en [ti]+1 secondes ne seront pas alertées.
- Détection
- Le préprocesseur commence par vérifier qu'il s'agit d'un paquet de traffic TCP (donc que la
session TCP est ouverte). Si ce n'est pas le cas, il quitte directement.
Il vérifie ensuite qu'il s'agit d'une réponse HTTP, c'est-à-dire que les 4 premiers bytes de données du paquet
sont 'HTTP'. Etant donné que 4 'char' = 1 'int', il suffit de considérer que les données sont un tableau de 'int'
et de vérifier que le premier est 'PTTH' (car la machine sur laquelle nous avons testé est little endian).
Le début d'un paquet de réponse HTTP ressemble toujours à ceci (car c'est le serveur qui l'envoie) :
HTTP/1.1 404 [...]
Si on le découpe en 'int', on obtient:
|HTTP| |/1.1| | 404| [...]
Il suffit donc pour détecter une erreur 404 de comparer le 3eme int à '404_' (c'est à dire '_404'
inversé). La vérification qu'un paquet TCP est bien une erreur HTTP 404 est donc très rapide.
Remarque :
On ne peut pas optimiser la détection de cette manière si on teste les 'GET' sur les pages 'html'
car dans ce cas, le 'GET' est envoyé par l'attaquant, et peut donc être 'gEt' si ca l'amuse.
- Comptage
- Une fois l'erreur détectée, il faut l'ajouter à un compteur pour ne lancer une alerte que
quand un certain seuil est dépassé. De plus, pour que la détection soit efficace, il faut qu'il
y ait un compteur par adresse IP.
Pour cela, le préprocesseur insère chaque compteur dans une liste doublement liée circulaire ordonnée
par adresse IP croissante. La liste des champs d'une cellule de la liste est :
- unsigned int IP_Addr;      // L'adresse IP
- double Time;        // La date et l'heure de la dernière erreur 404.
- float Count;          // Le compteur.
- void *Prev;          // Un pointeur vers la cellule précédente.
- void *Next;          // Un pointeur vers la cellule suivante.
Pour simplifier la gestion de la liste, on y insère par défaut une cellule avec l'adresse IP
égale à 0 de sorte que la liste ne soit jamais vide.
Il y a aussi un pointeur global qui pointe vers la cellule courante dans la liste.
Lorsque l'erreur 404 est détectée, le préprocesseur parcours la liste depuis la cellule courante
jusqu'à ce qu'il tombe sur la bonne adresse IP, ou qu'il dépasse l'endroit où elle aurait du se
trouver (ce qui est faisable vu que la liste est ordonnée par IP croissante). Vu que la liste est
doublement liée, on peut choisir le sens de parcours pour minimiser la distance entre l'adresse IP
recherchée et celle se trouvant dans la cellule courante.
Lors de cette recherche, si le préprocesseur tombe sur une cellule dont l'adresse IP ne correspond
pas et dont le champ 'Time' est inférieur à l'heure actuelle - [to], il retire la cellule de la liste
(sauf s'il s'agit de l'adresse IP 0). Cela permet d'élaguer la liste pour ne garder en mémoire que
les adresses IP actives, sans pour autant faire un tour complet de la liste à chaque fois.
Une fois la cellule trouvée (ou créée), le préprocesseur met à jour la valeur du compteur par la formule suivante :
Count=Count*exp(K1*(Now-Time))+K2;
Ensuite, il la compare à K3 et si elle plus grande, il lance une alerte et retire la cellule de la
liste.
Les valeurs de K1,K2 et K3 sont calculées à partir des paramètres [nb] et [ti].
Le préprocesseur retire la cellule de la liste pour limiter le nombre d'alertes lancées.
On pourrait toutefois envisager une autre stratégie, du style remettre le compteur à 0, ou désactiver
cette adresse pendant un certain temps, ...
Remarque :
Le calcul de K1,K2 et K3 est basé sur le fait qu'on peut calculer K1 et K2 pour que C ne dépasse
jamais une certaine constante si (Now-Time) est constant. En calculant ces valeurs avec Cmax=[ti]
et (Now-Time) = [ti]/[nb], on arrive a faire un compteur qui ne dépassera [ti] que si le nombre
d'erreurs moyen sur un temps [ti] est plus grand que [nb].
Le problème, c'est que si le nombre d'erreurs moyen est légèrement supérieur à [nb], le nombre
d'erreurs nécessaires pour dépasser [ti] peut être très grand. C'est pour cela que pour calculer
K1 et K2, on utilise un certain pourcentage de [nb].