Pour citer Richard Bejtlich [1] : « Un réseau défendable est un réseau où on voit quelque chose ». Cette maxime simple et communément admise résume à elle seule la notion de Network Security Monitoring (par la suite abrégé en NSM), à savoir un audit orienté sécurité des événements réseau. Cet article va vous décrire la mise en œuvre d'une plateforme intégrant les outils Dumpcap, Argus, Bro, Snort et Snorby.
Le NSM est une discipline mêlant les aspects instrumentation réseau (collecte de trafic) et détection d'intrusions, essentiellement via des NIDS (Network Intrusion Detection Systems) type Snort. La démarche générale est de partir d'une alerte levée par un des NIDS et de déterminer la source ainsi que la cible de l'attaque. Si la source est distante et que la cible est locale on parle alors d'intrusion [2]. Si la source est locale et que la cible est distante, on parle alors d'extrusion [3]. L'extrusion est souvent la conséquence d'une intrusion réussie (intrusion pas forcément véhiculée par le réseau, mais aussi potentiellement de source « clé USBesque »). Une fois l'incident isolé, il faut remonter dans le temps pour examiner les interactions des différents protagonistes (analyse « post mortem » - ou forensic [4] - réseau). Les aspects stockages sont donc également déterminants pour la durée de rétention des événements.
En résumé, nous avons cinq idées fortes :- Nature des données à collecter ;- Instrumentation du réseau ;- Placement de la sonde ;- Visualisation des événements ;- Forensic réseau.
1. Nature des données à collecter
La première question à se poser est : que veut-on voir ? En clair, quelles données doit-on collecter ? Dans le NSM, on distingue sept types de données à collecter. Chaque type de données est plus ou moins volumineux, les durées de rétention varient donc d'un type à l'autre. Nous allons les passer en revue de la plus complète (donc plus volumineuse en termes de stockage) à la plus synthétique (donc la moins volumineuse).
1.1 Capture complète
La capture complète récupère l’intégralité des données transitant sur le réseau. Ce type de capture est une base pour tous les outils composant la couche NSM. Selon le type d'information qu'on souhaite exploiter, ces outils vont utiliser tout ou partie des données remontées par une capture complète. L'outil le plus connu pour réaliser une capture complète est tcpdump. Si on veut stocker les données d'une capture complète, tcpdump propose de stocker les trames réseau dans un fichier au format PCAP.
Un autre outil moins connu pour acquérir du PCAP est dumpcap. Ce logiciel est issu de la suite Wireshark. Par rapport à tcpdump, il est spécialisé dans l'écriture de fichiers PCAP. Il intègre également un mécanisme de rotation de ces fichiers sur base de taille ou horaire. Par exemple :
/usr/bin/dumpcap -i eth1 -w /store/pcap/dump.eth1.pcap -b files:100 -b filesize:102400
Ici, dumpcap capture le trafic depuis l'interface eth1 (-i), écrit les paquets au format PCAP dans le fichier dump.eth1.pcap (-w) avec une rotation sur 100 fichiers (-b files:100) déclenchée lorsque le fichier en cours d'écriture arrive à 100 Mi (-b filesize:102400). Nous sommes donc à une taille de rétention du PCAP à 10 Gi. Dans les fichiers PCAP, on trouve les en-têtes des différents protocoles réseau (MAC, IP et TCP/UDP), ainsi que l'intégralité du payload applicatif (niveaux 5, 6 et 7 de l'OSI).
Pour une visualisation en mode texte, tcpdump et tshark sont les outils les plus répandus. En mode graphique, Wireshark est assez commode, car il embarque (entre autres) un système de reconstruction des sessions TCP et aussi des processeurs pour un certain nombre de protocoles applicatifs.
1.2 Contenu extrait des données
Plus précisément, on peut parler du contenu extrait du payload applicatif. Ici, il n'est pas question de localiser les interactions réseau, mais de récupérer les « objets » transportés par les protocoles applicatifs (pages web, images, vidéos, etc.). Les outils opérant à ce niveau sont complémentaires de ceux réalisant une capture complète. L'idée est de faire travailler les outils extrayant les contenus applicatifs sur les PCAP capturés en 2.1.
Un exemple d'outil est Xplico, qui propose via une interface web de charger un fichier PCAP pour en extraire les différents objets. Un autre outil en mode texte est tcpflow qui va également extraire des fichiers PCAP les archives compressées, pages HTML, documents XML, etc.
1.3 Données de session
À ce niveau, on cherche à savoir qui a parlé avec qui et la durée de leur discussion, peu importe le langage qu'ils ont utilisé. En termes de réseau, cela se traduit par récupérer les informations jusqu'au niveau quatre du modèle OSI, c'est-à-dire les adresses source et destination, le(s) protocole(s) utilisé(s) niveau liaison en y incluant éventuellement des éléments de volumétrie (nombre d'octets et/ou de paquets échangés).
Les équipements réseau peuvent être capables de faire du NetFlow. Dans ce cas, en installant un collecteur NetFlow sur la sonde, on récupère les informations de flux de manière centralisée sur la sonde. NetFlow est hors du périmètre de cet article. Cependant, c'est une solution qu'il faut avoir à l'esprit si pour une raison X ou Y les flux ne sont pas accessibles par la sonde (pas de budget, pas de possibilité d'installer un dispositif supplémentaire, etc.).
Il existe des logiciels qui capturent les flux depuis une interface réseau. Nous allons en utiliser deux : Argus et Bro. Argus est dédié à l'analyse/archivage de flux. Il conserve une trace de chaque interaction (sans le payload évidemment). Comme son travail est relativement simple et spécialisé, il consomme peu de CPU. Bro est plus versatile ; il suit les connexions, mais ne logue pas chaque interaction comme Argus. De plus, il effectue une analyse sur les couches au-dessus de la couche transport. En conséquence, il consomme beaucoup plus de CPU. Les deux outils sont complémentaires.
Argus :
ngreneche@sonde:/store/argus$ racluster -m proto saddr daddr dport -r /store/argus/argus.log -n - src host X.X.X.X
StartTime Flgs Proto SrcAddr Sport Dir DstAddr Dport TotPkts TotBytes State
14:48:17.462622 e tcp X.X.X.X.52424 -> A.A.A.A.443 22 2324 CON
14:49:17.033272 e tcp X.X.X.X.47349 -> B.B.B.B.80 14 6336 FIN
14:49:15.131391 e g tcp X.X.X.X.0 -> C.C.C.C.80 14 956 FIN
14:48:42.420490 e tcp X.X.X.X.42569 -> D.D.D.D.443 35 18650 FIN
Bro :
ngreneche@sonde:/store/bro/logs/current$ bro-cut < conn.log | awk '$3 == "X.X.X.X" { print $0 }'
1396015462.924918 CkqqS530cVJ7qT36gb X.X.X.X 37392 A.A.A.A 443 tcp ssl 10.010951 613 258 RSTO F 0 ShADadR 7 997 6 623 (empty)
1396015484.254693 CnVWEb4SQQxJYVdrDh X.X.X.X 51477 B.B.B.B 80 tcp http 8.656992 1767 36169 SF F 25040 ShADacdfF 15 2100 15 16141 (empty)
1396015484.045241 Ci7eBuYw4EJ1DTwd5 X.X.X.X 51474 C.C.C.C 80 tcp - 8.866448 2228 98669 SF F 0 hAadcDfF 22 2481 27 33612 (empty)
On voit que les deux outils remontent plus ou moins les mêmes informations pour la partie session. Cependant, rien qu'en se limitant au niveau session (fichier conn.log de Bro), Bro nous indique en plus si le flux est chiffré ou pas.
1.4 Données de transaction
Au niveau transaction, les données sont les mêmes qu'au niveau session avec, en plus, une interprétation du protocole encapsulé dans la couche transport (par exemple HTTP). Ce niveau est extrêmement riche en informations tout en restant assez compact au niveau de l'espace de stockage nécessaire. Bro stocke tous les flux dont il a réussi à déterminer le protocole applicatif dans des fichiers dédiés. Pour une session HTTP (fichier http.log) :
ngreneche@sonde:/store/bro/logs/current$ bro-cut < http.log | awk '$3 == "X.X.X.X" { print $0 }'
1396019832.360085 Ctbajh10aLAE6ZIXg9 X.X.X.X 40817 Y.Y.Y.Y 80 1 GET z.cdn.turner.com /nba/nba/.element/js/2.0/global/adspaces.js http://www.nba.com/ Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/32.0.1700.107 Chrome/32.0.1700.107 Safari/537.36 0 0 304 Not Modified - - - (empty) - - - - - - -
1.5 Données statistiques
Les données collectées à ce niveau sont assez macroscopiques. Elles donnent une vision globale du réseau. On y trouvera, par exemple, le nombre de paquets envoyés et reçus, la répartition des protocoles de niveau trois et quatre, ou encore la taille moyenne des paquets échangés, etc. Ces données peuvent être collectées depuis des fichiers PCAP avec des outils comme Wireshark.
1.6 Métadonnées
Les métadonnées sont les données sur les données. Par exemple, on pourra récupérer les informations WHOIS pour une IP, la géolocaliser, cartographier l’architecture DNS de son domaine, etc. Finalement, cette phase va utiliser les outils de la phase de footprinting [5] d'une intrusion. Cette phase consiste à acquérir des données sur la cible.
1.7 Alertes
Les alertes sont remontées par les NIDS. Elles sont levées suite à la détection d’événements suspects sur le réseau. Le NIDS le plus populaire dans le monde du libre est Snort. La figure 1 présente la chaîne de traitement du trafic par un NIDS.
Fig. 1 : Architecture d'un IDS
Le NIDS récupère le trafic depuis le lien réseau via la E-Box. Le fonctionnement est analogue à un sniffeur réseau type tcpdump. Les paquets récupérés sont ensuite passés à une A-Box composée le plus souvent de deux éléments : un jeu de règles et un préprocesseur. Le jeu de règles modélise les comportements malveillants de plusieurs malwares. Des signatures (contenu des règles) sont superposées aux paquets pour déterminer s'ils contiennent une charge malveillante. Certaines actions malveillantes sont plus compliquées à détecter, car elles nécessitent un peu d'intelligence pour être mises en lumière [6] (attaques fragmentées, scans). Pour cela, les NIDS implémentent des préprocesseurs qui effectuent des corrélations à partir du trafic pour lever des alertes. Une fois ces alertes levées, elles sont stockées par la D-Box dans un backend quelconque (fichier plat, fichier binaire, SGBD, etc.).
Si le NIDS est utilisé dans une optique de protection, on parle alors d'IPS (Intrusion Prevention System). Le « P » est implémenté via la C-Box qui déclenche une ou plusieurs action(s) de protection à la suite de la levée d'une alerte. Par exemple, une règle peut être ajoutée dans le pare-feu du site.
2. Instrumentation du réseau
Comme base de toute activité de NSM, il est nécessaire de collecter du trafic en gardant toujours en mémoire la notion d'imputabilité. Au niveau réseau strict, l'imputabilité se résume à l'IP source réelle du trafic : l'identité de la personne connectée sur la machine relève du niveau applicatif. Cette affirmation peut cependant être discutée avec l'introduction du NAC (Network Access Control), qui réalise une authentification avant l'accès physique au réseau. Il n'est pas forcément trivial de capturer le trafic avec la source réelle, à cause de l'utilisation du NAT (Network Address Translation). Le NAT est un mécanisme substituant l'IP source d'un paquet par une autre IP. Le NAT est souvent utilisé pour faire sortir des machines d'un réseau privé vers Internet (on substitue une adresse source privée non routable sur Internet par une IP publique qui, elle, est routable sur Internet). Se pose donc la question de placer les points d'instrumentation.
2.1 Récupération du trafic
La récupération du trafic peut se faire de façon intrusive ou non. Une récupération intrusive implique une configuration des équipements réseau. Dans beaucoup de cas, il s'agit d'une recopie de port, ou port mirroring. Le port mirroring consiste à recopier le trafic transitant par un port vers un autre port dedié à l'audit qui sert de miroir. En capturant le trafic du port miroir, on récupère le trafic du port supervisé. Cette solution est économiquement intéressante, car elle n'implique aucun coût supplémentaire, cependant elle comporte aussi des inconvénients. Lorsque l’équipement est saturé, le port mirroring est une des premières choses qui saute pour libérer du CPU. De plus, le port mirroring étant configuré par l'administrateur sur l'équipement, il peut aussi être désactivé par un pirate ayant pris le contrôle de ce dernier.
Ces inconvénients peuvent pousser l'administrateur à envisager une instrumentation non intrusive donc décorellée de l'équipement réseau. Il existe des boîtiers TAP (Test Access Port), qui se mettent en coupure du média réseau (fibre ou câble RJ45) afin de recopier le trafic sur une ou plusieurs interface(s).
Fig. 2 : Network TAP
La figure 2 illustre le placement d'un boîtier TAP entre le LAN et Internet. On voit que le LAN et Internet sont connectés via le boîtier TAP. Sans instrumentation, on aurait un simple uplink du commutateur (LAN) vers le routeur de sortie (Internet). Avec instrumentation, on place un TAP en coupure. Si le TAP est éteint, le trafic traverse le TAP et le réseau fonctionne sans soucis (une panne sur un TAP ne doit pas couper le réseau). Si le TAP est alimenté, les flux montants et descendants sont recopiés sur deux ports distincts du TAP (il existe des modèles qui agrègent ces deux flux pour les recopier sur un seul port). Il suffit ensuite de créer un bridge constitué de deux interfaces réseau sur la machine réalisant la capture.
2.2 La sonde
Une sonde est un point d'instrumentation sur le réseau. Physiquement, une sonde peut avoir une multitude d'incarnations : un PC standard avec une interface dédiée à la capture et une pile de logiciels d'analyse (libres ou non), un routeur avec un collecteur NetFlow configuré, un boîtier commercial dédié à l'analyse, ou encore un UTM (Unified Threat Management) embarquant des fonctionnalités IDS, etc.
Pour instrumenter un réseau, on place des équipements appelés sondes à différents endroits. Sur un réseau très simple, on peut se contenter de placer une sonde sur le lien externe du réseau. Dans des topologies plus compliquées (impliquant souvent du NAT), ou tout simplement pour des questions de volumétrie de données, on peut avoir à déployer plusieurs sondes sur le réseau.
3. Placement de la sonde
Une fois les aspects théoriques dégagés, nous pouvons composer notre solution de NSM. La première question à se poser est : qu'est-ce que je veux loguer (cf. section 1) ? Notre objectif derrière la mise en place du NSM est d'être capable d'imputer une alerte de sécurité à une machine dans un contexte où nous ne voyons que des adresses de NAT (c'est-à-dire que la source qui nous est présentée est une adresse de NAT derrière laquelle se trouvent X adresses IP).
3.1 Topologie
Notre réseau est divisé en plusieurs composantes relativement autonomes. C'est-à-dire que nous fournissons à chaque composante plusieurs adresses publiques pour gérer leur connectivité à Internet. Cependant, nous devons quand même essayer de les superviser au niveau sécurité. Notre mission est d'essayer de détecter les flux malveillants entre les composantes et Internet. Les problèmes de sécurité intra composante ne sont pas de notre champ d'investigation. Délimiter le périmètre et placer un objectif est le préliminaire essentiel à toute activité de NSM. La maxime « aller à la pêche aux informations » est synonyme d’échec programmé.
Nous allons donc placer plusieurs sondes, une à notre niveau de vision (c'est-à-dire entre Internet et les composantes) pour détecter les problèmes de sécurité et avoir une vision globale des interactions entre nos composantes et Internet, et une autre dans chaque composante pour récupérer leurs données de session/transaction.
Fig. 3 : Topologie
Sur la figure 3, nous voyons deux niveaux de sondes : (A) et (B,C). La sonde A doit garder les données de session/transaction et lever des alertes de sécurité. Les sondes B et C qui sont situées avant les NAT des composantes récupèrent les données de session/transaction avec les « vraies » sources des interactions réseau. Ainsi, la chaîne de traitement d'une alerte de sécurité sera :
- Levée d'une alerte via NIDS sur la partie interconnexion (sonde A) ;
- Récupération de la source en cas d'extrusion ou de destination en cas d'attaque. Ces adresses sont des NAT au niveau interconnexion ;
- Consultation des logs de session/transaction des sondes pour trouver la destination réelle de l'agression ou la source de l'extrusion.
3.2 Suite logicielle
Nous souhaitons garder des logs de capture complets sur une petite durée, des données de session et de transaction pour investiguer sur les sources des alertes levées par un NIDS. Nous allons utiliser la suite de logiciels suivante : dumpcap (capture complète), argus (session), bro (session/transaction) et snort (NIDS). La sonde est installée avec une Debian Wheezy.
4. Configuration des outils orientés réseau
Les outils que nous allons installer vont tous écouter sur l'interface eth1, qui a été configurée en mode promiscuous. Lorsque l'interface réseau reçoit un paquet, son comportement par défaut est de le jeter s'il n'est pas destiné à une interface de la machine. Quand on fait du NSM, on veut analyser tous les paquets qui arrivent sur l'interface, il faut donc tous les remonter.
Fig. 4 : Mode promiscuous
Lorsque l'interface réseau reçoit un paquet, elle envoie une requête d'interruption au noyau (IRQ). Cette requête est prise en charge par le gestionnaire d’interruption (interrupt handler). Le gestionnaire d'interruption est séparé en deux : « top halve » et « bottom halve ». Lorsque le noyau reçoit une interruption, il arrête ce qu'il fait pour basculer dans un mode particulier. Le traitement de l'interruption doit donc être très court. Ainsi, le gestionnaire d'interruption comporte une partie « top halve » qui récupère le paquet de l'interface, le copie en mémoire et en acquitte la bonne réception. Cette partie passe ensuite la main au « bottom halve » qui va faire les actions de plus haut niveau (comme passer le paquet à la pile réseau). Contrairement au « top halve », le « bottom halve » est déferrable. C'est-à-dire que les travaux sont mis en attente dans une file et peuvent être traités plus tard (alors que la copie et l’acquittement du paquet doivent être faits le plus rapidement possible).
Pour passer une interface en mode promiscuous, il faut modifier les /etc/network/interfaces :
auto eth1
iface eth1 inet manual
up ifconfig eth1 promisc up
down ifconfig eth1 promisc down
Nous sommes maintenant prêts à installer les outils sur la sonde.
4.1 Dumpcap
Cet outil fait partie de la suite Wireshark et il est entièrement dédié à la capture de PCAP. Il ne fait que ça en intégrant un mécanisme de rotation, ce qui est extrêmement pratique pour ne pas saturer le disque de la sonde. Une utilisation moyenne de notre réseau se situe à un débit de presque 300 Mo/s. Pour l'installer, nous sommes passés par le gestionnaire de paquets :
# apt-get install wireshark-common
Une fois le logiciel installé, il suffit de l'invoquer (pas de fichier de configuration) :
# /usr/bin/dumpcap -i eth1 -w /store/pcap/dump.eth1.pcap -b files:100 -b filesize:102400
On lance dumpcap en capture sur eth1 (-i), les fichiers seront nommés dump.eth1_numero_date.pcap (-w). La rotation est configurée sur 100 fichiers (-b files:100) de 100 Mi (-b filesize:102400). Vu le débit annoncé, la durée de rétention du PCAP est vraiment très courte.
4.2 Argus
Nous avons compilé Argus à partir des sources. Il faut commencer par récupérer les sources sur le site de QoSient [7]. Argus est divisé en deux parties : un exécutable qui écoute sur l'interface pour écrire les fichiers de logs, et des outils clients pour analyser ces logs. On commence par compiler la partie serveur (Argus-3.0.6.1 au moment de l'article) :
# ./configure –prefix=/usr/local/argus && make && make install
On compile et installe Argus avec un préfixe personnalisé. Pour la partie cliente (Argus-clients-3.0.6.2) c'est pareil :
# ./configure –prefix=/usr/local/argus-clients && make && make install
On rédige un argus.conf minimal :
ARGUS_DAEMON=yes
ARGUS_INTERFACE=eth1
ARGUS_OUTPUT_FILE=/store/Argus/argus.log
ARGUS_SET_PID=yes
ARGUS_PID_FILENAME=/var/run/argus.pid
On indique à Argus de se lancer en mode démon (ARGUS_DAEMON), de récupérer son trafic sur eth1 (ARGUS_INTERFACE), d'écrire ses logs dans /store/argus/argus.log (ARGUS_OUTPUT_FILE) et de créer son fichier PID (ARGUS_SET_PID) en indiquant le chemin vers celui-ci (ARGUS_PID_FILENAME). Une fois Argus installé, on peut lancer le service :
# /usr/local/argus/sbin/argus -F /usr/local/argus/etc/argus.conf
On notera que la rotation des logs Argus est très simple. Lorsque le fichier argus.log est absent au moment où Argus veut écrire dedans, il recrée un nouveau fichier. Ainsi avec logrotate, il suffit de créer le fichier suivant :
/store/argus/argus.log {
daily
missingok
dateext
rotate 31
}
Cette configuration garde 31 jours de logs Argus.
4.3 Bro
Nous avons compilé Bro à partir des sources (version 2.2 au moment de l'article) :
# ./configure –prefix=/usr/local/bro && make && make install
Nous le configurons en mode « standalone », c'est-à-dire qu'il fonctionne de manière autonome sur la sonde et pas sur un modèle agent/collecteur. On définit aussi l'hôte sur lequel il s’exécute et l'interface sur laquelle acquérir le trafic. Tout cela se passe dans le /usr/local/bro/etc/node.cfg :
[Bro]
type=standalone
host=localhost
interface=eth1
Ensuite, il faut configurer les options de broctl. broctl est un shell qui facilite le management du service Bro. Grâce à de simples commandes, il démarre, arrête, redémarre ou renvoie des informations sur le processus Bro en exécution. Pour que broctl fonctionne, il faut lui fournir des informations sur notre installation. Cela se passe dans le fichier /usr/local/bro/etc/broctl.cfg :
MailTo = admin@domaine.fr
SitePolicyStandalone = local.Bro
CfgDir = /usr/local/Bro/etc
SpoolDir = /store/Bro/spool
LogDir = /store/Bro/logs
LogRotationInterval = 3600
LogExpireInterval = 30
MinDiskSpace = 5
On lui donne le mail de l'administrateur (MailTo), la politique du site (SitePolicyStandalone, essentiellement les moteurs d'analyse de protocole type HTTP, DNS, etc.), le répertoire contenant les fichiers de configuration (CfgDir), celui contenant les fichiers générés à l'initialisation de Bro via la commande broctl install (SpoolDir) et enfin, celui contenant les traces des interactions réseau (LogDir). Ces traces sont éclatées en fichiers, nous allons avoir un fichier avec les données de session (conn.log) et un fichier de transaction par protocole (dns.log, http.log, ssh.log, etc.). Enfin, les paramètres de rotation des logs : durée de vie du fichier (LogRotationInterval), profondeur (LogExpireInterval) et espace minimal disponible sur la partition avant d'envoyer un message à l'administrateur (MinDiskSpace).
Pour démarrer une instance de Bro, il faut déjà initialiser le répertoire spool avec la commande broctl install :
# broctl install
removing old policies in /store/bro/spool/installed-scripts-do-not-touch/site ... done.
removing old policies in /store/bro/spool/installed-scripts-do-not-touch/auto ... done.
creating policy directories ... done.
installing site policies ... done.
generating standalone-layout.bro ... done.
generating local-networks.bro ... done.
generating Broctl-config.bro ... done.
updating nodes ... done.
Ensuite, il suffit de faire un simple broctl start :
# Broctl start
starting Bro ...
5. Configuration du NIDS
Une fois la configuration des différents logiciels s'occupant de l’acquisition des traces réseau effectuée, nous allons passer à la configuration du NIDS Snort. Snort est un programme qui écoute sur une interface réseau afin de confronter le trafic à tout un jeu de règles pour lever des alertes en cas de concordance entre une charge applicative et une signature modélisant un comportement malveillant.
5.1 Architecture de Snort
L'architecture que nous souhaitons mettre en place est d'avoir un Snort en écoute sur une interface réseau et de pouvoir visualiser les alertes dans une interface web. Nous allons utiliser l'interface Snorby.
Fig. 5 : Architecture de Snort
Lorsque Snort détecte une charge applicative suspecte, il génère deux sorties : une alerte et un enregistrement au format PCAP du (ou des) paquet(s) l'ayant générée. Ce stockage peut se faire sous forme de fichiers plats, ou au format unified2 qui est binaire mais qui optimise les performances en écriture. Le fait de vouloir utiliser Snorby nous oblige à stocker ces alertes dans une base de données MySQL. Pour ce faire, nous utilisons Barnyard2 qui « poll » (c'est-à-dire qui surveille les écritures) le fichier au format unified2 pour injecter les alertes dans une base MySQL.
5.2 Installation des dépendances
La première des choses à faire est d'installer la libdnet [8]. Celle-ci présente une interface à Snort pour manipuler les éléments réseau (adresses IP, tables ARP, etc.). Pour la compiler et l'installer, c'est du classique :
# ./configure –prefix=/usr/local/libdnet-1.12 && make && make install
Ensuite, il faut installer DAQ (Data AQuisition library). Cette librairie se place entre Snort et les appels à la libpcap pour pouvoir inter-opérer ce dernier avec quantité d'interfaces logicielles (autre format que le PCAP) et matérielles (dispositifs hardware dédiés à la capture). Les sources de la librairie DAQ se récupèrent sur le site de Snort. Il faut passer le chemin de la libdnet dans la chaîne de compilation :
# ./configure --prefix=/usr/local/daq-2.0.2 --with-dnet-includes=/usr/local/libdnet/include --with-dnet-libraries=/usr/local/libdnet/lib --disable-static-daq && make && make install
Enfin, il faut compiler Barnyard2 [9]. Nous verrons son exécution au moment où nous allons installer Snorby, car c'est lui qui va s'occuper de loguer les alertes dans MySQL. La compilation nécessite en préambule l’exécution d'un script autogen.sh.
./autogen.sh
./configure --prefix=/usr/local/barnyard2 --with-mysql-libraries=/usr/lib/x86_64-linux-gnu
5.3 Installation et configuration de Snort
Une fois les sources de Snort récupérées, la compilation n'est pas très compliquée :
# ./configure --prefix=/usr/local/snort-2.9.6.0 --with-dnet-includes=/usr/local/libdnet/include --with-dnet-libraries=/usr/local/libdnet/lib --with-daq-includes=/usr/local/daq/include –with-daq-libraries=/usr/local/daq/lib
# make && make install
Pour démarrer tranquillement et tester Snort, on peut récupérer un jeu de règles simple : les community rules.
# wget https://s3.amazonaws.com/snort-org/www/rules/community/community-rules.tar.gz
Une fois ces règles téléchargées, nous pouvons tester notre installation de Snort. Allons dans le fichier local.rules de l'archive compressée téléchargée précédemment. Nous allons ajouter une règle qui lève une alerte sur le passage d'un paquet ICMP :
alert icmp any any -> $HOME_NET any (msg:"ICMP test"; sid:10000001; rev:1;)
Ensuite, lançons Snort :
# /usr/local/bin/snort -dev -i eth1 -l /store/snort/logs --pid-path /var/run -c /usr/local/snortrules/etc/snort.conf -D
On indique à Snort de récupérer la charge applicative des paquets (-d), ainsi que les informations de niveau 2 (-e). Sans ces deux paramètres, il est impossible de traiter les couches applicatives et physiques dans les alertes. Nous lui donnons également l'emplacement de ses logs (-l), en l’occurrence le fichier alert (contenant les alertes) et snort.log (contenant les paquets ayant levé une alerte au format PCAP). Nous lui indiquons également l'emplacement du répertoire où stocker son fichier PID (--pid-path) et l'emplacement de son fichier de configuration (-c). On notera que nous utilisons le fichier de configuration fourni dans les règles Snort « community ». Enfin, nous disons à Snort de se lancer en arrière-plan (-D).
Il y a quelques petits ajustements à faire sur la configuration (snort.conf) selon votre installation :
config policy_mode: tap
Cela indique à Snort qu'il fonctionne en mode passif, c'est-à-dire qu'il ne fait qu'examiner le trafic. Ici, on construit une sonde et non un IPS (cf. figure 1, la C-Box).
config set_gid: snort
config set_uid: snort
Ici, on configure l'identité d’exécution du processus Snort ; on aura préalablement créé l'utilisateur avec la commande suivante :
# useradd -r snort
Une fois Snort démarré, nous pouvons générer un paquet ICMP (par exemple avec la commande ping). Nous devrions voir arriver une nouvelle ligne dans le fichier alert :
[**] [1:10000001:0] ICMP test [**]
[Priority: 0]
03/11-14:30:26.350549 XX:XX:XX:XX:XX:XX -> AA:AA:AA:AA:AA:AA type:0x86DD len:0x56
XXXX::BBBB:UUUU:KKKK:ZZZZ -> HHHH::S:FFFF:DDDD IPV6-ICMP TTL:1 TOS:0x0 ID:256 IpLen:40 DgmLen:72
Et la trace au format PCAP correspondante dans le fichier snort.log :
15:30:26.350549 IP6 XXXX::BBBB:UUUU:KKKK:ZZZZ > HHHH::S:FFFF:DDDD HBH ICMP6, multicast listener reportmax resp delay: 0 addr: HHHH::S:FFFF:DDDD, length 24
Enfin, on supprime la règle et on relance Snort. Nous voilà avec une plateforme de NSM fonctionnelle !
6. Exploitation de la sonde
Une fois le système mis en place, il faut encore l'exploiter. Cette section va présenter quelques exemples d'utilisation des logiciels utilisés au niveau session et transaction : Bro [10] et Argus [11]. Nous allons également vous présenter Snorby, qui offre une excellente visualisation des alertes levées par Snort.
6.1 Niveau session/transaction
L'architecture de ces logiciels est toujours la même. Un processus en arrière-plan qui collecte les données sous forme de fichiers et une suite d'outils clients qui exploitent ces données (cf. figure 6).
Fig. 6 : Logiciels session/transaction
6.1.1 Niveau session
Nous allons étudier le niveau session avec Argus en utilisant trois outils clients de la suite Argus. La suite contient une quantité d'outils, mais ces trois-là sont très représentatifs de ce que l'on peut tirer d'Argus :
- ra : l'outil sur lequel tout est bâti, il ouvre un fichier de logs et renvoie tous les enregistrements correspondant aux critères qu'on lui passe ;
- racluster : sensiblement le même outil que ra, sauf qu'il est possible de lui passer des clés pour, par exemple, sortir des résultats agrégés par adresses sources, protocoles, etc. ;
- ragraph : ce dernier outil trace des graphiques depuis les logs Argus.
Commençons par récupérer toutes les interactions ayant pour source X.X.X.X :
# ra -nnr argus.log - src host X.X.X.X
StartTime Flgs Proto SrcAddr Sport Dir DstAddr Dport TotPkts TotBytes State
14:21:12.985732 * 6 X.X.X.X.38083 -> A.A.A.A.443 4 295 FIN
14:21:13.657142 * 6 X.X.X.X.60488 -> B.B.B.B.80 7 1909 CON
14:21:17.189515 * 6 X.X.X.X.22 ?> C.C.C.C.1146 1 64 FIN
14:21:20.669893 * 6 X.X.X.X.37968 -> D.D.D.D.80 10 1899 FIN
La commande ra ne fait aucun travail d’agrégation, les interactions d'une même connexion sont présentées indépendamment. Nous demandons à ra de ne faire aucune résolution DNS et service (-nn), nous lui passons le fichier de log à examiner (-r) et le filtre BPF (Berkley Packet Filter) src host X.X.X.X. Dans la sortie, on trouve notamment : un horodatage, le protocole utilisé (6 = TCP), les couples adresse/port source et destination, le nombre de paquets et le volume de données échangées. On note une interaction étrange : on a un paquet à destination d'une machine C.C.C.C depuis le port 22 (SSH) de ma machine. Or, C.C.C.C n'est pas dans mon réseau. Après une vérification « whois » sur C.C.C.C, il s'agit d'une IP basée en Chine.
On peut regarder avec une commande racluster qui agrège sur l'adresse source (-m saddr) ce qu'a exactement fait la machine chinoise :
# racluster -m saddr -nr argus.log - src host C.C.C.C
StartTime Flgs Proto SrcAddr Sport Dir DstAddr Dport TotPkts TotBytes State
14:09:14.957390 *U*S tcp C.C.C.C.0 -> X.X.X.0.22 1952 351146 RST
On voit que cette machine tente des connexions sur les ports 22 des machines du réseau de X.X.X.X (X.X.X.0 car X.X.X.X est sur un réseau en /24). Regardons son activité dans le détail :
# racluster -nr argus.log - src host C.C.C.C
StartTime Flgs Proto SrcAddr Sport Dir DstAddr Dport TotPkts TotBytes State
14:09:14.957390 * tcp C.C.C.C.6000 -> X.X.X.A.22 1 64 REQ
14:09:14.958176 * tcp C.C.C.C.6000 -> X.X.X.B.22 1 64 REQ
14:09:14.958385 * tcp C.C.C.C.6000 -> X.X.X.C.22 2 128 RST
14:19:19.038443 * d tcp C.C.C.C.1839 -> X.X.X.D.22 53 9617 FIN
14:19:19.038443 * d tcp C.C.C.C.1839 -> X.X.X.D.22 53 9617 FIN
14:20:30.232315 * d tcp C.C.C.C.2732 -> X.X.X.D.22 48 8091 FIN
Au vu de l'activité, pour les trois premières lignes rien à signaler. Il y a un ou deux paquet(s), ce qui indique que la connexion n'a pas lieu. Par contre, la machine X.X.X.D semble avoir échangé pas mal de paquets avec la machine chinoise. Il va falloir vérifier dans les journaux de log SSH. Terminons pour le niveau session avec ragraph. On peut commencer par grapher le débit global sur un laps de temps avec un échantillonnage de cinq secondes :
# ragraph bytes -M 5s -r argus.log -w ra-debit.png
Fig. 7 : Débit global
Récupérons maintenant la bande passante utilisée par le service SSH (sur le port 22) :
# ragraph bytes -M 5s -r argus.log -w ra-ssh.png - dst port 22
Fig. 8 : Bande passante SSH
Ou encore le Web (port 80 et 443) :
# ragraph bytes -M 5s -r argus.log -w ra-http.png - dst port 443 or dst port 80
Fig. 9 : Bande passante HTTP(S)
C'est rigolo, la figure 9 peut presque être superposée à la figure 7 !
6.1.2 Niveau transaction
Pour le niveau transaction, nous allons nous focaliser sur Bro. Regardons par exemple ce qui se passe au niveau SSH (ssh.log). On peut déjà voir ce que le processeur applicatif de Bro récupère au niveau SSH :
# head -n 7 ssh.log
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path ssh
#open 2014-04-16-16-00-17
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p status direction client server remote_location.country_code remote_location.region remote_location.city remote_location.latitude remote_location.longitude
Le champ fields nous indique toutes les informations qu'il est possible d'obtenir sur les connexions SSH. Nous pouvons lister les connexions SSH, leur sens et le succès ou non de celles-ci via la commande bro-cut suivie de la liste des attributs que nous souhaitons afficher (id.orig_h id.resp_h status direction) :
# bro-cut id.orig_h id.resp_h status direction < ssh.log
A.A.A.A Z.Z.Z.Z failure INBOUND
A.A.A.A Z.Z.Z.Z failure INBOUND
A.A.A.A Z.Z.Z.Z failure INBOUND
B.B.B.B D.D.D.D success INBOUND
On voit que Z.Z.Z.Z est assez insistant : trois connexions ratées sur A.A.A.A. Sans doute un mot de passe oublié, car dans la suite des logs, il n y a pas d'autres connexions de Z.Z.Z.Z. On voit également une connexion « success » pour D.D.D.D sur B.B.B.B. Voyons voir ce qu'il en est de notre Chinois :
# Bro-cut id.orig_h id.resp_h status direction < ssh.log | grep C.C.C.C | grep 'success'
Ça ne nous renvoie rien, l'honneur est sauf ! On peut en profiter pour voir sur notre réseau s'il n'y a pas de services SSH déportés sur des ports autres que le 22 :
# Bro-cut id.resp_p direction < ssh.log | grep INBOUND | awk '{ print $1 }' | sort -u
22
4222
6922
Cette commande sélectionne le port de réponse des connexions SSH et le sens. On ne sélectionne que les entrantes (grep INBOUND). Avec awk, on n'affiche que le numéro de port. On obtient une liste de ports que l'on trie avec un sort -u pour n'afficher que les occurrences uniques. Nous n'avons vu que quelques fonctions basiques de Bro, mais il peut faire infiniment plus en implémentant des processeurs supplémentaires, comme par exemple un détecteur d’attaques basées sur la faille Heartbleed [12].
6.2 Snorby
Snorby est dédié à la visualisation des alertes. Pour installer Snorby, il faut un environnement Ruby, un serveur HTTP avec le mod_passenger (ici Nginx) et un serveur MySQL. Pour l’environnement Ruby, pas de soucis. Celui fourni dans les paquetages Debian fait l'affaire. Idem pour le serveur MySQL, celui des paquetages suffit. Il ne reste plus qu'à installer le mod_passenger avec Nginx.
# apt-get install mysql-server ruby
Il faut commencer par installer le module passenger sur la machine :
# gem install rails passenger bundler rake
Le module passenger embarque un outil nommé passenger-install-nginx-module qui va compiler Nginx avec le support de Passenger. Il risque de vous manquer quelques fichiers, mais on peut facilement les récupérer depuis le gestionnaire de paquets avec un coup de apt-file [13].
# passenger-install-nginx-module
Cette commande vérifie les dépendances, vous demande le chemin des sources de Nginx, le compile avec les bonnes options (vous pouvez ajouter les vôtres, nous avons fixé un --prefix=/usr/local/nginx-1.4.6) et enfin l'installe. Les workers du serveur web Nginx doivent s’exécuter avec une identité autre que « root » pour prévenir toute élévation de privilèges suite à une éventuelle faille sur la partie du service exposée sur le réseau. Nous avons choisi de créer un compte utilisateur « nginx » pour les workers. Il faut donc bien vérifier que vous avez défini les options de compilation --user=nginx et --group=nginx pour bien spécifier une identité d’exécution banalisée au worker Nginx. Ce compte doit être créé avec la commande :
# useradd -r nginx
Passons à l'installation de Snorby. Il faut commencer par initialiser la partie base de données :
# mysql -u root -p
mysql> grant all privileges on snorby.* to 'snorby'@'localhost' identified by 'bysnor' ;
mysql> create database snorby ;
Récupérons Snorby :
# git clone http://github.com/Snorby/snorby.git
Ajustons la configuration (snorby_config.yml et database.yml) :
# cd snorby/
# cp config/snorby_config.yml.example config/snorby_config.yml
# cp config/database.yml.example config/database.yml
Avec database.yml renseigné avec les paramètres habituels d'une base de données (type, identifiant, mot de passe et adresse du serveur) :
snorby: &snorby
adapter: mysql
username: snorby
password: "bysnor"
host: localhost
Et snorby_config.yml où il est nécessaire de lui spécifier l'emplacement des règles Snort :
rules:
- "/usr/local/snortrules/rules"
Une fois que la configuration est achevée, il faut installer tous les paquetages nécessaires au fonctionnement de Snorby :
# bundle install
Ensuite, il faut installer la base de données et créer l’environnement d'exécution de Snorby :
# bundle exec rake snorby:setup
Enfin, nous pouvons démarrer l'instance de Snorby (worker) :
# ./script/delayed_job start
6.3 Nginx
Dans l'étape précédente, nous avons compilé Nginx avec le module passenger Ruby. Il faut encore le configurer pour qu'il serve le frontal web de Snorby aux clients. Tout cela se passe dans nginx.conf :
http {
passenger_root /var/lib/gems/1.9.1/gems/passenger-4.0.37;
passenger_ruby /usr/bin/ruby1.9.1;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /usr/local/snorby/public;
passenger_enabled on;
}
error_page 500 502 503 504 /50x.HTML;
location = /50x.HTML {
root HTML;
}
}
}
Nous avons mis en évidence les parties spécifiques à Snorby/Passenger. Vérifiez bien que les chemins indiqués correspondent à votre installation. Enfin, il faut démarrer Nginx :
# /usr/local/sbin/nginx -c /usr/local/nginx-1.4.6/conf/nginx.conf
On vérifie que ça tourne :
# netstat -laputn -A inet | grep nginx
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 3678/nginx.conf
6.4 Barnyard2
La dernière étape de configuration (et de l'article !) est d'alimenter la base de données de Snorby avec les alertes Snort. Pour ce faire, il faut configurer Barnyard2 pour qu'il aille chercher les logs Snort et qu'il les injecte dans le serveur MySQL à l'intérieur de la base de données « snorby ». Il faut commencer par configurer Barnyard2 avec son fichier de configuration :
config reference_file: /usr/local/snortrules/etc/reference.config
config classification_file: /usr/local/snortrules/etc/classification.config
config gen_file: /usr/local/snortrules/etc/gen-msg.map
config sid_file: /usr/local/snortrules/etc/sid-msg.map
config logdir: /store/snort/logs
config hostname: localhost
config interface: eth1
config daemon
input unified2
output database: log, mysql, user=snorby password=snortby dbname=snorby host=localhost
On voit bien dans la dernière section (output database) la connexion entre Barnyard2 (donc Snort) et MySQL (donc Snorby). On voit également que le format d'entrée de Barnyard2 est unified2, il va falloir configurer Snort pour qu'il logue dans ce format (et plus dans des fichiers plats comme dans notre premier test). Cela se passe dans snort.conf en modifiant le paramètre « output » :
output unified2: filename merged.log, limit 128
Au format unified2, l'alerte et le paquet qui l'a générée sont dans le même fichier au format binaire. Cela permet à Snort d'optimiser l'écriture des événements. De plus, en déléguant le log dans MySQL à Barnyard2, Snort gagne en performances. Cela est plus intéressant d'avoir un processus indépendant s'occupant de l'archivage en base de données (d'autant plus que le nombre de cœurs sur les machines va croissant). On relance Snort pour prendre en compte la modification de snort.conf. Puis, on lance Barnyard2 :
# /usr/local/bin/barnyard2 -u snort -g snort -d /store/snort/logs -f merged.log -w /store/snort/logs/snort.waldo --pid-path /var/run -c /usr/local/barnyard2/etc/barnyard2.conf -D
On lui donne son identité d’exécution (-g et -u), l'emplacement des logs de Snort (-d), le nom du fichier au format unified2 (-f), le fichier waldo utilisé par Barnyard2 pour savoir où il en est au niveau des logs Snort (-w), l'emplacement du fichier PID (--pid-path), le fichier de configuration (-c) et enfin, le lancement en mode démon (-D).
Conclusion
C'est enfin terminé ! Voilà une sonde fonctionnelle avec une interface de visualisation des alertes et différents outils pour essayer d'établir précisément le contexte de cette alerte. Réitérons l'exemple avec le paquet ICMP vu dans section 5.3 et voyons ce que ça donne au niveau de Snorby (figures 10, 11 et 12).
Fig. 10 : Accueil de Snorby
Fig. 11 : Liste des occurrences d'alertes ICMP
Fig. 12 : Zoom sur une alerte ICMP
Nous n'avons pas parlé dans cet article d'un logiciel poids lourd du NSM : Sguil [14]. Sguil présente une interface de visualisation qui présente les alertes remontées par une collection de NIDS (PADS, Snort, etc.). À la sélection d'une alerte, l'interface propose de pivoter sur les flux réseau pour voir le contexte. Enfin, il est possible d'effectuer des recherches dans le trafic capturé. Cet outil assez tentaculaire est livré clés en main dans la suite « Security Onion » [15]. Il est présenté en détails dans l'ouvrage de Richard Bejtlich sur le NSM [16]. Nous avons souhaité présenter une suite d'outils différente de cet ouvrage.
Références
[1] Le blog de Richard Bejtlich : http://taosecurity.blogspot.fr
[2] Bejtlich R., « The Tao of Network Security Monitoring : Beyond Intrusion Detection », Addison Wesley, 2004
[3] Bejtlich R., « Extrusion Detection: Security Monitoring for Internal Intrusions », Addison Wesley, 2005
[4] Bejtlich R., Jones K. J., et Rose C. W., « Real Digital Forensics », Addison Wesley2005
[5] Les différentes phases du footprinting : http://fr.slideshare.net/KZAbv/owasp-modern-information-gathering
[6] Ptacek T. H. et Newsham T. N, « Insertion, evasion, and denial of service: Eluding network intrusion detection », Secure Networks Inc Calgary Alberta, 1998
[7] Le site du logiciel Argus : http://qosient.com/argus/
[8] Git de la librairie libdnet : https://github.com/boundary/libdnet
[9] Git de Barnyard2 : https://github.com/firnsy/barnyard2
[10] Quelques exemples de commandes Bro : http://www.bro.org/bro-workshop-2011/solutions/logs/
[11] Présentation sur Argus : http://www.oucs.ox.ac.uk/network/security/documents/itss-argus.pdf
[12] Détection de Heartbleed avec Bro : http://blog.bro.org/2014/04/detecting-heartbleed-bug-using-bro.html
[13] L'outil apt-file : http://doc.ubuntu-fr.org/apt-file
[14] Le site du logiciel Sguil : http://bammv.github.io/sguil/index.html
[15] La distribution Linux « Security Onion » : https://code.google.com/p/security-onion/
[16] Bejtlich R., « The practice of Network Security Monitoring », no scratch press, 2013