Le port-knocking ou comment cacher un service
Publié le 31 décembre 2009 - GNU/Linux
Dernière mise à jour le 07 juin 2011
- Article
- |
- Commentaires (0)
- |
- Fichiers attachés (0)
La technique du "port-knoking" consiste à insérer une règle préalablement définie dans un pare-feu en cas de réception, dans un délai donné, d'une séquence de paquets TCP et/ou UDP. Elle permet de cacher un service sur un serveur et de renforcer sa sécurité. Un petit daemon existe sous GNU/Linux pour mettre en place une telle porte dérobée, knockd.

Photographie de Jérôme Plano
Présentation du Port-Knocking
La page traitant du port-knocking sur WikiPédia est comme souvent fort bien réalisée, en voici un extrait :
« Un processus s'exécute en arrière-plan (daemon) et scrute le journal des connexions du pare-feu, ou une analyse de paquets passant par un port déjà ouvert, permet de détecter une séquence particulière. Le serveur garde une liste des ports sur lesquels ont été tentés des connexions en fonction de l'adresse IP de l'émetteur. À chaque tentative de connexion, le port est ajouté à la liste des ports pour cette adresse IP. Si la liste contient une séquence reconnue, les règles du pare-feu seront modifiées pour ouvrir un port défini à l'avance, et d'autres commandes peuvent être exécutées de cette manière. La complexité de la séquence peut varier de la simple liste (par exemple : TCP port 1000, TCP port 2000, UDP port 3000) à une vérification de la durée et à des données chiffrées.
La seule indication d'échec est de ne pas avoir de port nouvellement ouvert à la fin de la séquence. À aucun moment des informations sont envoyées au lanceur de séquence ; le système reste muet.
Lorsqu'un port a été ouvert par la séquence correcte, les règles du pare-feu n'autorisent que l'adresse IP ayant lancé la séquence à communiquer sur le port ouvert. Le port peut de nouveau être fermé après un délai prévu ou à la demande. »
Une petite série de schéma est disponible sur le site de l'université de Marne-la-Vallée.
Installation de knockd
Avant tout, sachez qu'il vous faut un firewall correctement configuré sur votre machine pour pouvoir utiliser knockd. Sous GNU/Linux, le firewall est NetFilter, associé à son principal outil de configuration : IPtables. Il existe une multitude d'outils graphiques pour configurer NetFilter simplement.
Knockd s'installe lui très simplement grâce à apt-get :
sudo apt-get install knockd
Une fois installé, le daemon se lance en employant la commande suivante :
/etc/init.d/knockd start
Comme souvent, on peut utiliser start, stop, restart, reload et force-reload pour contrôler le daemon.
Les options de lancement sont :
- -i, --interface <int>
Permet de spécifier l'interface à écouter. Par défaut eth0.
- -d, --daemon
Lancer en tant que daemon. Cette option est positionnée par défaut sous Ubuntu, directement dans le script de lancement de knockd.
- -c, --config <file>
Permet de spécifier un fichier de configuration. Par défaut le fichier est /etc/knockd.conf.
- -D, --debug
Active les messages de debug.
- -l, --lookup
Réaliser une résolution de noms pour les lignes du fichier de logs. Activer cette option peut représenter un risque de sécurité !
- -v, --verbose
Active le mode verbeux.
- -V, --version
Affiche le numéro de version de knockd.
- -h, --help
L'aide syntaxique des options de lancement.
Notez que sous Ubuntu, les options sont à indiquer dans le fichier /etc/default/knockd plutôt que directement dans le script /etc/init.d/knockd. Voici un exemple de ce fichier :
################################################ # # knockd's default file, for generic sys config # ################################################ # control if we start knockd at init or not # 1 = start # anything else = don't start # # PLEASE EDIT /etc/knockd.conf BEFORE ENABLING START_KNOCKD=1 # command line options KNOCKD_OPTS="-i eth0"
Dans l'exemple ci-dessus, on spécifie l'interface à écouter avec -i eth0. Il semble qu'en spécifier une en cas d'interfaces multiples soit obligatoire, puisque knockd refuse de démarrer sans cela.
C'est aussi dans ce fichier que l'on indique si le daemon doit être démarré au lancement du système, via l'option "START_KNOCKD=1".
L'option -d est quant à elle déjà positionnée dans le fichier de lancement /etc/init.d/knockd, il n'est donc pas utile de la mettre dans /etc/default/knockd.
Configuration
Sous Ubuntu, le fichier de configuration est /etc/knockd.conf. Il se présente ainsi par défaut :
[options] UseSyslog [openSSH] sequence = 7000,8000,9000 seq_timeout = 5 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn [closeSSH] sequence = 9000,8000,7000 seq_timeout = 5 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn
C'est ce fichier qui permet de définir les séquences de ports à écouter et les actions à réaliser en cas de détection d'une séquence valide.
Les options
Elles sont peu nombreuses et se trouvent en dessous de [options] dans le fichier :
- UseSyslog
Utiliser Syslog pour enregistrer les logs. Par défaut les enregistrements vont dans /var/log/messages.
- LogFile = /chemin/vers/fichier
Ne pas utiliser Syslog et enregistrer directement les logs dans le fichier indiqué.
- PidFile = /chemin/vers/fichier
Permet de spécifier le fichier pid, il s'agit par défaut de /var/run/knockd.pid.
- Interface = <nom_interface>
L'interface à écouter, comme eth0 ou venet0 par exemple.
Les règles d'ouverture/fermeture
Il est possible d'en spécifier plusieurs, en les indiquant sous un nom placé entre crochets, par exemple [ouvrirFTP]. Voici la liste des directives utilisables :
- Sequence = <port1>[:<tcp|udp>][,<port2>[:<tcp|udp>] ...]
Spécifie une séquence de ports. Si une erreur de port a lieu pendant la séquence, elle est entièrement invalidée. En option, il est possible de définir le protocole (TCP ou UDP) à utiliser pour chaque port (par défaut TCP).
- One_Time_Sequences = /chemin/vers/lefichierdesequence
Spécifie le fichier contenant les séquences uniques à utiliser. Au lieu d'utiliser une séquence fixe, knockd va lire ce fichier pour y trouver un séquence. Après chaque séquence correctement réalisée, knock va désactiver cette séquence en la commentant avec un "#" placé en remplacement du premier caractère de la ligne de cette séquence. Cette séquence désactivée sera remplacée par la prochaine séquence contenue dans le fichier.
Puisque knockd va remplacer par un "#" le premier caractère, il est recommandé de laisser un espace au début de chaque ligne de ce fichier, sous peine de voir les séquences compromises par le "#".
Chaque ligne du fichier doit contenir une et une seul séquence, écrite dans le même format que pour l'option "Sequence". Les lignes commençant par "#" sont ignorées.
Note: Ne jamais éditer cette ligne lorsque knockd est en fonctionnement !
- Seq_Timeout = <délai>
Le temps durant lequel une séquence doit être exécutée. Au delà de ce délai, la séquence est annulée et doit être recommencée depuis le début.
- TCPFlags = fin|syn|rst|psh|ack|urg
Permet d'ignorer les paquets qui n'ont pas les flags TCP spécifiés. Si cette option est activée, knockd va ignorer les paquets TCP qui n'ont pas le-s bon-s flag-s, sans pour autant invalider la séquence en cours. C'est un comportement différent de la normale, où knockd invalide entièrement une séquence en cours si un mauvais port est frappé, obligeant le client à recommencer du début.
Utiliser "TCPFlags = syn" est utile si vous réalisez la séquence dans un tunnel SSH, puisque le trafic SSH risque d'interférer avec la séquence et donc d'invalider cette dernière.
Séparez de multiples flags avec une virgule (par exemple : "TCPFlags = syn,ack,urg"). Il est possible d'exclure des flags en utilisant le point d'exclamation "!" (par exemple, "TCPFlags = syn,!ack" correspond au flag "syn" sans flag "ack").
- Start_Command = <commande>
C'est la commande à exécuter en cas de réception valide de la séquence. %IP% sera remplacé par l'adresse IP du client aillant correctement réalisé la séquence. La directive "command" est un alias vers "Start_Command"
- Cmd_Timeout = <délai>
Le délai entre l'exécution de Start_Command et Stop_Command. Indiquer ce délai est optionnel, sauf si Stop_Command est utilisé.
- Stop_Command = <commande>
C'est la commande qui sera exécutée une fois le délai spécifié avec Cmd_Timeout écoulé. %IP% sera remplacé par l'adresse IP du client ayant correctement réalisé la séquence. Cette directive est optionnelle.
Surveiller les logs
Le fichier de logs peut être choisi avec l'option "LogFile". Voici à quoi ressemble les enregistrements réalisés par knockd :
[2009-12-31 09:51] openSSH: running command: /sbin/iptables -D INPUT -s 192.168.36.3 -p tcp --dport 22316 -j ACCEPT [2009-12-31 09:51] 192.168.36.3: openSSH: command timeout [2009-12-31 09:51] openSSH: running command: /sbin/iptables -I INPUT -s 192.168.36.3 -p tcp --dport 22316 -j ACCEPT [2009-12-31 09:51] 192.168.36.3: openSSH: OPEN SESAME [2009-12-31 09:51] 192.168.36.3: openSSH: Stage 4 [2009-12-31 09:51] 192.168.36.3: openSSH: Stage 3 [2009-12-31 09:51] 192.168.36.3: openSSH: Stage 2 [2009-12-31 09:51] 192.168.36.3: openSSH: Stage 1 [2009-12-31 09:49] starting up, listening on eth0
Pensez à surveiller les échecs trop fréquents, qui se présentent sous cette forme :
[2009-12-19 14:52] 192.168.38.112: closeLightHTTP: sequence timeout (stage 2) [2009-12-19 14:14] 192.168.38.112: closeLightHTTP: Stage 2 [2009-12-19 14:14] 192.168.38.112: closeLightHTTP: Stage 1
Une multiplication d'enregistrements de ce genre avec la même IP source peut indiquer une tentative de découverte par brute force. Une règle d'interdiction ajoutée à votre firewall devrait suffire à refroidir la situation.
Comment réaliser une séquence
C'est bien gentil de mettre en place plein de séquences, mais il faudrait savoir comment les réussir sur le serveur depuis une machine distante maintenant !
Le plus simple est d'utiliser le petit outil nommé knock qui est installé en même temps que le daemon knockd. Il est donc aussi utile d'installer le paquet "knockd" sur les machines clientes, même si elles ne feront par tourner le daemon.
L'utilisation de knock est très simple :
knock -v 192.168.36.3 20000 30000:udp 40000 50000:udp
La commande ci-dessus va envoyer la séquence "TCP/20000" puis "UDP/30000" puis "TCP/40000" et enfin "UDP/50000" à la machine d'adresse IP 192.168.36.3.
Il est évidemment possible d'utiliser des outils plus évolués pour réaliser les séquences. Je vous recommande HPing, qui fait ça très bien et s'installe facilement sous Ubuntu :
sudo apt-get install hping3
HPing est un outil puissant et complexe, il faut donc prendre un peu le temps de comprendre comment il fonctionne. La page de manuel est superbe :
man hping3
Les outils pour forger des paquets TCP/UDP/etc sont nombreux, libre à vous de trouver celui qui vous convient. :-)
Exemples de configuration
Voici quelques exemples simples de configuration. Notez que dans tous les exemples donnés, les caractères %IP% seront remplacés par l'adresse IP de la machine ayant réussi une séquence.
Pour un serveur SSH
Il s'agit ici d'ouvrir un port pour s'y connecter. Une fois la connexion établie dans le cadre d'une connexion maintenue, il n'est pas nécessaire de garder ce port ouvert, il se refermera donc automatiquement après un délai choisi.
[openSSH] sequence = 5555:udp,6666,7777,8888:udp seq_timeout = 8 command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn,urg cmd_timeout = 15 stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
Ici, "seq_timeout" indique que la "sequence" doit être réalisée en moins de 8 secondes, il faut donc que les quatre ports indiqués soient frappés dans un délai de 8 secondes. Les paquets doivent avoir les flags "syn" et "urg" comme le précise "tcpflags".
Une fois la séquence correctement réalisée, la "command" sera exécutée (elle ouvre le port TCP/22 pour l'adresse IP qui a réalisé la séquence correctement), et elle restera active pendant 15 secondes grâce à "cmd_timeout". C'est à dire qu'au bout de 15 secondes, le port TCP/22 sera automatiquement refermé en utilisant la commande "stop_command", qui referme le port TCP/22.
Pour un serveur HTTP
Voilà un exemple de configuration en deux temps. La première séquence permet d'ouvrir le port, et la seconde de le refermer. C'est particulièrement utile pour les connexions HTTP et plus généralement pour les connexions non-maintenues, dans la mesure où l'on ne sait pas précisément combien de temps on va avoir besoin de l'accès au serveur.
[openHTTP] sequence = 17000,38000,29000 seq_timeout = 5 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 80 -j ACCEPT tcpflags = syn [closeHTTP] sequence = 49000,18000,37000 seq_timeout = 5 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 80 -j ACCEPT tcpflags = syn
La première séquence, celle de "openHTTP" va donc ouvrir le port TCP/80 en cas de réussite. La réussite étant de frapper les ports TCP/17000 puis TCP/38000 et TCP/29000 avec des paquets ayant le flag "syn", le tout en moins de 5 secondes.
Puis la seconde séquence, celle de "closeHTTP" va fermer ce port TCP/80 une fois la besogne accomplie. Il faut bien sûr ne pas oublier de refermer le port, ou bien automatiser cette tâche via un script qui se lance par exemple à la fermeture du navigateur.
Pour finir
Le port knocking permet de protéger un service efficacement. La seule méthode force brute est utilisable pour découvrir une séquence, et une simple séquence de trois ports représente déjà un nombre astronomique de possibilités à tester pour un attaquant. Notez tout de même que le sniffing permet de découvrir autrement plus rapidement une séquence.
Notez aussi que si votre daemon knockd se termine anormalement pour une raison ou une autre, l'accès au serveur sera impossible via SSH, ce qui peut être très gênant... Il est néanmoins possible de mettre en place un script pour relancer le daemon si celui-ci disparaît sans raison.

