Port knocking ou comment dissimuler un service

Ce n'est pas la première fois que vous le lisez sur vos logs et ce ne sera sûrement pas la dernière. Et oui, le port scanning fait partie intégrante d'une attaque dans la mesure où il permet d'identifier exactement ce qui tourne sur la machine  victime et beaucoup d'autres informations relatives aux équipements en amont de celle-ci. Le port scanning ne débouche pas nécessairement sur un exploit, néanmoins, il le précède toujours. Pour vous prémunir un minimum de ce mapping, une des solutions consiste à implémenter le port knocking. Le port knocking est une technique complémentaire de sécurisation des serveurs qui permet d'identifier une séquence bien précise et de la lier à une commande. La plus évidente des commandes est d'ajouter ou de supprimer une règle sur le firewall afin d'ouvrir ou de fermer un port, mais il peut en être tout autrement. Cela signifie entre autre que tant que la séquence secrète n'a pas été produite, et ce dans un temps donné, le port restera fermé et donc opaque vu de l'extérieur. Par extension, si un jour le daemon de port knocking est dans les choux, vous ne pourrez plus ouvrir dynamiquement vos ports (donc plus de SSH par exemple). Mieux vaut donc avoir son serveur sous la main...

Fonctionnement

Le port knocking est réalisé par un daemon qui se situe sous le firewall. De cette façon, il peut monitorer toutes les tentatives de connexion et détecter si une séquence bien définie a été initialisée dans un temps donné, suite à quoi il modifie les règles du firewall en conséquence. Durant toute la séquence, le comportement du firewall reste inchangé (aucun ACK), tout se fait sous silence, en submersible afin d'être le moins suspicieux possible.

Sequence

Une séquence est une tentative de connexion sur une suite de port, associé chacun à son protocole et avec un ou plusieurs flags en particulier. Une séquence peut également être sur une seule tentative, mais encapsulant le knock dans le payload du paquet. Il est possible de définir une séquence encryptée. Pour vous y essayer, vous pouvez tout aussi bien passer par knock lab.

Implementations

Le port knocking est avant tout une technique, il existe donc beaucoup d'implémentations qui l'illustrent. Comme c'est un package que nous avons retrouvé sur les dépôts de la distribution, nous allons travailler sur knockd.

Knockd est un trousseau d'outil qui contient le serveur de port knocking mais aussi un client rudimentaire qui permettra à un utilisateur de réaliser la séquence de knock.

Installation et tests

On va partir des sources, afin que la mise en oeuvre soit le plus plateforme independant possible. Les sources iront direct dans /usr/local/src/. Au jour où j'écris ces lignes, la dernière version est la 0.5.
$ cd /usr/local/src/
$ wget http://www.zeroflux.org/proj/knock/files/knock-0.5.tar.gz
$ tar zxf knock-0.5.tar.gz
$ cd knock-0.5
Le README nous fournit plus d'info concernant le projet mais aussi des liens concernant d'autres implémentations et d'autres clients.
$ cat README

knock :: A port-knocking implementation
=======================================

Copyright (c) 2004-2005, Judd Vinet <jvinet@zeroflux.org>

=========
ABOUT
=========
This is a port-knocking server/client.  Port-knocking is a method where a
server can sniff one of its interfaces for a special "knock" sequence of
port-hits.  When detected, it will run a specified event bound to that port
knock sequence.  These port-hits need not be on open ports, since we use
libpcap to sniff the raw interface traffic.

===========
EXAMPLE
===========
The example below could be used to run a strict (DENY policy) firewall that
can only be accessed after a successful knock sequence.

1) Client sends four TCP SYN packets to Server, at the following ports:
38281, 29374, 4921, 54918

2) Server detects this and runs an iptables command to open port 22 to Client.

3) Client connects to Server via SSH and does whatever it needs to do.

4) Client sends four more TCP SYN packets to Server:
37281, 8529, 40127, 10100

5) Server detects this and runs another iptables to close port 22 to Client.

====================
KNOCKING CLIENTS
====================
The accompanying knock client is very basic.  If you want to more advanced
knocks (eg, setting specific tcp flags) then you should take look at hping,
sendip or packit.

http://freshmeat.net/projects/hping/
http://freshmeat.net/projects/sendip/
http://freshmeat.net/projects/packit/

=========================
OTHER IMPLEMENTATIONS
=========================
Here are some other implementations of port-knocking:

http://sourceforge.net/projects/pasmal/
http://doorman.sourceforge.net/
En dehors de ce README, aucune indication sur l'install, mais la présence d'un configure et d'un Makefile.in est suffisamment explicite sur la marche à suivre.
$ ./configure --help
`configure' configures FULL-PACKAGE-NAME VERSION to adapt to many kinds of systems.

Usage: ./configure [OPTION]... [VAR=VALUE]...

To assign environment variables (e.g., CC, CFLAGS...), specify them as
VAR=VALUE.  See below for descriptions of some of the useful variables.

Defaults for the options are specified in brackets.

Configuration:
-h, --help              display this help and exit
--help=short        display options specific to this package
--help=recursive    display the short help of all the included packages
-V, --version           display version information and exit
-q, --quiet, --silent   do not print `checking...' messages
--cache-file=FILE   cache test results in FILE [disabled]
-C, --config-cache      alias for `--cache-file=config.cache'
-n, --no-create         do not create output files
--srcdir=DIR        find the sources in DIR [configure dir or `..']

Installation directories:
--prefix=PREFIX         install architecture-independent files in PREFIX
[/usr/local]
--exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
[PREFIX]

By default, `make install' will install all the files in
`/usr/local/bin', `/usr/local/lib' etc.  You can specify
an installation prefix other than `/usr/local' using `--prefix',
for instance `--prefix=$HOME'.

For better control, use the options below.

Fine tuning of the installation directories:
--bindir=DIR           user executables [EPREFIX/bin]
--sbindir=DIR          system admin executables [EPREFIX/sbin]
--libexecdir=DIR       program executables [EPREFIX/libexec]
--datadir=DIR          read-only architecture-independent data [PREFIX/share]
--sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR    modifiable single-machine data [PREFIX/var]
--libdir=DIR           object code libraries [EPREFIX/lib]
--includedir=DIR       C header files [PREFIX/include]
--oldincludedir=DIR    C header files for non-gcc [/usr/include]
--infodir=DIR          info documentation [PREFIX/info]
--mandir=DIR           man documentation [PREFIX/man]

Some influential environment variables:
CC          C compiler command
CFLAGS      C compiler flags
LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir>
CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
headers in a nonstandard directory <include dir>
CXX         C++ compiler command
CXXFLAGS    C++ compiler flags
CPP         C preprocessor

Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.

Report bugs to <BUG-REPORT-ADDRESS>.
Si les paramétrages par défaut vous conviennent, ce qui est mon cas, il suffit d'invoquer un :
$ ./configure
$ make
Premier souci de la manip', une erreur de compilation arrête net le make :
$ make
gcc -g -O2 -g -Wall -pedantic -fno-exceptions -D_GNU_SOURCE -I. -o src/knockd.o -c src/knockd.c
src/knockd.c:134: erreur: ‘PATH_MAX’ undeclared here (not in a function)
src/knockd.c: In function ‘parseconfig’:
src/knockd.c:438: attention : unused variable ‘line’
src/knockd.c: In function ‘get_next_one_time_sequence’:
src/knockd.c:695: attention : unused variable ‘line’
src/knockd.c: In function ‘sniff’:
src/knockd.c:1386: attention : unused variable ‘parsed_stop_cmd’
src/knockd.c:1385: attention : unused variable ‘parsed_start_cmd’
make: *** [src/knockd.o] Erreur 1
L'erreur se situe sur les lignes 134 et 135 de src/knockd.c :
char o_cfg[PATH_MAX]     = "/etc/knockd.conf";
char o_pidfile[PATH_MAX] = "/var/run/knockd.pid";
Il suffit pour débloquer d'ajouter en tête du fichier un #define qui contiendra par exemple :
#define PATH_MAX    2048
Et on relance !
$ make
gcc -g -O2 -g -Wall -pedantic -fno-exceptions -D_GNU_SOURCE -I. -o src/knockd.o -c src/knockd.c
src/knockd.c: In function ‘get_ip’:
src/knockd.c:1077: attention : dereferencing pointer ‘({anonymous})’ does break strict-aliasing rules
src/knockd.c:1077: note: initialized from here
gcc -g -O2 -g -Wall -pedantic -fno-exceptions -D_GNU_SOURCE -I. -o src/list.o -c src/list.c
gcc ./src/knockd.o ./src/list.o -o knockd  -lpcap
gcc -g -O2 -g -Wall -pedantic -fno-exceptions -D_GNU_SOURCE -I. -o src/knock.o -c src/knock.c
gcc ./src/knock.o -o knock
sed -e "s/#VERSION#/0.5/" doc/knockd.1.in > doc/knockd.1
sed -e "s/#VERSION#/0.5/" doc/knock.1.in > doc/knock.1

$ make install
/usr/bin/install -c -D -m0755 knockd /usr/local/sbin/knockd
/usr/bin/install -c -D -m0755 knock /usr/local/bin/knock
/usr/bin/install -c -D -m0644 ./doc/knockd.1 /usr/local/man/man1/knockd.1
/usr/bin/install -c -D -m0644 ./doc/knock.1 /usr/local/man/man1/knock.1
/usr/bin/install -c -D -m0644 knockd.conf /etc/knockd.conf

Installation completed ! Jetons un oeil sur la conf !

La configuration se trouve sous /etc, dans un fichier bien nommé knockd.conf.
$ cat /etc/knockd.conf
[options]
logfile = /var/log/knockd.log

[openSSH]
sequence    = 7000,8000,9000
seq_timeout = 5
command     = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags    = syn

[closeSSH]
sequence    = 9000,8000,7000
seq_timeout = 5
command     = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
tcpflags    = syn
On peut vite se rendre compte que la syntaxe de la conf est tout ce qu'il y a de plus limpide. Le second point est que la configuration par défaut invoque des règles iptables pour les 2 comportements décrits : une pour ajouter une règle à la table INPUT qui ouvre le port 22, et l'autre qui la supprime. Par exemple, pour la section openSSH, la lecture est la suivante : Si l'on tape en tcp, avec le flag syn armé sur les ports 7000, 8000 et 9000, le tout maximum 5 secondes, la séquence est identifiée comme valide et déclenche /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT, avec %IP% correspondant à l'IP du knocker. En clair, seule l'IP du knocker est autorisée sur le port 22 à la validation de la séquence. Et si vous voulez taper par udp, rien ne vous empêche de le formuler dans la séquence : 7000:tcp,8000:udp,9000:tcp Pour les flags à utiliser, vous avez le choix entre fin, syn, rst, psh, ack et urg. Enfin, si vous souhaitez moduler en terme de temps d'ouverture de port plutôt qu'en ouverture tout court, il vous faut restructurer la section comme suit :
[opencloseSSH]
sequence      = 2222:udp,3333:tcp,4444:udp
seq_timeout   = 15
tcpflags      = syn,ack
start_command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --syn -j ACCEPT
cmd_timeout   = 5
stop_command  = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --syn -j ACCEPT
Petit détail, au bout de 15 secondes, le port est fermé, mais comme votre firewall est à état (statefull), la connexion établie perdure. Allez, un petit dernier pour la route ! Ici, nous ne voulons pas spécifier une séquence unique et fixe, mais un ensemble de séquence, que nous définirons dans le fichier /etc/knockd/smtp_sequences. Ces séquences déclencheront l'ouverture du port 25 (oui, oui, le smtp) pour une durée de 5 secondes. Détail intéressant, knockd va se charger de commenter la séquence précédemment utilisée, ce qui protègera contre les attaque de rejeu dans le cas où votre séquence a été sniffée.
[opencloseSMTP]
one_time_sequences = /etc/knockd/smtp_sequences
seq_timeout        = 15
tcpflags           = fin,!ack
start_command      = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 25 -j ACCEPT
cmd_timeout        = 5
stop_command       = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 25 -j ACCEPT
Bien entendu, il faut vraiment éviter de trifouiller dans notre /etc/knockd/smtp_sequences pendant que knockd tourne. Egalement intéressant dans le cadre d'une centralisation de log, vous avez la possibilité de paramétrer knockd afin qu'il stocke ses infos via syslog grâce à l'option UseSyslog qui est à renseigner dans la section [options]. Pour pouvoir être opérationnel, knockd a besoin de rechercher dans les logs du firewall les éléments concernant une éventuelle combinaison gagnante. Cet élément implique que la machine supportant knockd ait accès à ces logs, et de façon réactive. En clair, si vous centraliser ces logs, mieux vaut avoir de bons équipements réseau, dédiés et une liaison qui envoie... Une règle d'or lors de l'installation d'outil de ce genre sur des machines exposées au World Wild Web, ne jamais laisser la configuration par défaut. Pour vous en convaincre, pensez que n'importe qui peut installer ce soft, et si ça ne suffit pas, vous pouvez toujours taper sur http://defaultpassword.com/. Donc première chose à faire, trouver une bonne séquence et paramétrer la commande qu'elle déclenchera. Il ne vous reste plus qu'à le lancer, mode daemon, s'il vous plait !
$ /usr/local/sbin/knockd -d
$ tail -f /var/log/knockd.log
[2010-08-25 16:14] starting up, listening on eth0
[2010-08-25 16:14] 192.168.20.3: openSSH: Stage 1
[2010-08-25 16:14] 192.168.20.3: openSSH: Stage 2
[2010-08-25 16:16] 192.168.20.3: openSSH: Stage 3
[2010-08-25 16:17] 192.168.20.3: openSSH: Stage 4
[2010-08-25 16:19] 192.168.20.3: openSSH: OPEN SESAME
[2010-08-25 16:20] openSSH: running command: /sbin/iptables -I INPUT -s 192.168.20.3 -p tcp --dport 22316 -j ACCEPT
[2010-08-25 16:25] 192.168.20.3: openSSH: command timeout
[2010-08-25 16:25] openSSH: running command: /sbin/iptables -D INPUT -s 192.168.20.3 -p tcp --dport 22316 -j ACCEPT

Knocker

C'est bien beau tout ça, mais comment expliquer l'art du knock à vos utilisateurs ? Car oui, pas tout le monde n'est capable de knocker dans les règles de l'art, et d'ailleurs, la manipulation en soi implique des outils bien spécifiques, que vos utilisateurs ne manient pas tous les jours (ou si, mais avec peu de curiosité). Des outils comme telnet, netcat... Ou bien encore le client knock, construit avec nos sources. Ces outils sont tout-à-fait pertinents pour des séquences TCP simples. Knock a une syntaxe simplicime : knock [options] <host> <port[:proto]> [port[:proto]] ..., les options étant principalement udp (si tous nos knocks sont en udp), verbose, help et version. Quand je vous disais simplicime... Le knock effectué, la commande renvoie la main sans retourner d'infos, ce qui est normal puisqu'on joue la carte de la discrétion. En revanche, si vous cherchez un outil un peu plus élaboré, tournez-vous vers hping, sendip ou packit. Leurs utilisations sort du contexte de cet article et nécessiterait un post à part entière, ça sera donc pour une autre fois ! Mais rien ne vous empêche de jouer avec !

Conclusion

Le port knocking est une technologie très facilement mise en place et qui permet de rajouter un niveau de sécurité sur les accès. Pour autant, son exploitation ne signifie pas que le site est entièrement protégé, puisqu'il est potentiellement capable de faire appel à beaucoup d'autres services et donc potentiellement plus vulnérable que s'il était complètement indépendant. Bref, le port knocking ne doit pas être votre seule/dernière cartouche. Plus généralement, gardez toujours à l'esprit qu'un système aussi près du front doit être à jour dans ses packages, dans son OS, disposer de multiples protections et qu'il n'y a rien qui ne puisse être invulnérable (pas même une machine éteinte). A bon entendeur... Bonne soirée !
Vus : 1586
Publié par K-Tux : 59