|
|
 | |  |  | Introduction à la libnids |  |
by Frédéric Lavécot (13/04/2001)
--[ Introduction ]---------------------------------------------------------
La libnids est une bibliothèque qui fournit des fonctionnalités aux systèmes
de détection d'intrusion (IDS : Intrusion Detection System) Réseau.
Cette bibliothèque écoute le trafic réseau et permet de fournir une
information pratique et utilisable aux systèmes de détection d'intrusion.
Elle a été écrite pour émuler le comportement de la pile TCP/IP de
Linux 2.0.36, certains fichiers provenant de celle-ci.
Les principaux avantages de la libnids (par rapport à la simple écoute du
trafic d'un réseau) sont qu'elle permet :
a) de réassembler les sessions TCP et de suivre un flux de données UDP
b) de défragmenter les paquets IP
Toutes les informations de cet article viennent de la documentation de
libnids, des exemples fournis avec la libnids et de l'inspection du code
source de programmes utilisant la libnids comme dsniff.
Les exemples de codes donnés sont des simplifications des exemples existants
dans la documentation de libnids.
Ce document a pour unique objectif de faire découvrir quelques-unes des
possibilités qu'offre la libnids.
--[ Disclaimer ]-----------------------------------------------------------
Les exemples de programmes donnés sont destinés à montrer comment manipuler
les structures de la libnids. Ces exemples ont été écrits rapidement et de
manière simpliste (ce qui explique la qualité plus que limite du code ;-).
--[ libnids ]--------------------------------------------------------------
http://www.packetfactory.net/Projects/Libnids (quand ça résout)
http://www.avet.com.pl/libnids/libnids.html
Pour compiler et utiliser la libnids il vous faudra :
- la libpcap : ftp://ftp.ee.lbl.gov/ ou http://www.tcpdump.org/
- la libnet : http://www.packetfactory.net/projects/libnet/
--[ Fonctionnalités de la libnids et exemples ]----------------------------
Les structures et fonctions mises à disposition par la libnids sont
déclarées dans le fichier nids.h qu'il faudra inclure dans l'application.
Il faudra aussi lier cette application avec la libnids.
L'interfaçage avec la libnids se fait en spécifiant des fonctions de
callback et en enregistrant ces fonctions auprès de la libnids.
Dans le cas du suivi d'une connexion TCP, la fonction de callback doit être
du type : void tcp_callback(struct tcp_stream *stream, void ** param)
Pour enregistrer cette fonction on utilise la fonction nids_register_tcp :
nids_register_tcp (tcp_callback);
Une fonction nids_register_udp existe aussi et dans ce cas le premier
paramètre de la fonction de callback est du type struct udp_stream *.
Une fonction nids_register_ip (qui permet de recevoir tous les paquets IP
corrects et défragmentés) existe aussi. La fonction de callback ne prend
qu'un paramètre du type struct ip *.
La structure tuple4 sert à stocker l'adresse du client et du serveur et le
port source du client et le port contacté sur le serveur.
La structure tcp_stream permet de stocker les paramètres de connexion (dans
une variable de type tuple4), l'état logique de la connexion (Established,
Data, Closed, Reset) et les informations complémentaires sur la connexion
dans une structure half_stream.
La structure half_stream stocke les informations qui permettent de décrire
la connexion du coté client ou du coté serveur (comme les données dans les
paquets, le nombre d'octets de données, ... ).
C'est notamment grâce à l'information count_new (qui compte le nombre d'octets
de données arrivés) que l'on sait si les données viennent du serveur ou du
client.
A) Réassemblage d'une session TCP
Une des possibilités de la libnids est de pouvoir suivre les sessions TCP.
En effet, la libnids agissant comme une pile, elle nous permet de ne
récupérer que les paquets valides (sommes de contrôle correctes) et les
paquets défragmentés (si ceux-ci ont été fragmentés).
Cela permet de se décharger de toute la partie gestion des paquets et permet
de consacrer ses efforts à d'autres tâches.
Exemple 1 : Visualiser l'état des connexions en cours (avec une fois n'est
pas coutume les commentaires en français)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <nids.h>
#define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x))
void tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed);
/* Fonction de callback pour gérer les sessions TCP*/
int
main ()
{
if (!nids_init ())/* initialisation des fonctionnalités de la libnids */
{ /* Quitter s'il y a une erreur */
fprintf(stderr,"%s\n",nids_errbuf);
exit(1);
}
nids_register_tcp (tcp_callback);
nids_run ();
return 0;
}
void
tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed)
{
char buf[1024];
if(a_tcp->addr.dest==8080)
/* On décide de surveiller le trafic sur le port 8080 */
/*
Pour cela le port destination (addr.dest) doit être égal à 8080
*/
{
strcpy(buf,int_ntoa(a_tcp->addr.saddr)); strcat(buf,":");
sprintf (buf + strlen (buf), "%i ", a_tcp->addr.source);
/* Oula la! mais c'est pas très très joli comme code ! */
/* Où est-ce qu'on t'a appris à programmer comme ça ? ;-) */
if (a_tcp->nids_state == NIDS_JUST_EST)/* Une nouvelle session vient */
/* d'être établie */
{
strcat(buf,"-> ");
strcat(buf,int_ntoa(a_tcp->addr.daddr)); strcat(buf,":");
sprintf (buf + strlen (buf), "%i ", a_tcp->addr.dest);
fprintf (stderr, "%s established\n", buf);
/* EUARK ! de plus en plus laid! */
a_tcp->client.collect++;/* Pour recevoir les autres paquets */
a_tcp->server.collect++;/* provenant du client et du serveur, */
/* il est nécessaire d'incrémenter */
/* la valeur de ces champs */
return;
}
if (a_tcp->nids_state == NIDS_CLOSE)/* Fin normale d'une session */
{
strcat(buf,"<-> ");
strcat(buf,int_ntoa(a_tcp->addr.daddr)); strcat(buf,":");
sprintf (buf + strlen (buf), "%i ", a_tcp->addr.dest);
fprintf (stderr, "%s closing\n", buf);
return;
}
if (a_tcp->nids_state == NIDS_RESET)/* Paquet RST mettant fin */
{ /* à la session */
strcat(buf,"<-> ");
strcat(buf,int_ntoa(a_tcp->addr.daddr)); strcat(buf,":");
sprintf (buf + strlen (buf), "%i ", a_tcp->addr.dest);
fprintf (stderr, "%s reset\n", buf);
return;
}
if (a_tcp->nids_state == NIDS_DATA)/* Paquet de données */
{
/* Des données viennent d'arriver, on veut savoir dans quelle */
/* direction ces paquets vont. */
struct half_stream *hlf;
if (a_tcp->client.count_new)
{/*Les données viennent du client */
strcat(buf,"<- ");
strcat(buf,int_ntoa(a_tcp->addr.daddr)); strcat(buf,":");
sprintf (buf + strlen (buf), "%i ", a_tcp->addr.dest);
hlf = &a_tcp->client; /*Stocker les données du client dans hlf*/
}
else
{
strcat(buf,"-> ");
strcat(buf,int_ntoa(a_tcp->addr.daddr)); strcat(buf,":");
sprintf (buf + strlen (buf), "%i ", a_tcp->addr.dest);
hlf = &a_tcp->server;
}
fprintf (stderr, "%s data %d bytes\n", buf, hlf->count_new);
return;
}
}
return ;
}
Il s'agit d'un exemple basique : il reste beaucoup d'éléments dans les
structures qui n'ont pas été mentionnés (une inspection de nids.h vous
donnera une idée des autres possibilités).
Notamment cet exemple ne retourne pas les paquets urgents.
Mais cette série de fonctions et de structures peut très bien être utilisée
pour repérer des débordements de buffers (en détectant un suite de nop, en
reconnaissant une signature comme le décodeur du shellcode du hellkit, ...)
ou pour faire des analyses statistiques sur le trafic observé (taille des
paquets de commandes et cetera).
B) Fragments et paquets erronés
Pour récupérer tous les paquets IP reçus par la libnids (y compris les
fragments, les paquets invalides et les paquets d'établissement de connexion),
il faut définir une fonction de callback du type :
void ip_frag_func(struct ip * a_packet, int len)
et enregistrer cette fonction auprès de la libnids comme suit :
nids_register_ip_frag(ip_frag_func);
C) Compilation
Il faut inclure les bibliothèques : nids, pcap et lnet.
ex : gcc -Wall nids.c -o nids -lnids -lpcap -lnet
D) Conclusion
La libnids offre beaucoup de possibilités dont l'article ne donne qu'un tout
petit aperçu. Un second article permettra de découvrir quelques-unes de ces
autres fonctionnalités comme la détection de scans de ports ou la
journalisation d'informations vers le démon syslogd.
|