Por Domingo González

Hace algún tiempo se publicó en Kriptópolis un interesante artículo titulado Seguridad por oscuridad, en cuyo debate se habló entre otras cosas del uso del port-knocking para dificultar posibles ataques a servicios. En aquel momento me ofrecí para escribir un artículo sobre mis experiencias en la puesta en marcha de la técnica de port-knocking que tenía previsto en breve implementar en un servidor en producción. Actualmente, muy a pesar mío, debido a numerosos retrasos y cambio de prioridades sigue en la lista de tareas pendientes.

No obstante, he estado haciendo pruebas "de andar por casa" con las dos herramientas de port-knocking que tenía como candidatas (knockd y fwknop), a fin de comprobar su funcionamiento, estudiar sus ventajas e inconvenientes, y decidirme por una de ellas. Para ello he instalado y configurado ambas herramientas de port-knocking en una máquina Debian con servidor SSH, comprobando el funcionamiento y la seguridad de una herramienta básica de port-knocking como es knockd frente a otra más avanzada como fwknop que utiliza Single Packet Authorization (SPA). Tanto el servidor de port-knocking como el cliente están instalados en la misma máquina, usando la interfaz local (localhost) para establecer las conexiones. He optado por utilizar la interfaz localhost por su comodidad, siendo suficiente para ilustrar el funcionamiento del port-knocking, y además las pruebas son fácilmente reproducibles por el lector y extensibles a cualquier otra interfaz...

Sin constituir una información tan valiosa como la obtenida de la experiencia real en un servidor en producción, creo que la descripción en este artículo de estas sencillas pruebas de funcionamiento puede ser de interés para los lectores de este portal y me permite además hacer frente (por ahora) a mis compromisos adquiridos.

El enfoque del artículo es principalmente práctico, sin pretender en ningún momento ser exhaustivo ni profundizar en un tema como este tan extenso, sobre todo cuando la documentación oficial existente es abundante y detallada. Tampoco se pretende incidir en el aspecto teórico del port-knocking, disponiendo de numerosa información en las referencias que se proporcionan a lo largo de este artículo.

1.- Breve resumen de la técnica de port-knocking

El port knocking (literalmente golpeo de puertos) es una técnica mediante la cual el cortafuegos, que mantiene cerrado un determinado puerto, detecta una secuencia preestablecida que procede de una conexión externa y abre dicho puerto para que el servicio asignado al puerto sea accesible. Este mecanismo permite añadir una capa adicional de seguridad (por oscuridad), de manera que para cualquier conexión externa no autorizada el puerto aparece como cerrado, eliminando así la posibilidad de intentar la conexión y no ofreciendo información sobre el servicio configurado. Por su parte, el usuario autorizado necesita disponer de un cliente de port-knocking que envíe una secuencia de paquetes dirigidos a dicho puerto, con el fin de que el cortafuegos detecte la secuencia correcta y abra el puerto dejando accesible el servicio. Una vez desactivada esta capa de seguridad, el servicio está accesible para realizar el intento de conexión (por ejemplo SSH).

Se puede encontrar más información sobre port-knocking en la wikipedia y sobre todo en la página oficial de portknocking.org.

knockd es una herramienta de port-knocking de primera generación, que funciona enviando una secuencia ordenada de intentos de conexión a puertos TCP o UDP. El principal problema de esta herramienta desde el punto de vista de la seguridad es la posibilidad de que la secuencia de puertos sea interceptada por un atacante, siendo por tanto superado este primer filtro con facilidad. Además, la pérdida de paquetes en la comunicación puede dar lugar a que la secuencia no llegue en el orden correcto.

Estas limitaciones están solucionadas con la utilización de herramientas de nueva generación como fwknop, que implementa Single Packet Authorization (SPA), conocido como técnica de port-knocking de paquete único. En este caso, toda la secuencia -o contraseña- se encuentra dentro de un único paquete cifrado, evitando así la captura de los datos y no teniendo el problema de pérdida parcial de paquetes. Otras funcionalidades de fwknop aumentan la seguridad, como la inclusión de la IP de origen dentro del paquete cifrado, lo que dificulta un ataque Man in the Middle, así como el soporte para usar criptografía de clave pública.

Se puede encontrar muy buena información sobre fwknop y el funcionamiento de SPA en la página oficial de fwknop.

2.- Implementando port-knocking tradicional con knockd

Para instalar las herramientas de port-knocking hay que instalar el paquete knockd, que contiene el servidor y el cliente.

# aptitude install knockd
knockd disabled: not starting. To enable it edit /etc/default/knockd ... (warning).

A continuación configuramos el servidor de port-knocking editando los ficheros /etc/knockd.conf y /etc/default/knockd. En primer lugar editamos el fichero knockd.conf, que tiene el siguiente contenido por defecto:

#/etc/knockd.conf 
[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

Los parámetros habituales son:

  • UseSyslog, que envía los logs a /var/log/syslog. Si se desea un fichero de log propio, hay que utilizar LogFile=/var/log/knockd.log
  • sequence, que especifica la secuencia de puertos y tipo de paquete a enviar para hacer el portknocking (por defecto TCP, pero se puede especificar una secuencia más compleja como sequence=2140:udp,4500:tcp,3025:udp).
  • seq_timeout, que especifica el periodo máximo de tiempo en segundos en el que la secuencia completa es recibida como secuencia válida.
  • tcpflags, que especifica los flags que deben ser tenidos en cuenta (fin|syn|rst|psh|ack|urg).
  • command, que especifica el comando a ejecutar cuando se reconoce la secuencia definida.
  • %IP%, que toma del valor de la ip desde la que se envía la secuencia de puertos (el cliente de port-knocking).

Con esta configuración, el servidor knockd escucha permanentemente todos los puertos, esperando una secuencia válida. Si recibe paquetes TCP dirigidos a los puertos 7000, 8000 y 9000, en este mismo orden y dentro de un intervalo de 5 segundos, reconoce la directiva [openSSH] y ejecuta iptables para abrir el puerto 22 (donde escucha sshd) a la IP que solicita la conexión. Ahora el cliente SSH puede realizar la conexión, y podemos cerrar el puerto de nuevo con la directiva [closeSSH] manteniendo establecida dicha conexión.

A continuación se edita el fichero etc/default/knockd, donde se activa el funcionamiento y se define la interfaz donde debe escuchar el servidor knockd.

#/etc/default/knockd 
# control if we start knockd at init or not
# 1 = start
# PLEASE EDIT /etc/knockd.conf BEFORE ENABLING
START_KNOCKD=1
# command line options
KNOCKD_OPTS="-i eth0"

Una vez completada la configuración se inicia el servicio:

  1. # /etc/init.d/knockd start
  2. Starting Port-knock daemon: knockd

Como podemos observar en la lista de procesos activos, el servidor knockd se encuentra activo y escuchando en el puerto eth0:

# ps ax | grep knock
18876 ?        Ss     0:00 /usr/sbin/knockd -d -i eth0

Para probar knockd vamos a invocarlo manualmente, escuchando en la interfaz localhost y en modo verbose, para monitorizar los detalles de las conexiones en el propio terminal.

  1. # knockd -v -i lo
  2. listening on lo...

Ahora ejecutamos en otro terminal el comando knock, que es el cliente de port-knocking. Enviamos a la interfaz localhost la secuencia que abre el puerto del SSH, y a continuación la secuencia que vuelve a cerrar el puerto:

  1. $ knock -v localhost 7000 8000 9000
  2. hitting tcp 127.0.0.1:7000
  3. hitting tcp 127.0.0.1:8000
  4. hitting tcp 127.0.0.1:9000

  1. $ knock -v localhost 9000 8000 7000
  2. hitting tcp 127.0.0.1:9000
  3. hitting tcp 127.0.0.1:8000
  4. hitting tcp 127.0.0.1:7000

En tiempo real podemos ver en el terminal donde se ejecuta el servidor cómo knockd reconoce y ejecuta las acciones de iptables programadas.

  1. # knockd -v -i lo
  2. listening on lo...
  3. 127.0.0.1: openSSH: Stage 1
  4. 127.0.0.1: openSSH: Stage 2
  5. 127.0.0.1: openSSH: Stage 3
  6. 127.0.0.1: openSSH: OPEN SESAME
  7. openSSH: running command: /sbin/iptables -A INPUT -s 127.0.0.1 -p tcp --dport 22 -j ACCEPT
  8. 127.0.0.1: closeSSH: Stage 1
  9. 127.0.0.1: closeSSH: Stage 2
  10. 127.0.0.1: closeSSH: Stage 3
  11. 127.0.0.1: closeSSH: OPEN SESAME
  12. closeSSH: running command: /sbin/iptables -D INPUT -s 127.0.0.1 -p tcp --dport 22 -j ACCEPT

Es importante asegurarse de que permitimos en la configuración de iptables el acceso a conexiones ya establecidas, usando reglas del tipo
iptables -A INPUT -p tcp -m tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT.

La documentación oficial de knockd proporciona varios ejemplos adicionales de configuración para /etc/knockd.conf, siendo especialmente interesante este otro:

#/etc/knockd.conf 
	[opencloseSSH]
	        sequence = 7000,8000,9000
	        seq_timeout = 5
	        tcpflags = syn
	        start_command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
	        cmd_timeout = 25
	        stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp  --dport 22 -j ACCEPT

Esta configuración es similar a la anterior, pero define un parámetro cmd_timeout que determina el tiempo que debe pasar tras la apertura de puerto para que se vuelva a cerrar de forma automática. Con la configuración anterior, si nos olvidamos de realizar el cierre manualmente (secuencia inversa [closeSSH]) se queda abierto el puerto SSH en el cortafuegos, cosa que ahora ya no ocurre.

Probamos esta configuración en lo:

  1. $ knock -v localhost 7000 8000 9000
  2. hitting tcp 127.0.0.1:7000
  3. hitting tcp 127.0.0.1:8000
  4. hitting tcp 127.0.0.1:9000

# knockd -v -i lo
listening on lo...
127.0.0.1: opencloseSSH: Stage 1
127.0.0.1: opencloseSSH: Stage 2
127.0.0.1: opencloseSSH: Stage 3
127.0.0.1: opencloseSSH: OPEN SESAME
opencloseSSH: running command: /sbin/iptables -I INPUT -s 127.0.0.1 -p tcp --dport 22 -j ACCEPT
127.0.0.1: opencloseSSH: command timeout
opencloseSSH: running command: /sbin/iptables -D INPUT -s 127.0.0.1 -p tcp  --dport 22 -j ACCEPT

Por último es interesante comentar el parámetro One_Time_Sequences del fichero /etc/knockd.conf, mediante el cual se puede definir un fichero específico con una lista de secuencias a utilizar. De esta forma, después de un port-knocking correcto, el servidor selecciona la siguiente secuencia de puertos como la correcta, implementando así un sistema de contraseñas de un solo uso que incrementa la seguridad evitando riesgos en casos de la interceptación de la secuencia por medio de un sniffer.

3.- Implementar port-knocking de paquete único (SPA) con fwknop

fwknop (FireWall KNock OPerator) es una herramienta más avanzada que knockd, y utiliza la técnica de port-knocking de paquete único (SPA) frente al envío de secuencias de paquetes que ya hemos visto. En este caso el cliente y el servidor de port-knocking van en paquetes separados.

$ aptitude install fwknop-server fwknop-client

Durante la instalación del servidor nos pide una serie de datos de configuración, como la interfaz de red donde debe escuchar fknopd en modo promiscuo y la clave para cifrado y descifrado del paquete SPA recibido. Además nos consulta por defecto si queremos proteger el puerto ssh, realizando la configuración necesaria. A través de los ficheros de configuración también podemos establecer estos parámetros, como veremos a continuación.

El fichero /etc/fwknop/fwknop.conf contiene una extensa relación de parámetros sobre el funcionamiento de fwknop, principalmente relacionados con el cortafuegos. De este fichero editamos el interfaz de red donde escucha fwknopd (en nuestro caso lo) y si nos parece oportuno el puerto hacia el que se envía el paquete SPA (por defecto UDP 62201).

#/etc/fwknop/fwknop.conf
### Define the filter used for PCAP modes; we default to udp port 62201.
### However, if an fwknop client uses the --rand-port option to send the
### SPA packet over a random port, then this variable should be updated to
### something like "udp dst portrange 10000-65535";
PCAP_FILTER                 udp port 62201;
### Define the ethernet interface on which we will sniff packets.  Note
### that this is only used if the AUTH_MODE keyword above is set to
### "PCAP"
PCAP_INTF                   lo;

El otro fichero de configuración que debemos modificar es /etc/fwknop/access.conf, que contiene las directivas de control del cortafuegos:

#/etc/fwknop/access.conf 
#This file defines how fwknop will modify firewall access controls for specific IPs/networks. 
#default Single Packet Authorization (SPA) via libpcap:
SOURCE: ANY;
OPEN_PORTS: tcp/22;   ### for ssh (change for access to other services)
KEY: 12345678;
FW_ACCESS_TIMEOUT: 30;
### if you want to use GnuPG keys (recommended) then define the following variables
#GPG_HOME_DIR: /root/.gnupg;
#GPG_DECRYPT_ID: ABCD1234;
#GPG_DECRYPT_PW: myGpgPassword;
#GPG_REMOTE_ID: 1234ABCD;

Los parámetros susceptibles de modificar son los siguientes:

  • OPEN_PORTS, que define el puerto y el tipo de conexión a proteger a través de port-knocking.
  • FW_ACCESS_TIMEOUT, que define el tiempo máximo establecido desde la recepción del paquete correcto hasta que se produce la conexión ssh.
  • KEY, que define la contraseña de cifrado y descifrado del paquete SPA recibido (por defecto fwknop usa el algoritmo Rijndael).

En este ejemplo vamos a utilizar cifrado simétrico, pero es interesante mencionar que fwknop soporta el uso de criptografía de clave pública (de hecho es la opción recomendada por la documentación oficial) de forma similar a SSH. Una vez generado el par de claves con gpg, especificamos los datos necesarios en el fichero anterior, de acuerdo con los parámetros GPG_HOME_DIR, GPG_DECRYPT_ID, GPG_DECRYPT_PW y GPG_REMOTE_ID. Por otra parte, el cliente fwknop (cuyo uso veremos más adelante) debería ejecutarse añadiendo las opciones --gpg-recip y --gpg-sign.

Reiniciamos el servidor fwknopd y comprobamos qué procesos están ejecutándose:

  1. # /etc/init.d/fwknop-server restart
  2. Stopping FireWall KNock OPerator: knopwatchd knoptm fwknopd.
  3. Starting FireWall KNock OPerator: fwknopd .

# ps ax | grep fwknop
 5448 ?        Ss     0:00 /usr/bin/perl -w /usr/sbin/fwknopd
 5455 ?        Ss     0:00 /usr/bin/perl -w /usr/sbin/knoptm -i lo -c /etc/fwknop/fwknop.conf
 5457 ?        Ss     0:00 /usr/sbin/knopwatchd -c /etc/fwknop/fwknop.conf

Además del servidor fwknopd, vemos los procesos correspondientes a knoptm (daemon responsable de eliminar reglas de iptables) y a kfnopwatchd (daemon que monitoriza el buen funcionamiento de fwknop).

Para monitorizar el servidor fwknop y ver los envíos de paquetes SPA se puede consultar el syslog del sistema.

# tail -f /var/log/syslog | grep fwknop

Una vez en funcionamiento el servidor fwknopd, acudimos al cliente (fwknop) para enviar un paquete SPA a localhost y comprobar su funcionamiento.

$ fwknop -A tcp/22 -a localhost -D 127.0.0.1
.
[+] Starting fwknop client (SPA mode)...
[+] Enter an encryption key. This key must match a key in the file
    /etc/fwknop/access.conf on the remote system.
.
Encryption Key: 
.
[+] Building encrypted Single Packet Authorization (SPA) message...
[+] Packet fields:
. 
        Random data:    3315739143445257
        Username:       gonav
        Timestamp:      1326410237
        Version:        1.9.12
        Type:           1   (access mode)
        Access:         127.0.0.1,tcp/22
        SHA256 digest:  0FPWpVauS6j3EZLt4UMRhre8hW0BWn5ZmkF3jkFGe0s
. 
[+] Sending 182 byte message to 127.0.0.1 over udp/62201...

Como podemos ver, hemos enviado desde nuestra ip de origen (-a localhost) un paquete SPA al puerto udp/62201 de la ip donde escucha el servidor fwknopd (-D 127.0.0.1). Este paquete tiene un tamaño de 182 bytes y ha sido cifrado previamente a su envío con una clave simétrica que nos ha sido solicitada (Encryption Key: 12345678) y que debe coincidir con la definida en el fichero /etc/fwknop/access.conf.

Ahora comprobamos qué ha recibido el servidor, consultando el fichero de log.

# tail -f /var/log/syslog | grep fwknop
Jan 13 00:17:18 debian-desktop fwknopd: received valid Rijndael encrypted packet from: 127.0.0.1, remote user: gonav, client version: 1.9.12 (SOURCE line num: 26)
Jan 13 00:17:18 debian-desktop fwknopd: add FWKNOP_INPUT 127.0.0.1 -> 0.0.0.0/0(tcp/22) ACCEPT rule 30 sec
Jan 13 00:17:49 debian-desktop fwknop(knoptm): removed iptables FWKNOP_INPUT ACCEPT rule for 127.0.0.1 -> 0.0.0.0/0(tcp/22), 30 sec timeout exceeded

fwknopd ha recibido correctamente el paquete SPA, lo ha descifrado y lo ha reconocido como válido, añadiendo a iptables una nueva regla que permite conexiones al puerto tcp/22 durante 30 segundos. Al transcurrir este tiempo, la regla es eliminada a través de fnoptm y regresamos a la situación inicial.

Por último, para el que quiera profundizar en los aspectos más criptográficos de la herramienta, solamente decir que fwknop construye el paquete SPA concatenando los datos que muestra el cliente (Packet fields), codificando en Base64 y cifrando con el algoritmo Rijndael, dando lugar a los 182 bytes finales. Para más información consultar el código fuente de fwknop y fwknopd (scripts en perl), los módulos MIME::Base64, Crypt::CBC y Crypt::Rijndael (también disponibles vía comando perldoc), y la documentación oficial sobre SPA que describe con detalle la estructura del paquete envíado.

4.- Conclusiones

Tradicionalmente las técnicas de seguridad por oscuridad (como es el port-knocking) han tenido muy mala fama, no sin razones. Esta capa de invisibilidad proporciona una falsa seguridad que funciona hasta que el atacante descubre la forma de "acceder" a la puerta. A partir de este momento esta capa desaparece y el servicio queda protegido exclusivamente con la seguridad subyacente que existiese bajo la capa del port-knocking. Por lo tanto, se recomienda encarecidamente usar este tipo de técnicas como capa adicional y nunca como único sistema de seguridad.

Otro problema que se le atribuye al port-knocking es su tremenda vulnerabilidad frente a ataques MitM. En el caso de knockd la captura de la secuencia de puertos es inmediata, hecho que ha sido subsanado en fwknop mediante el uso de paquetes cifrados. Además, el uso de este único paquete SPA elimina los errores en la recepción de la secuencia de puertos, problema muy criticado en el caso de knockd.

También existe un riesgo importante de no disponibilidad. En un sistema con el cortafuegos cerrando por defecto los puertos de los servicios, un fallo del servidor de port-knocking bloquea totalmente el acceso a cualquier servicio al ser incapaz de activar nuevas reglas de iptables. Para evitar este problema es fundamental el uso de una herramienta como monit para monitorizar constantemente el servidor de port-knocking y reiniciar el servicio en caso de estar caído.

Sobre las ventajas e inconvenientes de la seguridad por oscuridad, y del port-knocking en particular, se habló largo y tendido en el ya citado debate del artículo Seguridad por oscuridad, al cual remito para completar información.

Existen versiones de los clientes knockd y fwknop para el sistema de los chicos de Redmond, lo cual facilita su utilización desde esta plataforma. En sistemas Linux, el cliente knockd puede ser sustituido por herramientas como telnet, netcat, hping, sendip o packit, capaces de enviar secuencias de puertos.

Por último, espero que este artículo os haya ilustrado suficientemente el funcionamiento básico del port-knocking, así como las funcionalidades, ventajas e inconvenientes de las herramientas knockd y fwknop.

Quiero terminar dando las gracias a admin por ofrecerme este espacio para la difusión del artículo, sometiendo el mismo a la consideración de tan ilustre foro. Agradezco y espero también vuestras correcciones, críticas, sugerencias, preguntas y opiniones.

Un saludo cordial,
Domingo González