Seguridad avanzada en Linux: Port Knocking

Una de las principales vulnerabilidades en un sistema es el hecho de tener que dejar abiertos los puertos cuyos servicios desea proporcionar. Si bien servicios como el http o el smtp deben permanecer abiertos para ser funcionales, es posible hacer que los servicios neurálgicos de nuestro sistema solo sean accesibles cuando verdaderamente son necesarios.

– ¿A qué nos referimos? A la posibilidad de ofrecer un servicio sobre un puerto cerrado.
– ¿Mande? Dime qué has fumado que yo también quiero…
– No he fumado nada incrédulo, hablo de la estrategia del port knocking.
– Em, ¿me lo explica?
– Desde luego.

El port knocking es una estrategia que permite a un usuario solicitar la apertura de un puerto para disponer de un servicio inicialmente cerrado. Esta solicitud tiene la forma de secuencia de paquetes de autenticación enviados a puertos cerrados. Es posible enviar información a través de puertos cerrados y en ausencia de otro servicio de red, ya que nuestro cortafuegos y otras utilidades como tcpdump pueden configurarse para observar todos los paquetes que llegan sin importar si luego serán entregados a un servicio. La gran potencia del port knocking radica en el hecho de trabajar siempre con puertos cerrados, lo cuál no deja resquicios de seguridad.

Una forma de limitar los usuarios entrantes es por filtrado de IP, pero esta ténica tiene muchas debilidades: puertos abiertos, suplantación física, imposibilidad de movilidad de los usuarios… Port Knocking resuelve todos estos problemas y además aporta más seguridad.

El port knocking fue descrito por primera vez en la revista SysAdmin. Existen diversas formas de implementarlo, una bastante popular es doorman, aunque aquí haremos una implementación muy sencilla basada en tcpdump y en tan solo 4 pasos.

Paso 1: cerrar todo. Lo primero es usar una configuración muy restrictiva en nuestro iptables: lo cerramos todo. Un ejemplo simple sería:

#!/bin/sh
#firewall.sh
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -s 0/0 -d 0/0 -p udp -j DROP
iptables -A INPUT -s 0/0 -d 0/0 -p tcp --syn -j DROP

Cabe destacar el uso de DROP, nunca de REJECT en nuestra política de rechazo ya que debe producirse una denegación silenciosa del servicio que no provoque respuesta al supuesto intruso. Esta configuración no permite nuevas conexiones sobre puertos cerrados pero sí permite continuar aquellas iniciadas.

Paso 2: apertura de puerto. Diseñamos un script que durante abra durante 10 segundos por ejemplo el puerto solicitado si recibe un paquete cuyo «santo y seña» es correcto.

#!/bin/bash
# guard.sh
# acepta los paquetes entrantes
/sbin/iptables -I INPUT -j ACCEPT -i eth0 -p tcp -s $1 --dport $2
sleep 10
# rechaza los paquetes entrantes
/sbin/iptables -D INPUT -j ACCEPT -i eth0 -p tcp -s $1 --dport $2

A los 10 segundos el puerto se cierra, pero nosotros ya hemos iniciado la conexión (SSH, FTP, etc) y el firewall permite continuar conexiones iniciadas. ¡Estamos trabajando sobre un puerto cerrado!

Paso 3: escucha activa. El port knocking se asemeja a las famosos santo y seña de las películas de espionaje donde abrimos una puerta llamando con «2 toques largos, 3 cortos y uno largo». En nuestro caso la contraseña correcta podría ser que el paquete recibido tuviera una cabecera de valor 17. Debemos diseñar un script que permanezca a la escucha e identifique la llamada correcta. Cuando ésta se produzca lanzará guard.sh lo que abrirá el puerto durante 10 segundos, tiempo suficiente para entrar.

tcpdump -l -O "tcp[2:2]>=1000 and tcp[2:2]<=10999 and tcp[4:4]=17 and tcp[tcpflags] & tcp-syn!=0" | sed -u 's/.*IP \([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\).*\.10*\([0-9]*\): S.*/\1 \2/' | xargs -t -l -i bash -c "/root/guard.sh {} &"

tcpdump no permite especificar intervalos de puertos, cosa que suplimos con la sintaxis tcp[2:2] que indica un campo de 2 bytes en la cabecera TCP a partir del byte 2 (tcp[inicio:longitud]). Nuestra estrategia de implementación en esta ocasión consiste en eliminar la cabecera de 2 bytes y abrir el puerto resultante. Así por ejemplo con 1022 abriríamos el puerto 22 (SSH).

Paso 4: llamada. Nuestro sistema permanece atento, vamos a hacer la llamada correcta:

# Sintaxis:
# sendip ip_origen puerto_origen ip_destino puerto_destino
# 1000 < puerto_destino <=10999 sendip -p ipv4 -p tcp -is $1 -ts $2 -td $3 -tfs 1 -tn 17 $4

La contraseña 17 es correcta y solicitamos la apertura del puerto para nuestra IP.

Con estos sencillos pasos conseguimos que servicios tan útiles como el FTP, de los cuáles los administradores siempre son recelosos, puedan ofrecerse con riesgo nulo. El ataque de un intruso es prácticamente imposible.

Si deseas profundizar en el uso de port knocking recomiendo que te documentes sobre Doorman, de Bruce Ward, que se caracteriza por su original y elegante enfoque del port knocking.

Hasta la próxima entrega.

Comentarios

Comments are closed.