Lo hice y lo entendí

El blog de Vicente Navarro
20 oct

Port Knocking

Hace unas semanas leí por primera vez sobre el port knocking en “Security by Default: Una autenticación fuerte para Openvpn“. No conocía la idea y me ha parecido muy interesante y útil. Consiste en enviar una secuencia de paquetes a un sistema que active un servicio que anteriormente estaba cerrado.

Por ejemplo, cualquiera que tenga un sistema con un servidor SSH expuesto a Internet sabrá que no es nada infrecuente encontrar registrados diferentes intentos de intrusión en el sistema (en el /var/log/auth.log en un sistema Debian):

Oct 19 09:31:49 darkstar sshd[13481]: Invalid user fluffy from 202.102.XX.XX
Oct 19 09:32:02 darkstar sshd[13493]: Invalid user oracle from 202.102.XX.XX
Oct 19 09:32:08 darkstar sshd[13500]: Invalid user www from 202.102.XX.XX
Oct 19 09:32:17 darkstar sshd[13508]: Invalid user postmaster from 202.102.XX.XX
Oct 19 09:32:31 darkstar sshd[13520]: Invalid user file from 202.102.XX.XX

Si el servidor de SSH sólo permite autentificación por clave privada/pública (Autentificación trasparente por clave pública/privada con OpenSSH) podemos estar razonablemente tranquilos de que el intento de intrusión no tendrá éxito. Pero como nunca se puede estar seguro del todo, puede ser buena idea añadir, adicionalmente, un sistema de port knocking para abrir el puerto SSH sólo cuando realmente lo vayamos a usar.

En un sistema basado en Debian, esto se puede hacer muy fácilmente con el demonio knockd, uno de los paquetes oficiales de la distribución. Tras instalar el paquete, tenemos que editar el fichero /etc/default/knockd para permitir que el demonio arranque:

# PLEASE EDIT /etc/knockd.conf BEFORE ENABLING
START_KNOCKD=1

y especificarle cualquier opción que queramos usar, especialmente en qué interfaz tiene que escuchar si no es el "eth0":

# command line options
# KNOCKD_OPTS="-i eth1"

Hay que tener en cuenta que el knockd siempre ve los paquetes llegar, incluso si los puertos están cerrados y si alguna regla de iptables rechaza el paquete. Podríamos decir que se comporta como el tcpdump, en el sentido de que el interfaz se pone en modo promiscuo y ve todo lo que llega a él. Pero adicionalmente, según qué paquetes vea, ejecuta una acción. Por cierto, parece que no funciona poner dos interfaces con "-i eth0 -i eth1".

A continuación, tenemos que personalizar el fichero /etc/knockd.conf para especificar qué secuencia de puertos queremos y qué hacer cuando ésta llegue. En el “man knockd” encontramos interesantes ejemplos de uso, pero podemos centrarnos en el más típico que es el que instala Debian por defecto:

[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

Vemos que cuando llega una secuencia de paquetes TCP con el flag de SYN a los puertos 7000, 8000 y 9000 en menos de 5 segundos, el demonio knockd añade una regla de iptables para permitir el acceso al servicio SSH a la IP que ha hecho el port knocking. Y con otra secuencia, podemos cerrar el puerto posteriormente.

Tras configurar el /etc/knockd.conf a nuestro gusto, arrancamos el knockd con “/etc/init.d/knockd start“.

Para mandar la secuencia que active la acción podemos usar, desde otro sistema, el comando knock, que se instala también al instalar el paquete knockd:

[root@clientessh ~]# knock 192.168.100.2 7000 8000 9000

y en el sistema en el que hemos arrancado el knockd vemos cómo los paquetes llegan y se ejecuta la acción planificada para dicha secuencia:

Oct 19 21:55:17 darkstar knockd: 192.168.100.1: openSSH: Stage 1
Oct 19 21:55:17 darkstar knockd: 192.168.100.1: openSSH: Stage 2
Oct 19 21:55:17 darkstar knockd: 192.168.100.1: openSSH: Stage 3
Oct 19 21:55:17 darkstar knockd: 192.168.100.1: openSSH: OPEN SESAME
Oct 19 21:55:17 darkstar knockd: openSSH: running command: /sbin/iptables -A INPUT -s 192.168.100.1    -p tcp --dport 22 -j ACCEPT

El hecho de que podamos ejecutar cualquier comando nos da total flexibilidad. Por ejemplo, se me ocurre que en vez de, o adicionalmente a añadir o quitar reglas de iptables, también podríamos incluso arrancar o parar el demonio sshd:

[openSSH]
        sequence    = 7000,8000,9000
        seq_timeout = 5
        command     = /etc/init.d/ssh start
        tcpflags    = syn

[closeSSH]
        sequence    = 9000,8000,7000
        seq_timeout = 5
        command     = /etc/init.d/ssh stop
        tcpflags    = syn

Lo que nos quedaría por ver es qué alternativas tenemos para mandar la secuencia de puertos. Hemos visto que el propio paquete knockd de Debian lleva el comando knock. Pero, ¿hay alternativas? ¿y si queremos hacer port knocking desde Windows? ¿y si queremos hacerlo desde un sistema que no tenga ningún software especial?

La propia página de man de knockd nos sugiere algunas alternativas:

knock is the accompanying port-knock client, though telnet or netcat could be used for simple TCP knocks instead. For more advanced knocks, see hping, sendip or packit.

Una posibilidad para Windows también es el KnockKnock, descubierto gracias a “Add Port Knocking to SSH for Extra Security“:

pero un inconveniente del KnockKnock es que lanza la secuencia más de una vez:

Oct 19 22:38:32 darkstar knockd: 192.168.100.1: Prueba: Stage 1
Oct 19 22:38:32 darkstar knockd: 192.168.100.1: Prueba: Stage 2
Oct 19 22:38:32 darkstar knockd: 192.168.100.1: Prueba: Stage 3
Oct 19 22:38:32 darkstar knockd: 192.168.100.1: Prueba: Stage 4
Oct 19 22:38:32 darkstar knockd: 192.168.100.1: Prueba: OPEN SESAME
Oct 19 22:38:32 darkstar knockd: Prueba: running command: /sbin/iptables -A INPUT -s 192.168.100.1    -p tcp --dport 22 -j ACCEPT
Oct 19 22:38:33 darkstar knockd: 192.168.100.1: Prueba: Stage 1
Oct 19 22:38:33 darkstar knockd: 192.168.100.1: Prueba: Stage 2
Oct 19 22:38:33 darkstar knockd: 192.168.100.1: Prueba: Stage 3
Oct 19 22:38:33 darkstar knockd: 192.168.100.1: Prueba: Stage 4
Oct 19 22:38:33 darkstar knockd: 192.168.100.1: Prueba: OPEN SESAME
Oct 19 22:38:33 darkstar knockd: Prueba: running command: /sbin/iptables -A INPUT -s 192.168.100.1    -p tcp --dport 22 -j ACCEPT
Oct 19 22:38:33 darkstar knockd: 192.168.100.1: Prueba: Stage 1
Oct 19 22:38:34 darkstar knockd: 192.168.100.1: Prueba: Stage 2
Oct 19 22:38:34 darkstar knockd: 192.168.100.1: Prueba: Stage 3
Oct 19 22:38:34 darkstar knockd: 192.168.100.1: Prueba: Stage 4
Oct 19 22:38:34 darkstar knockd: 192.168.100.1: Prueba: OPEN SESAME
Oct 19 22:38:34 darkstar knockd: Prueba: running command: /sbin/iptables -A INPUT -s 192.168.100.1    -p tcp --dport 22 -j ACCEPT

Si el comando a ejecutar se puede ejecutar más de una vez sin otros efectos, como es el caso del iptables, nos da igual, pero en otros casos, puede ser un problema.

Otra solución es usar el cliente de telnet de Windows contra los diferentes puertos para activar la secuencia. Para ello, en Windows se puede usar un .bat con este aspecto:

start telnet 192.168.100.2 7000
start telnet 192.168.100.2 8000
start telnet 192.168.100.2 9000

El resultado no siempre es el deseado. El telnet de Windows envía varios paquetes SYN cuando falla el primero y parece que el knockd no siempre acepta la secuencia. Pero bueno, normalmente antes de 4 o 5 intentos, ya ha funcionado la secuencia.

Por supuesto, el knockd es un punto de fallo adicional en el acceso al sistema. Si le pasa algo a este demonio, nos quedaremos sin poder acceder.

Actualización 21/10/11: Como comentan Dany y SebaMinguez en los comentarios, también podemos configurar port knocking sólo con reglas iptables: Multiple-port knocking Netfilter/IPtables only implementation.

:wq

Entradas relacionadas

12 Comentarios a “Port Knocking”

  • SebaMinguez dice:

    Hola!

    Siempre con contenido tan interesante, muchas gracias.

    En este caso el truquito ya estaba en mi galera y me permito aportar algun detalle.

    Por un lado, tener en cuenta que es mejor que los puertos no esten en orden ascendente o descendente para evitar que un escaneo de puertos caiga en la regla.
    No conozco este demonio y si es inteligente como para detectar el escaneo. Igualmente es mejor usar un puerto bajo, uno alto y uno en medio.

    esto mismo se puede implementar con iptables (que es como lo uso). Si interesa paso el tuto, ahora estoy en el mobil.

    Abrazo y gracias por post tan interesantes

  • dani dice:

    Creo que hay un error en la linea de iptables en el apartado [closessh] no seria un ACCEPT, es un DROP o REJECT.

    Otro problema que veo, es levantar/parar el demonio ssh.
    Estas abriendo una ventana de ataque por fuerza bruta mientras dure tu sesión. Siempre y cuando te acuerdes de activar el proceso de cerrado, sino se quedaria abierta, desactivando toda la “seguridad” que ofrece el knocking.

    Como dice SebaMinguez, con iptables se puede hacer, evitando tener otro demonio corriendo en el sistema.
    http://www.debian-administration.org/articles/268

    Saludos,
    dani

    • Hola Dani

      Respecto al iptables, fíjate bien en que la regla para abrir es:

      -A INPUT

      y para cerrar es:

      -D INPUT

      Es decir, primero añadimos la regla y luego la eliminamos. Acostumbrado al ACCEPT y al DROP/REJECT, yo también he tenido que mirarlo un par de veces antes de darme cuenta.

      Respecto a arrancar o parar el sshd, lo he puesto sólo como un ejemplo de la flexibilidad que tenemos con la configuración. Yo lo combinaría con la regla de iptables. Por eso he puesto intencionadamente “Por ejemplo, se me ocurre que en vez de, o adicionalmente a…”.

      Finalmente, muchas gracias por el enlace a Multiple-port knocking Netfilter/IPtables only implementation, lo añado a la entrada. como actualización Me ha gustado mucho la idea, aunque me parece que un demonio como el knockd te puede dar más flexibilidad, ya que puedes ejecutar cualquier script (como /etc/init.d/ssh). Que yo sepa, no se puede ejecutar un script desde iptables cuando detectamos algún paquete en concreto, ¿no?

  • Guty dice:

    Un pequeño aporte, para poder utilizar dos interfaces lo que hice es poner “-i eth0 eth1″ , acabo de probarlo y funciona perfecto.
    Saludos.

    • Hola Guty

      Gracias por el consejo, pero a mí tampoco me funciona eso. Si pongo "-i eth 0 eth1", en el syslog sigo viendo que sólo escucha en eth0:

      knockd: starting up, listening on eth0
  • Osqui dice:

    ¡Qué bueno volverte a ver por aquí! ¡Se te echaba de menos!
    Y siempre con estos artículos tan deliciosos…lástima que este programa no esté en los repositorios de Fedora. ¡Mecachis!

    (aunque sí que hay un paquete .rpm oficial en http://www.zeroflux.org/projects/knock …qué cosas)

Trackbacks y pingbacks:

Tema LHYLE09, creado por Vicente Navarro