Nettoyez votre pare-feu à état avec Wolfenstein

GNU/Linux Magazine n° 108 | septembre 2008 | Sebastien Tricaud
Creative Commons
  • Actuellement 1 sur 5 étoiles
1
Merci d'avoir participé !
Vous avez déjà noté cette page, vous ne pouvez la noter qu'une fois !
Votre note a été changée, merci de votre participation !
Le système de pare-feu Linux, plus connu sous le doux nom de « Netfilter », est simplement une liste de cinq hooks noyau autour desquels des développeurs ont joué. L'implémentation de Netfilter la plus connue reste le programme Iptables, mais il existe d'autres applications permettant d'exploiter les autres fonctionnalités : conntrack-tools, Weatherwall ou encore NuFW...Le but de cet article est de présenter, à travers Wolfotrack, comment vous pouvez manipuler vos connexions à état afin d'améliorer la sécurité de votre réseau et d'éviter de vous encombrer d'éléments indésirables.

1. Introduction

Wolfenstein 3D est un programme qui date d'un temps que les moins de vingt ans ne peuvent pas connaître, mais qui eut le bon goût d'amener un nouveau genre de jeux : les FPS, amenant une nouvelle génération de fulgurantes personnes shootant sur tout ce qui bouge.

Il fût le précurseur de Doom, Quake et tous les autres jeux du même genre. Et comme il s'avère indispensable dans les loisirs de tous les jours, et grâce à la libération du code en GPL, il était donc tout naturel de lui développer du code permettant de récupérer les informations que Netfilter peut lui donner.

Depuis les versions 2.6.14 du noyau Linux, il est possible d'utiliser trois piles permettant de récupérer et modifier les informations réseau capturées par Netfilter. Ces piles sont :

- log ;

- queue ;

- conntrack.

Elles font en fait partie de l'infrastructure NfNetlink pour renvoyer ces informations en espace utilisateur.

Parmi ces trois piles, log et queue sont utilisables comme cibles pour Iptables. Si l’on rajoute la règle suivante :

# iptables -A INPUT -p tcp --dport 80 -j NFQUEUE

La décision d'acceptation ou de rejet du paquet ne sera pas controlée par Iptables, mais par une application de plus haut niveau. Ainsi, il sera possible d'accepter ou de refuser une connexion sur d'autres critères que ses champs standards TCP/IP. Deux exemples utilisent cette fonctionnalité : Snort inline, pour rejeter les paquets malicieux, et NuFW, pour authentifier les utilisateurs.

Wolfotrack, quant à lui, utilise la pile de conntrack, qui est utilisée par Netfilter pour maintenir une liste des connexions en définissant des tables de quatre types :

- NEW : toute nouvelle connexion non vue auparavant ;

- RELATED : toute connexion liée à une autre ;

- ESTABLISHED : toute connexion déjà établie.

- INVALID : toute connexion avec des propriétés invalides vis-à-vis de la pile TCP/IP de Linux.

Ainsi, si vous voulez paramétrer votre pare-feu à la maison pour n'autoriser que les connexions qui sont initiées depuis votre ordinateur et refuser toute connexion émanant de l'extérieur, vous pouvez simplement taper :

# iptables -P INPUT DROP

# iptables -P OUTPUT DROP

# iptables -A OUTPUT -m state --state ! INVALID -j ACCEPT

# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

Ce qui signifie que vous pourrez parcourir le web tranquillement, en étant assuré que personne ne puisse se connecter à un service ouvert sur votre machine depuis l'extérieur.

2. Le suivi de connexions Netfilter

Netfilter étant un pare-feu à états, nous pouvons librement modifier ces états de façon plus courante par une inspection du protocole : ainsi, FTP étant un protocole ayant ses données passant en clair, il suffit de regarder la clef PORT pour détecter le port qui sera utilisé pour le transfert des données, ce qui évite au pare-feu d'être moins sécurisé en autorisant tout ce qui va vers le port 21 et 20 – peu importe le sens –, mais aussi qui a pour effet Kiss-cool de diviser le nombre de règles par deux.

Si vous mettez en place les règles définies à la fin de l'introduction, vous pourrez allègrement interroger les tables avec la commande conntrack. Par exemple :

# conntrack -E

udp      17 23 src=10.2.20.227 dst=255.255.255.255 sport=68 dport=67 packets=2 bytes=656 [UNREPLIED] src=255.255.255.255 dst=10.2.20.227 sport=67 dport=68 packets=0 bytes=0 mark=0 use=1

[NEW] tcp      6 120 SYN_SENT src=10.2.20.153 dst=209.85.129.99 sport=57198 dport=80 [UNREPLIED] src=209.85.129.99 dst=10.2.20.153 sport=80 dport=57198

[UPDATE] tcp      6 60 SYN_RECV src=10.2.20.153 dst=209.85.129.99 sport=57198 dport=80 src=209.85.129.99 dst=10.2.20.153 sport=80 dport=57198

[UPDATE] tcp      6 432000 ESTABLISHED src=10.2.20.153 dst=209.85.129.99 sport=57198 dport=80 src=209.85.129.99 dst=10.2.20.153 sport=80 dport=57198 [ASSURED]

[DESTROY] udp      17 src=10.2.20.48 dst=10.2.23.255 sport=631 dport=631 packets=1 bytes=189 src=10.2.23.255 dst=10.2.20.48 sport=631 dport=631 packets=0 bytes=0

[NEW] udp      17 30 src=10.2.20.48 dst=10.2.23.255 sport=631 dport=631 [UNREPLIED] src=10.2.23.255 dst=10.2.20.48 sport=631 dport=631

Le but de Wolfotrack est de récupérer ces informations, de les afficher lorsqu'une personne se présente et de tuer la connexion associée lorsque le personnage est mort.

Vous pouvez maintenant installer les bibliothèques nécessaires pour la compilation de Wolfotrack, en tapant sur une Debian :

# apt-get install libnetfilter-conntrack-dev libsdl-mixer1.2-dev

Sinon, sur une distribution ne distribuant pas ces packages, vous pouvez télécharger les libnfnetlink et libnetfilter_conntrack sur le site de Netfilter http://www.netfilter.org, puis la libsdl sur http://www.libsdl.org.

3. Lancer Wolfotrack

Il suffit de télécharger Wolfotrack ici : http://software.inl.fr/releases/wolfotrack/wolfotrack-1.1.tar.gz, puis de décompresser et compiler les sources :

# cmake .

# make

Ensuite, il faut récupérer les fichiers de données. Soit vous trouvez un moyen de décompresser les fichiers .WL1 depuis l'archive d'id Software disponible à l'adresse ftp://ftp.3drealms.com/share/1wolf14.zip, soit vous pouvez télécharger directement une version à l'adresse http://www.wallinfire.net/files/wolf3d-data_nupik-shareware.tar.gz.

Reprennez les règles Iptables que l'on a écrites plus haut. Soyez sûr qu'il y a suffisamment de trafic pour que chaque personnage soit associé à une connexion.

Une fois que vous avez fait tout ceci, vous pouvez enfin lancer en tant que root (puisque l'on accède à la socketnfnetlink de Netfilter) Wolfotrack :

$ sudo ./sdlwolf3d x3

Le paramètre x3 permet de le lancer dans un écran plus grand.

4. Modifications apportées

Le découpage des fonctions liées au suivi de connexions se trouve dans le fichier ct_build.c. Puis, il a fallu se placer à divers endroits pour placer les appels aux fonctions de Netfilter. Enfin, il a fallu créer une fonction pour pouvoir afficher du texte à n'importe quel endroit, lorsque nous avons l'ennemi dans un certain angle et seulement lorsque ce personnage est vivant.

4.1 Fonctions de ct_build.c

Afin de comprendre les modifications apportées, nous regroupons les fonctions que nous utilisons dans Wolfotrack dans un tableau :

Fonction Lignes de code Description
ct_list_clear 16 Nettoyage des structures du conntrack
ct_list_append 13 Ajoute les informations src/dst
ct_list_get 1 Récupère la structure conntrack
port_ntoa 3 Transforme un port en ascii
actor_id_incr 2 Incrémente l'ID de l'ennemi
ct_cb 27 Callback du conntrack
ct_list_create 22 Fonction principale du conntrack
ct_close 1 Fermeture du conntrack
ct_remove_from_id 9 Suppression de la structure conntrack

4.2 Code dans Wolfenstein 3d

La structure qui permet de connaitre le type d'ennemi se fait dans la structure objtype. Ainsi, pour déterminer si un acteur (ennemi) est mort, il suffit de coder la fonction :

int ActorDead(objtype *obj)

{

        int retval = 0;

        switch(obj->state) {

                case s_grddie4:

                        retval = 1;

                        break;

                default:

                        retval = 0;

        }

        return retval;

}

que l'on utilisera pour savoir si l’on doit afficher les informations sur les adresses et ports du suivi de connexion.

Ensuite, si l'on regarde le fichier wl_draw.c ligne 386, on voit l'utilisation des fonctions de récupération du conntrack lorsqu'un acteur est visible, est un gardien et n'est pas mort :

if ( ( obj->obclass == guardobj ) && ( ! ActorDead(obj)) ) {

        if (ct_list_get(obj->id)) {

                struct conntrack_t *entry = ct_list_get(obj->id);

                source = malloc(strlen(entry->saddr) +

                        strlen(":") +

                        strlen(entry->sport) + 1);

                target = malloc(strlen(entry->daddr) +

                        strlen(":") +

                        strlen(entry->dport) + 1);

                sprintf(source, "%s:%s", entry->saddr, entry->sport );

                sprintf(target, "%s:%s", entry->daddr, entry->dport );

                SETFONTCOLOR(68, BKGDCOLOR);

                US_PrintXY(source, 30, 20);

                US_PrintXY(target, 30, 30);

                SETFONTCOLOR(TEXTCOLOR,BKGDCOLOR);

                free(source);

                free(target);

        }

}

Une liste est construite à partir des numéros identifiant les personnages. Cette liste contient les données relatives au conntrack. Ensuite, nous alouons la taille nécessaire pour récupérer l'IP et le port source, puis la destination. Nous mettons la couleur jaune pour le texte que nous imprimons avec la fonction US_PrintXY pour enfin libérer la mémoire de ces pointeurs.

Pour être sûr que nous avons toujours des connexions récentes lorsque nous jouons, on rafraîchit la liste des connexions lorsque l'on ouvre une porte. Cela se fait dans wl_act1.c ligne 377 avec l'appel à la fonction ct_list_create.

5. Utilisation de l'API de libnetfilter_conntrack

Dès que nous activons le conntrack de Netfilter avec un commande comme :

# iptables -A INPUT -m state --state ! INVALID -j ACCEPT

nous avons la possibilité de nous connecter à l'API du conntrack. Cette API est utilisable à travers la bibliothèque libnetfilter_conntrack qui elle-même utilise la socket CONNTRACK de nfnetlink, comme expliqué par le schéma ci-dessous :

Tout d'abord, nous déclarons un handler de conntrack que nous utiliserons dans notre fichier :

static struct nfct_handle *h = NULL;

Puis, nous démarrons la communication en demandant à nfnetlink de nous connecter à la socket du conntrack :

h = nfct_open(CONNTRACK, 0);

Enfin, nous déclarons la fonction à utiliser pour les callbacks (ct_cb) que l'on lancera, ainsi que l'action qui sera déclenchée à l'appel de cette fonction (NFCT_Q_DUMP) :

nfct_callback_register(h, NFCT_T_ALL, ct_cb, NULL);

ret = nfct_query(h, NFCT_Q_DUMP, &family);

Ce qui fait que tout le code gérant le conntrack sera dans ct_cb. C'est dans cette fonction que nous allons récupérer les données que Netfilter va nous envoyer. Il ne nous reste plus qu'à récupérer l'IP et le port avec ces fonctions :

ip_src = nfct_get_attr_u32(ct, ATTR_IPV4_SRC);

saddr = strdup((const char*)inet_ntoa(ip_src));

port_src = nfct_get_attr_u16(ct, ATTR_PORT_SRC);

sport = port_ntoa(port_src);

puis à rajouter ces informations à la liste :

ct_list_append(actor_id, ct, saddr, daddr, sport, dport);

sans oublier la fonction de retour du callback :

return NFCT_CB_STOLEN;

qui permettra de continuer à exécuter le callback sur les connexions suivantes.

6. Conclusion

Je ne risque pas de vous dévoiler les raisons qui m'ont poussé à non seulement avoir l'idée, mais en plus développer Wolfotrack. Cependant, il est intéressant de voir à quel point il peut être facile, grâce à de bonnes bibliothèques, de développer des applications amusantes en rejoignant deux technologies qui n'ont rien à voir (un jeu et un pare-feu). À noter qu'avec l'état global de sécurité actuel, il nous faut des outils permettant d'arriver à simplifier son utilisation pour madame Michu.

Pour finir, je voudrais remercier Victor Stinner et Laurent Defert qui se sont impliqués dans le développement, pour arriver à avoir enfin un pare-feu utilisable !