Bastionando SSH II

dg0Hoy en día existen multitud de máquinas que funcionan con un sistema operativo derivado de Unix como puede ser BSD, MacOS, HP-UX, Solaris, Linux (y derivados de ellos). Aparte de los sistemas mencionados, existe una gran cantidad de dispositivos que funcionan bajo un sistema Linux embebido, como por ejemplo un router/AP Wifi. Una de las principales ventajas que siempre he encontrado en todos los sistemas mencionados es que normalmente siempre tienen accesible una consola de administración remota, lo que comúnmente se ha llamado “un ssh”.

Puesto que este servicio da acceso a la máquina y “potencialmente” a las entrañas del sistema, es necesario darle un repaso a su configuración y “perder” tiempo en bastionarlo adecuadamente para evitar sustos.

Normalmente se hace más hincapié en bastionar servidores, pero no hay que olvidar que un router con el servicio ssh no bastionado y publicado al mundo exterior puede ser el punto de entrada a nuestra organización.

Temporalmente voy a publicar el servicio ssh en el puerto por defecto (22/tcp) y tan solo hay que esperar un par de horas para empezar a recibir escaneos de bots intentando conectarse al servicio:

dg1

Como se puede observar, intentan conectarse con usuarios por defecto (root, admin, guest… e incluso intentan con usuarios por defecto de sistemas de la marca Ubiquiti: ubnt). Por ello, una de las primeras recomendaciones es cambiar la contraseña por defecto de estos usuarios y en caso de que se pueda también el nombre.

Un antiguo compañero, Joel Sevilleja, publicó un post relacionado con este, del cual recomiendo su lectura íntegramente (https://www.securityartwork.es/2013/10/23/bastionado-de-servidores-ssh/). Para no repetir recomendaciones, mostraré a continuación puntos en los que creo que es necesario hacer hincapié o que no estaban reflejados en su artículo.

Entre las recomendaciones encontramos tanto medidas a aplicar en el fichero de configuración del servicio, en el sistema de ficheros y por último reglas dentro del firewall (iptables).

NOTAS: 

  • El artículo se centra en la configuración de OpenSSH así como la implementación de reglas en el firewall para protegerlo. El fichero de configuración por defecto se encuentra en la ruta “/etc/ssh/sshd_config”.
  • Las reglas expuestas de IPTables están especificadas a modo de prueba, por lo que se deberán ajustar a cada entorno. En las pruebas, el servidor SSH se encuentra dentro de la misma máquina que el firewall. El orden de las reglas del firewall no es irrelevante, se deberá estudiar el resto de las mismas para establecer el orden adecuado.
  • Para algunas de las reglas del firewall, necesitamos tener cargados los siguientes módulos del kernel: xt_connlimit y xt_conntrack. (comprobar con lsmod y cargar con modprobe + nombre_modulo).

1.- Acceso únicamente mediante clave pública/privada

Esta medida es prioritaria y necesaria, ya que incrementamos sustancialmente la seguridad de nuestro sistema. Únicamente podremos acceder al servicio si disponemos de una pareja de clave pública/privada autorizada. No permitimos acceder únicamente por contraseña.

El primer paso será crear una pareja de claves pública/privada para cada usuario que se conecte al sistema. No es una buena práctica compartir la pareja de claves, al igual que no compartes la contraseña “tradicional”.

ssh-keygen -C “$(whoami)@$(hostname)-$(date -I)” -t rsa -b 4096

dg2

De esta manera, creamos la pareja de claves pública/privada, añadiendo un comentario a las mismas especificando el usuario activo, host y fecha. Se ha elegido como algoritmo para generar la pareja de claves RSA por compatibilidad con cualquier dispositivo/software. Existen algoritmos basados en curva elíptica  más modernos y livianos (computacionalmente hablando) con una menor longitud de clave y garantizando la misma fortaleza, pero pueden existir problemas de compatibilidad.

A la hora de generar la pareja de claves, podremos especificar una clave para desbloquear la clave privada antes de enviarla al servidor. Con ello estamos incluyendo una capa más de fortaleza ya que en el caso de que nos roben la clave privada no podrán conectarse a priori al servidor puesto que desconocen esta clave de desbloqueo. Para establecerla simplemente la insertamos a la hora de crear la pareja de claves.

Una vez generadas, el siguiente paso es insertar la clave pública del usuario en el fichero “authorized_keys” del usuario dentro del servidor. Normalmente se encuentra ubicado en ~/.ssh/authorized_keys.

Con el siguiente comando insertamos la clave pública directamente en el fichero indicado.

cat ~/.ssh/archlinux.pub | ssh -p2468 david@192.168.1.25 "mkdir -p ~/.ssh && cat >>  ~/.ssh/authorized_keys"

Comprobamos que se ha realizado correctamente:

dg3

Por último, debemos modificar un par de opciones en el fichero de configuración del servidor (/etc/ssh/sshd_config) para activar la autenticación mediante clave pública/privada y deshabilitar la autenticación por contraseña. Se recomienda no deshabilitar esta última hasta que no se haya comprobado que se accede correctamente.

dg4

Guardamos los cambios en el fichero y reiniciamos el servicio

/etc/init.d/ssh restart

Para conectar, simplemente le indicamos con el parámetro -i el fichero con la clave privada del usuario.

dg5

Como podemos observar, el sistema nos ha solicitado una clave para desbloquear la clave privada ya que lo indiqué al crear la pareja de claves.En caso de que queramos modificar esta clave sin generar otra clave privada, podemos hacerlo con el siguiente comando:

$ ssh-keygen -f ~/.ssh/archlinux.key -p

2.- Establecer protocolo SSH v2.

 Únicamente debemos permitir el uso del protocolo 2 en nuestro servidor. El protocolo v1 tiene fallos de seguridad conocidos. Debemos cambiar la directiva en el fichero de configuración del servidor ssh.

3.- Limitar los usuarios/grupos que pueden conectarse al servidor SSH.

Mediante la opcion “AllowUsers” y “AllowGroups”, especificamos los usuarios o grupos autorizados a conectarse. De la misma manera, con las directivas  “DenyUsers” y “DenyGroups” especificamos usuarios o grupos a los que les prohibimos expresamente la entrada.

dg6

4.- Securizar el fichero Authorized_keys en el servidor.

Como medida adicional podemos prevenir que los usuarios añadan nuevas claves públicas a sus ficheros Authorized_keys, por ello les eliminamos el permiso de escritura.

$ chmod 400 ~/.ssh/authorized_keys

dg7

Para prevenir que el usuario vuelva a poner los permisos como estaban y en consecuencia añada o modifique el fichero, debemos establecer el bit de inmutable tanto sobre el fichero Authorized_keys como sobre el directorio .ssh del usuario.

Recordad que solo el usuario root puede añadir o quitar el bit de inmutable.

dg8

Si intentamos cambiar los permisos con el usuario, nos devolverá un error.

dg9

5.- Reducir el tiempo de SSH Login Grace Time.

Es el tiempo que el sistema mantiene activa la conexión de un usuario no autenticado. Por defecto se encuentra establecida a 2 minutos. La reducimos a 30 segundos para evitar tener conexiones abiertas no autenticadas.

LoginGraceTime 30

6.- Limitar el número de conexiones no autenticadas.

A la hora de realizar un ataque por fuerza bruta contra un servidor SSH, cuantas más conexiones simultáneas se permitan, mayor rapidez conseguimos en el ataque. Mediante la siguiente directiva, podemos limitar vía el propio demonio del servicio, el número de conexiones permitidas independientemente de la IP de origen.

#MaxStartups start:rate:full
MaxStartups 2:50:10
  • Permitimos solo 2 conexiones no autenticadas antes de que empecemos a “dropear” conexiones nuevas.
  • Permitimos como máximo 10 conexiones no autenticadas, a partir de ahí, descartamos cualquier conexión.
  • En el intervalo entre 2 y 10 conexiones, tenemos una tasa aleatoria de “dropeo” del 50% (rate/100)

Lógicamente debemos variar estos parámetros en función del número de usuarios que tengamos y el uso que le demos al servicio. En nuestro caso, a partir de 2 conexiones, empezará a rechazar conexiones nuevas.

dg10

7.- Limitar las conexiones solamente desde una o varias IP de origen.

iptables -A INPUT -p tcp -s IP_ORIGEN --dport 2468 -j ACCEPT

Con esto solo permitimos las conexiones al servicio SSH (puerto 2468) desde la IP_ORIGEN que hayamos establecido. Recordad que para que esta regla sea útil, debemos tener la política por defecto de la cadena INPUT en la tabla filter a DROP o en su defecto una regla posterior a esta que deniegue cualquier paquete que no haya cumplido las reglas anteriores.

8.- Limitar el número de conexiones desde una misma IP.

iptables -A INPUT -p tcp --syn --dport 2468 -m connlimit --connlimit-above 2 -j REJECT

Con esta regla solo permitimos como máximo 2 conexiones no autenticadas desde la misma IP de origen. A partir de la 2 empezará a denegar el resto de conexiones que establezcamos.

dg11

9.- Protección ante ataques DoS mediante Iptables.

iptables -N LOGDROP
iptables -A LOGDROP -j LOG --log-level 4 --log-prefix "DoS SSH - "
iptables -A LOGDROP -j DROP
iptables -A INPUT -p tcp --dport 2468 -m state --state NEW -m recent --set
iptables -A INPUT -p tcp --dport 2468 -m state --state NEW -m recent --update --seconds 60 --hitcount 2 -j LOGDROP

Mediante las reglas anteriores estamos añadiendo una capa más de protección a nuestro servicio, de manera que solo permitimos un máximo de 2 conexiones nuevas desde la misma IP origen en un intervalo de 60 seg. Por lo tanto debemos esperar 60 seg sin volver a enviar petición de conexión para que el sistema no nos bloquee. Aparte, escribimos en el log del sistema la información de los paquetes descartados.

dg12

En el log (/var/log/messages) podemos ver las trazas escritas por iptables.

dg13

10.- Limitar el número de conexiones globales al servicio por unidad de tiempo.

Otra medida de protección que podemos implantar es limitar el número de conexiones independientemente de la IP de origen por segundo/min/hora/dia al servicio.

iptables -A INPUT -m tcp -p tcp --dport 2468 -m state --state NEW -m limit --limit 1/m --limit-burst 2 -j ACCEPT

dg14

Con el módulo –limit se especifica una media, no un caudal.  Por ejemplo: decir 3/minuto puede significar que en el primer minuto se reciben 3 paquetes y en el segundo también 3 o puede significar que en el primer minuto se reciben 5 y en el segundo 1… la media sigue siendo 3/minuto.

El parámetro –limit-bust especifica un máximo, si no se especifica un valor por defecto vale 5 y significa que se aceptaran los primeros 5 paquetes y luego se esperará hasta alcanzar la media para seguir aceptando.

En nuestro ejemplo, especificamos un –-limit de 1/minuto y un –limit-burst de 2: se aceptarán los primeros 2 paquetes independientemente de cuando lleguen y luego se dejara de aceptar hasta alcanzar la media, luego se volverá a permitir un máximo de 2, y así sucesivamente.

11.- Acceso condicional al servicio SSH

Otra de las posibilidades que nos brinda es el acceso condicionado y personalizado en función a ciertos parámetros mediante la directiva “Match”. De esta manera tenemos bloques configuración de directivas asociadas a ciertas “entidades”:

  • User – Usuarios separados por comas. Por ej. “Manuel, David, Javier”
  • Group – Nombre de grupo local. Por ej. “Seguridad”
  • Host – Nombre de dominio o FQDN. Por ej. “*.dominiossh.com”
  • Address – Una IP o rango de IP de origen. Por ej. 192.168.1.0/24
  • Port – Puerto destino. Por ej. 22
  • LocalAddress – Dirección local del servicio.

Todos los parámetros que se escriban debajo de la directiva Match aplicaran y tendrán prioridad sobre las de por defecto para los usuarios/grupos/hosts/direcciones/puertos que hayamos especificado. Es necesario establecer siempre las directivas Match al final del fichero de configuración.

Por ejemplo:

# Allow administrators to use the agent & X11 forwarding
Match Group admin
AllowAgentForwarding yes
X11Forwarding yes
# Regular users cannot forward TCP sessions and cannot use a password
Match Group users
AllowTcpForwarding no
PasswordAuthentication no
Banner banner.users
# Allow a few IP addresses and one hostname to login with a password, as root
Match Address 10.20.30.40,80.90.100.200 Host dispatch.raymii.org
PasswordAuthentication Yes
PermitRootLogin yes

La directiva Match nos permite unas opciones de configuración y restricción casi infinitas. Para ver el listado completo consultar la siguiente referencia: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/sshd_config.5?query=sshd_config&sec=5

12.- Monitorizar.

Por último, mediante un par de comandos podemos visualizar los nombres de usuario más atacados, así como las 5 IP más atacantes:

awk 'gsub(".*sshd.*Failed password for (invalid user )?", "") {print $1}' /var/log/auth* | sort | uniq -c | sort -rn | head -5
awk 'gsub(".*sshd.*Failed password for (invalid user )?", "") {print $3}' /var/log/auth* | sort | uniq -c | sort -rn | head -5

dg15

A modo de resumen…

  • Deshabilitar el acceso root al SSH.
  • Usar únicamente autenticación basada en clave pública/privada usando clave de desbloqueo.
  • Deshabilitar autenticación por password.
  • Deshabilitar el protocolo v1.
  • Cambiar el puerto por defecto del servidor (protección anti bots).
  • Permitir solo el acceso a ciertos usuarios/grupos.
  • Reducir el “Login Grace Time”.
  • Limitar los intentos para la autenticación: MaxAuthTries 3.
  • Limitar el número de conexiones no autenticadas (MaxStartups).
  • Restringir el acceso a conexiones desde ciertas IP (en caso de ser posible).
  • Monitorización mediante Fail2ban/Denyhost/sshguard para la prevención de ataques por fuerza bruta.
  • Establecer reglas en el firewall acorde con la organización.

Referencias:

Comments

  1. Como aclaración al post, he de decir que el entorno de pruebas ha sido el siguiente:
    Servidor OpenSSh en una distribución derivada de Debian.
    IPTables como firewall por su extensa difusión y compatibilidad.
    Cliente con distribución ArchLinux

  2. Y que el II hace referencia al ssh y no a una segunda parte del artículo xD

  3. Buenos días Moisés

    El II hace lo puse porque un antiguo compañero hizo un artículo con temática similar y este es una continuación y ampliación del mismo.