AccueilFAQStatistiquesDiversContact

Linux : utiliser iptables pour bloquer les chaines de caractères (1ere partie)

Est-ce que "w00tw00t.at.ISC.SANS.DFind:)" vous rappelle quelque chose ? Si vous administrez un serveur, vous avez probablement déjà rencontré "ça" dans vos logs, ou peut-être en avez-vous même fait des cauchemars en tentant, en vain, de vous en débarrasser ?

En général, ça se présente sous cette forme dans vos logs apache :


 213.251.134.23 [16/Nov/2008:07:43:58] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400
 213.251.134.23 [16/Nov/2008:07:43:58] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400
 213.251.134.23 [16/Nov/2008:07:43:58] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400
 213.251.134.23 [17/Nov/2008:05:16:31] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400

On voit ici que le 213.251.134.23, petit serveur ks31944.kimsufi.com hébergé (et piraté) chez OVH, a demandé la page "/w00tw00t.at.ISC.SANS.DFind:)" et apache lui a poliment fait comprendre que la requête ne pouvait aboutir en lui retournant un code 400 et en ajoutant dans le fichier log des erreurs la raison de son refus :


 client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23)

Apache rejette cette requête tout simplement parce qu'elle n'est pas conforme. Toute requête HTTP 1.1 se doit de comporter dans son en-tête un champ "Host:" comme cela :


 GET /page_demandee.html HTTP/1.1
 Host: monsite.com

Dans notre cas il n'y a pas le nom d'hôte donc cette requête est rejetée et, de ce fait, n'est pas dangereuse : votre serveur n'a pas été piraté !
Il ne s'agit que d'un scanner de vulnérabilités (DFind) sans grand intérêt si ce n'est qu'il pollue en permance vos logs. Il en existe plusieurs variantes dont :

 /w00tw00t.at.ISC.SANS.DFind:)
 /w00tw00t.at.ISC.SANS.test0:)
 /w00tw00t.at.ISC.SANS.MSlog:)
 /w00tw00t.at.ISC.SANS.ntsvc:)


Comment s'en débarasser ?

  • Fail2ban ? L'utilisation d'une telle application, ou toute autre similaire que j'appelle ironiquement les After Hours, n'a que peut d'intérêt ici car elle n'agit pas en temps réel mais uniquement après coup et bloquer l'IP ne servirait pas à grand chose car la prochaine fois c'en sera une autre et ainsi de suite.
  • installer un NIDS (Network Intrusion Detection System) ? Installer une telle application pour une simple chaîne de 28 caractères est tout de même un peu lourd...
  • utiliser mod_rewrite ou mod_security ? Impossible, et même si étrangement on trouve sur certains sites des règles pour mod_security afin de se débarasser de DFind elles sont totalement inutiles. mod_security n'est pas, contrairement à la croyance, un firewall mais rien d'autre qu'un simple module. Les données arrivent par apache, qui écoute sur le port 80, et c'est lui qui va les passer à mod_security et non pas l'inverse (d'où le danger d'imaginer son serveur apache comme étant totalement protégé par mod_security). Dans notre cas, apache rejete la requête sans la passer à mod_security.
  • Nous allons tout simplement utiliser l'excellent iptables et notamment son filtre permettant de rejeter des paquets en se basant sur les chaînes de caractères avec l'option -m string. Pour connaître son fonctionnement, il suffit tout simplement de le lui demander :

    
     # iptables -m string --help
    
     STRING match v1.3.8 options:
     --from                       Offset to start searching from
     --to                         Offset to stop searching
     --algo                       Algorithm
     --string [!] string          Match a string in a packet
     --hex-string [!] string      Match a hex string in a packet
    
    
  • --from : offset du paquet à partir duquel la recherche doit commencer. Par défaut, la recherche s'effectue à partir de l'offset 0 du paquet.
  • --to : offset où la recherche s'arrête. Cette option, de même que la précédente, est très utile car permet de limiter la recherche et donc d'éviter de perdre du temps à filtrer l'intégralité d'un paquet. Par défaut, la recherche s'effectue sur la totalité du paquet, la limite maximale étant fixée à 65535.
  • --algo : le type d'algorithme a utiliser. Il sont deux : Boyer-Moore (bm) et Knuth-Pratt-Morris (kmp). Nous utiliserons le premier.
  • --string : la chaîne recherchée sous la forme de caractères (ex : 'abcd'). La recherche tient compte des minuscules et des MAJUSCULES.
  • --hex-string : la chaîne recherchée sous sa forme hexadécimale. La chaîne doit être encadrée du signe '|'. Les caractères peuvent être séparés par un espace (ex : '|61 62 63 64|') ou non (ex : '|61626364|').

  • Avant d'aller plus loin il est important de noter que les quelques règles décrites ci-dessous sont suffisantes pour vous débarasser des "script-kiddies" qui scannent les IPs au hasard pour trouver quelques vulnérabilités et éviter qu'ils ne polluent vos logs apache, mais qu'elles ne peuvent en aucun cas s'appliquer à un hacker expérimenté qui s'en prendrait spécifiquement à votre serveur, que seule une configuration poussée pourrait bloquer.

    Comme nous avons vu qu'il existait plusieurs variantes de notre "w00tw00t", nous allons donc filter sur la partie commune à toutes :

    
     GET /w00tw00t.at.ISC.SANS.
    
    
    Cela représente 26 octets, auxquels nous en ajouterons 44 par sécurité (dont une douzaine pour le champs "Options" de l'en-tête du paquet TCP/IP), ce qui fera une recherche sur un maximum de 70 octets (paramètre --to) :
    
     # iptables -I INPUT -d xxx.xxx.xxx.xxx -p tcp --dport 80 -m string --to 70 \
     --algo bm --string 'GET /w00tw00t.at.ISC.SANS.' -j DROP
    
    
    Remplacez la chaîne 'xxx.xxx.xxx.xxx' par l'IP de votre serveur.

    Si votre serveur dispose de plusieurs IP et que celles-ci se suivent, vous pouvez n'utiliser qu'une seule règle et inclure la plage d'IPs avec l'option -m iprange. Dans le cas où elles ne se suivent pas vous devrez créer une règle pour chacune d'elles.
    Exemple avec les 5 adresses IP 1.0.0.1, 1.0.0.2, 1.0.0.3, 1.0.0.4 et 1.0.0.5 :

    
     # iptables -I INPUT -p tcp --dport 80 -m iprange --dst-range 1.0.0.1-1.0.0.5 \
     -m string --to 70 --algo bm --string 'GET /w00tw00t.at.ISC.SANS.' -j DROP
    
    
    Vérifiez que la règle est bien en place :
    
    # iptables -L INPUT -nvx
    
     pkts  bytes target ...
     0     0     DROP   ... STRING match "GET /w00tw00t.at.ISC.SANS." ALGO name bm TO 70
    
    
    Patientez quelques heures afin que votre ami DFind revienne vous voir et, normalement, vous ne devriez plus en avoir aucune trace dans vos logs. Cependant, vous aurez tout de même la joie de pouvoir consulter le nombre de paquets rejetés comme dans cet exemple :
    
    # iptables -L INPUT -nvx
    
     pkts  bytes target ...
     64    5504  DROP   ... STRING match "GET /w00tw00t.at.ISC.SANS." ALGO name bm TO 70
    
    
    On voit ici que 64 paquets on été "droppés", pour un total de 5504 octets, soit 86 octets/paquets.


    Comment se débarasser de (presque) tous les scanners HTTP ?

    Toujours dans vos logs apache, vous avez peut-être remarqué de nombreuses traces de scans recherchant des vulnérabilités comme des failles php, awstats etc ? Ou pire, des recherches de vulnérabilités IIS alors que vous utilisez un serveur Linux ? Plutôt que de créer des centaines de règles avec mod_security qui, nous l'avons vu, ne vont pas totalement protéger apache mais en plus risquent de terriblement ralentir son temps de réponse, nous allons nous débarasser de la presque (j'insiste sur le "presque" car il y a des exceptions) totalité de ces scanners idiots avec une seule et unique règle iptables.

    Voyons à nouveau comment doit se présenter une requête HTTP 1/1. Si l'adresse IP de votre serveur est "110.220.110.220" et que votre nom de domaine est "domaine.com", lorsqu'un utilisateur va demander la page racine de votre site "/", sa requête devra comporter au moins les 2 éléments suivants :

    
     GET / HTTP/1.1
     Host: domaine.com
    
    
    Maintenant la même requête avec un scanner de vulnérabilité :
    
     GET / HTTP/1.1
     Host: 110.220.110.220
    
    
    Toute la différence se trouve dans le champ "Host:". L'utilisateur connait votre nom de domaine mais le scanner, lui, ne le connait pas. Il scanne les IPs, pas les domaines et de ce fait est très facilement détectable.
    Donc nous pouvons bloquer tout simplement ces scanners en rejetant avec iptables toutes les requêtes HTTP entrantes qui utilisent votre IP au lieu de votre nom de domaine :
    
     # iptables -I INPUT -d xxx.xxx.xxx.xxx -p tcp --dport 80 -m string --to 700 \
     --algo bm --string 'Host: xxx.xxx.xxx.xxx' -j DROP
    
    
    Il vous suffit juste de remplacer les 2 séries de 'xxx.xxx.xxx.xxx' avec l'IP de votre serveur. Le filtrage s'effectue ici sur les 700 premiers octets (--to 700) car d'autres champs et variables pourraient se trouver entre les lignes "GET" et "Host:".
    Patientez quelques heures et lancez la commande de visualisation des règles  # iptables -L INPUT -nvx  pour voir le nombre de scanners qui se sont englués dans votre firewall sans avoir eu le temps de polluer vos logs. Sans oublié bien entendu que vous leur avez fait perdre du temps avec le 'DROP' :)

    A noter : vous pouvez aussi pour plus de précision dans le filtrage utiliser le paramètre --hex-string en encodant la chaîne de caractères "Host: xxx.xxx.xxx.xxx" en notation héxadécimale et en la faisant précéder et suivre des 2 CR/LF présents dans la requête ("| 0d 0a CHAINE_ENCODEE_HEX 0d 0a|").


      [ 2e partie : affiner la règle >> ]