Netfilter pour faire la chasse aux spammeurs

Magazine
Marque
GNU/Linux Magazine
Numéro
125
Mois de parution
mars 2010


Résumé
Je vous propose de découvrir le mécanisme des NFQUEUE Netfilter, le filtrage réseau par un programme userland, appliqué à la lutte contre les spammeurs. Nous allons voir comment, avec très peu d'informations et de ressources, nous allons pouvoir détecter un spammeur potentiel et l'empêcher de se connecter à un serveur mail.

Body

1. Faites la queue comme tout le monde !

Le filtrage réseau classique que beaucoup connaissent est le filtrage basé sur les adresses et les ports source et de destination. Il existe un autre type de filtrage, souvent appelé DPI (Deep Packet Inspection) par les constructeurs ou vendeurs de firewalls propriétaires. Il consiste à filtrer en fonction des informations contenues dans les couches applicatives. Netfilter propose plusieurs moyens de faire cette analyse applicative, vous avez peut-être utilisé certaines d'entre elles, mais nous allons étudier un seul de ces moyens : les NFQUEUE. Le principe est simple : tout le trafic est placé dans une file d'attente qui est lue par un programme, ce dernier va pouvoir décider du filtrage de chaque paquet qu'il traite. Mais pour bien comprendre, rien de mieux qu'un schéma.

schema_nfqueue

1.2 Un petit exemple pour bien comprendre

Avant de nous jeter à corps perdu dans notre antispam utilisant NFQUEUE, voici un programme d'exemple permettant d'analyser les requêtes DNS émises par votre machine. Tout d'abord, nous redirigeons les requêtes vers une file d'attente :

# iptables -I OUTPUT -p udp --dport domain -j NFQUEUE --queue-num 0

Ensuite, nous lançons un simple programme :

#!/usr/bin/perl

use nfqueue;

use Socket qw(AF_INET);

use NetPacket::IP qw(:ALL);

use NetPacket::UDP qw(:ALL);

use Net::DNS::Packet;

sub callback {

        my ($dummy, $payload) = @_;

        if ($payload) {

        my $ip = NetPacket::IP->decode($payload->get_data());

        my $udp = NetPacket::UDP->decode($ip->{data});

        my $dns = Net::DNS::Packet->new($udp->{data});

        eval { $dns->string };

        $dns->print;

        if ($@) {

            $payload->set_verdict($nfqueue::NF_DROP);

        } else {

            $payload->set_verdict($nfqueue::NF_ACCEPT);

        }

    }

}

my $q = new nfqueue::queue();

$q->open();

$q->bind(AF_INET);

$q->set_callback(\&callback);

$q->create_queue(0);

$q->try_run();

On note dans ce programme deux parties, la création de la nfqueue et la fonction de callback à laquelle elle se rattache. Créer une file d'attente n'est pas bien dur comme vous le remarquez, on utilise l'API nfqueue-bindings, qui nous permet de créer notre file d'attente (méthode open), d'associer un traitement spécifique (méthode set_callback) puis de la rattacher à un numéro spécifique (create_queue(votre_numero)) avant de commencer à scruter la file d'attente pour récupérer les paquets (try_run). Parlons maintenant du callback lui-même : nous avons une règle iptables qui place pour nous les datagrammes UDP à destination du port 53 dans une file d'attente et la fonction nommée callback utilise deux bibliothèques tierces, Net::DNS et NetPacket, afin de pratiquer l'analyse protocolaire. Il est souvent plus confortable et/ou plus performant d'utiliser des bibliothèques toutes faites plutôt que réinventer la roue. Ici, nous pourrons donc afficher les requêtes émises par notre machine. Maintenant que vous avez compris le principe, vous vous posez probablement cette question : « A quoi ça sert ? Est-ce encore un jouet de geek inutile ? ». La seconde partie de l'article vous propose de voir une application concrète de ce principe : un antispam.

2. Un antispam basé sur Netfilter !

Les antispams « classiques » fonctionnent ainsi : le mail est récupéré dans son intégralité afin de passer une batterie de tests comme les filtres bayésiens, la comparaison à des expressions rationnelles, l'analyse des en-têtes, l'interrogation des listes noires sur Internet. Au final, beaucoup de ressources (CPU, mémoire, disque, réseau) sont utilisées. Nous allons voir qu'il est possible avec l'utilisation de NFQUEUE d'innover en matière d'antispam : je vais vous présenter synspam, ou comment empêcher les spammeurs de se connecter à votre serveur mail pour y déverser leur publicité pour des pilules bleues, des montres de luxe ou des jetons de casino.

2.1 Les limites des antispams classiques

Les méthodes classiques ont leurs limites, je citerai ici 3 exemples :

  • Le bug du 1er Janvier 2010 de Spamassassin : la règle FH_DATE_PAST_20XX s'est déclenchée et a commencé à générer des faux positifs, le score attribué étant compris en 2.0 et 3.5 en fonction des environnements, les sysadmins ont eu du travail le 1er Janvier pour comprendre l'erreur et rectifier le tir afin d'éviter de marquer comme spams des messages légitimes.
  • Le contrôle de contenu mal configuré peut donner des résultats très déplaisants, voici un exemple dans lequel l'antispam refuse purement et simplement les mails contenant les types MIME .movie, .mpg, mais aussi text/plain :

Objet : BANNED (text/plain,.movie,.mpg) IN MAIL FROM YOU

BANNED CONTENTS ALERT

Our content checker found

   banned name: text/plain,.movie,.mpg

Il s'en suit que l'utilisateur ne recevra plus beaucoup de messages (ceci dit, c'est aussi une méthode assez efficace contre les spams si on se fiche des faux positifs !)

  • Les mails de reconnaissance : en envoyant un faux mail innocent (une carte de vœux en plain/text, par exemple, sans rien d'autre), un spammeur pourra découvrir les adresses mails valides sur votre serveur. L'antispam ne trouvant rien à redire à ce type de message, il se peut que le message passe.
  • Le dernier exemple est celui du filtrage bayésien, pour rappel un filtrage probabilistique basé sur une base de données de hams (messages légitimes) et de spams : les spammeurs emploient déjà depuis un moment une technique dite de « désapprentissage » des filtres bayésiens. Il s'agit d'envoyer des messages parfaitement « normaux » aux yeux des filtres bayésiens pour que ceux-ci apprennent les messages envoyés depuis une certaine adresse mail comme des messages légitimes. Le spammeur enverra ensuite ses spams avec la même adresse d'expéditeur et le filtre bayésien sera moins efficace sur la détection de ce spam.

2.2 Synspam à la rescousse !

La méthode de synspam est d'intercepter le tout premier paquet TCP d'une connexion sur le port 25 afin de donner un indice sur la probabilité que la machine source est émettrice de spams. Cette probabilité n'a rien à voir avec les filtres bayésiens évoqués précédemment, une série de tests est déroulée à l'arrivée de chaque paquet SYN, chaque test positif ajoute des points à un score, si ce dernier dépasse un seuil prédéfini, la connexion est tout simplement refusée car considérée comme émanant d'un spammeur.

Tout d'abord, reprenons les bases de TCP. Pour qu'une connexion s'établisse, le client envoie au serveur une demande (un SYN). C'est ici que nous allons récupérer toutes les informations nécessaires à l'identification des spammeurs : son adresse IP que nous pouvons confronter à différentes listes noires ainsi qu'à une comparaison de son enregistrement DNS à une expression rationnelle, les en-têtes IP et TCP peuvent même nous permettre de faire de l'OS fingerprinting.

Voyons tout d'abord le cœur du logiciel :

sub main {

  close(STDOUT); # to prevent nfqueue-bindings verbose debugging

$queue = new nfqueue::queue();

  $queue->set_callback(\&cb);

$queue->fast_open($queuenum, AF_INET);

$SIG{INT} = \&cleanup;

  $SIG{HUP} = \&cleanup;

  $SIG{TERM} = \&cleanup;

$queue->set_queue_maxlen(5000);

  $queue->try_run();

}

sub cb()

{

        my ($dummy,$payload) = @_;

        if ($payload) {

        my $ip_obj = NetPacket::IP->decode($payload->get_data());

        my $ipsrc = $ip_obj->{src_ip};

        my $result = "";

        $debugstr = " ";

        $score = 0;

        run_tests($ipsrc);

        my $connection = "$ipsrc score=".$score;

}

sub run_tests {

         my ($ipsrc) = @_;

         my $reverse = rdns_testsuite($ipsrc);

         check_dnsbl($ipsrc, $score_dnsbl_high, "dnsbl_high ", @dnsbl_high);

         check_dnsbl($ipsrc, $score_dnsbl_med, "dnsbl_med ", @dnsbl_med);

         check_dnsbl($ipsrc, $score_dnsbl_low, "dnsbl_low ", @dnsbl_low);

}

}

Comme dans notre programme d'exemple, tout commence par la mise en place d'une file d'attente NFQUEUE avec sa fonction de callback dans laquelle on décortique les en-têtes IP grâce à NetPacket, puis viennent les tests proprement dits via un appel à la fonction run_tests. Examinons en détail les tests en question :

  • Les tests sur les listes noires sont classés par ordre de confiance : de low à high, respectivement pour les DNSBL auxquelles nous accordons le moins de confiance jusqu'à celles considérées comme les plus performantes (que ce soit en termes de détection que de faux positifs). Il ne faut jamais faire confiance à un seul système en matière de sécurité. Aussi, chaque DNSBL se voit attribuer un score, ce qui réduit l'impact d'une liste qui bloquerait une machine désirant envoyer un message légitime.
  • Le deuxième système sur lequel repose synspam est... le DNS, ou plus précisément une analyse des enregistrements liés à la machine source. Comme vous le savez peut-être déjà, une très large majorité des spams sont envoyés depuis des machines faisant partie de botnets. Ces machines ont (très) souvent les caractéristiques suivantes : une connexion dialup de type modem, adsl ou cable avec un enregistrement DNS qui contient l'adresse IP sous une forme ou une autre et parfois des mots-clés. Nous allons donc analyser ce reverse afin de savoir si une machine dialup tente de se connecter. Voici le code en charge du test :

sub check_words {

   my ($name) = @_;

   my @clientwords = ('.*dsl.*', 'cable', 'catv', 'ddns', 'dhcp',

    'dial(-?up)?', 'dip', 'docsis', 'dyn(amic)?(ip)?', 'modem', 'ppp(oe)?',

    'res(net|ident(ial)?)?', 'bredband', 'broadband', 'triband'

    , 'client', 'fixed', 'ip', 'pool', 'static', 'user' # controversial ones

    );

   my $wordre = '((\b|\d)' . join('(\b|\d))|((\b|\d)', @clientwords) . '(\b|\d))';

   my $wordexp = '(' . $wordre . ')\S*\.\S+\.\S+$';

   if (($name ne "") && ($wordexp ne "") && ($name =~ /(?:$wordexp)/i) ) {

      $score += $score_botnet_reverse;

      $debugstr .= "botnet_reverse ";

      return (1);

}

   return (0);

}

code

Le système de détection d'adresse IP contenue dans le reverse DNS emploie une méthode similaire. Nous avons donc ici nos 2 premières séries de tests : les DNSBL et la comparaison des enregistrements DNS à une expression rationnelle.

Une troisième méthode pourrait être employée, il s'agit de l'OS fingerprinting ou, en bon français, la détection de système d'exploitation. Pourquoi donc ce besoin ? Car si la majorité des spams sont envoyés par des machines compromises et rattachées à des botnets, ces machines sont quasiment toutes des machines faisant tourner une version de Windows (2000, 2003, Vista, mais surtout les différentes versions de XP). Les serveurs de mails des FAI, hébergeurs sont très souvent des machines Unix, même si on croise de temps à autre des plates-formes basées sur Exchange, mais ces exemples sont marginaux. Détecter une machine Windows, c'est une probabilité de plus que le message délivré soit un spam.

2.3 Oui à l'OS fingerprinting, mais comment ?

Comme vu dans le paragraphe précédent, le logiciel intercepte les paquets SYN avant que ceux-ci n'atteignent le port 25 de la machine, nous ne disposons donc que de ce paquet pour faire notre reconnaissance d'OS qui sera donc passive. Il existe plusieurs logiciels capables de réaliser ce que nous voulons (parmi eux, on compte p0f, sinfp), mais ils utilisent la bibliothèque pcap et ne peuvent pas se rattacher à une file d'attente Netfilter. Deux solutions sont envisageables : la première est de coder en perl notre propre moteur, la deuxième est de se reposer sur Netfilter pour la détection d'OS. Fort heureusement pour nous, depuis la version 2.6.31 du noyau Linux, un module Netfilter a fait son apparition. Il s'agit de xt_osf, qui utilise le format de p0f pour réaliser la détection d'OS, fantastique ! Voyons un peu en détail ce que cela donne :

Pour reconnaître les différents OS, il nous faut 2 choses : un fichier de définition, par exemple, pf.os (fichier mis à disposition par le projet OpenBSD) et l'outil userland permettant d'intégrer ledit fichier :

# modprobe xt_osf

# wget http://www.ioremap.net/archive/osf/osf-2009_06_07.tar.gz

# tar xzf osf-2009_06_07.tar.gz && cd osf-2009_06_07

# make

# wget http://www.openbsd.org/pf.os

# ./nfnl -f pf.os

Nous avons donc notre système prêt à l'emploi. Il ne reste plus qu'à faire 2 files d'attente : la première pour les machines de type Windows, la deuxième pour le reste.

# iptables -I INPUT -m osf ! --genre Windows -p tcp --syn --dport 25 -j NFQUEUE --queue-num 0

# iptables -I INPUT -m osf --genre Windows -p tcp --syn --dport 25 -j NFQUEUE --queue-num 1

Un premier processus synspam rattaché à la file d'attente 0, qui aura pour score de base 0, un deuxième processus synspam rattaché à la file d'attente 1, qui ajoutera une pondération supplémentaire pour toutes les connexions, le score de base sera alors de 1, 2, 3... ou 42 au gré de l'humeur de l'administrateur ;-)

3. Alors, on peut conclure ?

Le système antispam proposé ne tombe pas dans les travers des systèmes classiques. Ceci dit, il n'est pas parfait non plus dans le sens où il ne détectera probablement pas 100% des connexions ayant pour but l'envoi de spams. Cette solution a l'avantage de pouvoir être utilisée en amont d'un MTA, qui aura un logiciel antispam classique qui inspectera alors le contenu des messages passés au travers des mailles du premier filet. Alors profitez-en pour croiser les technologies pour un filtrage encore plus performant. Pour reprendre une formule d'un très bon enseignant : « en sécurité, c'est ceinture + bretelles ».




Article rédigé par

Les derniers articles Premiums

Les derniers articles Premium

Cryptographie : débuter par la pratique grâce à picoCTF

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’apprentissage de la cryptographie n’est pas toujours évident lorsqu’on souhaite le faire par la pratique. Lorsque l’on débute, il existe cependant des challenges accessibles qui permettent de découvrir ce monde passionnant sans avoir de connaissances mathématiques approfondies en la matière. C’est le cas de picoCTF, qui propose une série d’épreuves en cryptographie avec une difficulté progressive et à destination des débutants !

Game & Watch : utilisons judicieusement la mémoire

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Au terme de l'article précédent [1] concernant la transformation de la console Nintendo Game & Watch en plateforme de développement, nous nous sommes heurtés à un problème : les 128 Ko de flash intégrés au microcontrôleur STM32 sont une ressource précieuse, car en quantité réduite. Mais heureusement pour nous, le STM32H7B0 dispose d'une mémoire vive de taille conséquente (~ 1,2 Mo) et se trouve être connecté à une flash externe QSPI offrant autant d'espace. Pour pouvoir développer des codes plus étoffés, nous devons apprendre à utiliser ces deux ressources.

Raspberry Pi Pico : PIO, DMA et mémoire flash

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Le microcontrôleur RP2040 équipant la Pico est une petite merveille et malgré l'absence de connectivité wifi ou Bluetooth, l'étendue des fonctionnalités intégrées reste très impressionnante. Nous avons abordé le sujet du sous-système PIO dans un précédent article [1], mais celui-ci n'était qu'une découverte de la fonctionnalité. Il est temps à présent de pousser plus loin nos expérimentations en mêlant plusieurs ressources à notre disposition : PIO, DMA et accès à la flash QSPI.

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous