Estas aquiContenido / DenyHosts, una magnífica herramienta

DenyHosts, una magnífica herramienta


PorFernando Acero- Publicado el16 Septiembre 2008

Por Fernando Acero

Desde hace tiempo soy un feliz usuario de DenyHosts. Este programa Python, lee un archivo de logs del sistema como "messages" y detecta los intentos de acceso ilegales al demonio SSH, ya sea mediante ataques de diccionario, o mediante fuerza bruta, tomando las acciones necesarias para impedirlos.

La configuración es muy sencilla e implica modificaciones en los archivos de configuración de DenyHost, mediante la edición del archivo /usr/share/denyhosts/denyhosts.py, y del demonio SSH, sobre todo, para especificar un nivel adecuado de logs para SSH y para comprobar que nuestro servidor SSH está compilado con soporte para tcp_wrappers. Para no tener problemas durante la configuración del programa, también es conveniente empezar con unos archivos /etc/hosts.deny y /etc/hosts.allow vacíos. Yo tenía algunas configuraciones por omisión en los mismos, que eran incompatibles con DenyHosts, por lo que me costó un poco su configuración...

El funcionamiento del programa es sencillo. Cada cierto tiempo, programable por el usuario y que es conveniente que sea de unos 30 segundos o inferior, DenyHosts comprueba el contenido del archivo messages (o de cualquier otro archivo de logs del sistema que se le indique en su configuración) y si los intentos de acceso al servidor SSHD sobrepasan unos umbrales predefinidos por el usuario, DenyHosts introduce la IP atacante en el archivo /etc/hosts.deny del servidor. Con ello, ya no se podrá volver a intentar la conexión desde esa IP. El programa permite filtrar solamente los siguientes accesos al demonio SSHD, o a todos los servicios del servidor con soporte de tcp_wrappers mediante el uso del mandato "BLOCK_SERVICE = ALL" en la configuración de DenyHosts.

Si se configura el demonio SSHD, mediante el archivo /etc/ssh/sshd_config, para que solamente sea accesible mediante claves RSA, es factible configurar DenyHost para que reaccione al más mínimo intento de acceso ilegal al sistema. Por ejemplo podemos usar los siguientes mandatos:

RhostsRSAAuthentication yes
RSAAuthentication yes
PubkeyAuthentication yes
PermitEmptyPasswords no
PasswordAuthentication no

El proceso de configuración de las claves RSA en el servidor es muy sencillo. Basta utilizar los siguientes mandatos en nuestro ordenador cliente, usando para ello el usuario con el que accederemos posteriormente al servidor. Como es lógico, para que esto funcione también es necesario tener acceso remoto al servidor, ya que el último mandato de la secuencia envía nuestra clave pública al servidor:

$ ssh-keygen -t rsa
(intro a todo)
$ ssh-copy-id -i ~/.ssh/id_rsa.pub usuario@miservidor.com

DenyHosts permite definir distintos umbrales para los intentos de acceso realizados con usuarios válidos, inválidos, restringidos o root. Como es lógico, es conveniente configurar SSH para que no permita el acceso desde el usuario root usando el mandato "PermitRootLogin no" en /etc/ssh/sshd_config.

Si accedemos desde una IP fija, también es conveniente, para evitar problemas, introducir una línea en el archivo hosts.allow del servidor con el contenido "ALL : xxx.xxx.xxx.xxx : ALLOW", en las que xxx.xxx.xxx.xxx deberá ser sustituido por la IP del ordenador cliente. De esta forma nuestro sistema no será incluido nunca en la lista negra. Si tenemos IP variable, también cabe la posibilidad, aunque lógicamente es menos seguro, de indicar un rango de IP's válidas, que no podrán ser prohibidas por DenyHosts.

Además, DenyHost permite limpiar las IP's contenidas en el archivo /etc/hosts.deny si se cumplen determinadas condiciones programables por el usuario, por ejemplo si no se realizan más intentos de acceso desde esa IP en un determinado tiempo. Los tiempos para proceder a la limpieza se pueden configurar en base a si los intentos se hicieron con usuarios válidos, inválidos, restringidos o como root. Por ejemplo, podemos decir que las IP's que han sido introducidas en /etc/hosts.deny por usar un determinado número de veces un usuario restringido se mantengan 30 días, y 15 días solamente si se intentó el acceso con un usuario válido. También puede ser configurado para que una IP no pueda ser borrada de nuevo de /etc/hosts.deny si después de haber sido borrada se produce un nuevo intento de acceso ilegítimo desde la misma IP. Asimismo, el programa permite poner a cero el contador cuando se realiza un acceso con éxito desde una determinada IP.

DenyHosts tiene otras muchas cualidades interesantes, como la posibilidad de recibir información mediante correo electrónico de las IP's introducidas en /etc/hosts.deny, o la de poder enviar y recibir de un servidor las IP's que hemos capturado, o que han sido capturadas por otros usuarios. En la actualidad, el número de usuarios suscritos a este servicio supera los 27.000, lo que nos puede permitir adelantarnos a algunos atacantes demasiado "conocidos".

Una de las capacidades más interesantes de DenyHosts es la posibilidad de definir cadenas para identificar los ataques, lo que nos permite ampliar el uso de este programa a otros servicios, siempre que la información de estos servicios aparezca en el archivo de logs que hemos seleccionado (por ello es interesante usar messages) y seamos capaces de definir las cadenas que ha de identificar DenyHosts como ataques. Para configurarlo, se usan los mandatos "USERDEF_FAILED_ENTRY_REGEXn=" en el archivo /usr/share/denyhosts/denyhosts.conf. Por ejemplo, podemos usar estos mandatos para que DenyHosts identifique otras situaciones de ataque no contempladas inicialmente por el programa:

USERDEF_FAILED_ENTRY_REGEX1=Did not receive identification string from (?P.*)
USERDEF_FAILED_ENTRY_REGEX2=User (?P.*) from (?P.*) not allowed because 
not listed in AllowUsers
USERDEF_FAILED_ENTRY_REGEX3=Address (?P.*) maps to (?P.*), but this 
does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!

Es importante ver que las partes variables de las cadenas van entre los caracteres (?P.*), pudiendo definir las variables "<user>", "<host>" y "<message>"

 

UN ERROR DEL PROGRAMA Y SU SOLUCIÓN

A pesar de que el programa funcionaba bastante bien y añadía la mayor parte de los ataques detectados al archivo /etc/hosts.deny, en algunas ocasiones no lo hacía y entre los logs del sistema encontré este mensaje de error repetido un montón de veces:

Sep 14 01:02:14 - denyhosts : ERROR regex pattern ( User (?P.*) 
not allowed because not listed in AllowUsers ) is missing 'host' group

Como es lógico, para que el programa funcione adecuadamente, lo más importante es que pueda identificar la IP atacante, es decir, que la cadena ha de tener siempre un grupo del tipo (?P<host>.*), algo que falta en esta cadena ya que los mensajes son del tipo:

Sep 14 01:02:41 casa sshd[2771]: User  from  
not allowed because not listed in AllowUsers

Para eliminar este error y lograr que el programa funcione adecuadamente, tenemos que modificar el archivo regex.py, que en mi sistema está en el directorio /usr/lib/python2.5/site-packages/DenyHosts. En otros sistemas, con versiones anteriores de Python, lo tendrán en /usr/lib/python2.4/site-package /DenyHosts.

Hay que cambiar la cadena:

FAILED_ENTRY_REGEX7 = re.compile(r"""User (?P<user>.*) 
not allowed because not listed in AllowUsers""")

Por esta otra, con cuidado de escribirla exactamente tal como se ve aquí (pero en una sola línea):

FAILED_ENTRY_REGEX7 = re.compile(r"""User (?P<user>.*) .*from (?P<host>.*) 
not allowed because not listed in AllowUsers""")

Como veremos, hay disponibles otras cadenas para versiones futuras, que podemos añadir o cambiar si queremos; por ejemplo:

FAILED_ENTRY_REGEX8 = re.compile(r"""Address (?P<host>.*) maps to (?P<message>.*),
but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!""")

Después de modificar este programa, bastará con que vayamos a /etc/init.d y reinicialicemos DenyHosts mediante el mandato "sudo denyhosts restart" y si es necesario, el demonio sshd, mediante el mandato "sudo sshd restart".

Como se puede ver, este magnífico programa es muy potente y flexible y si lo tenemos configurado adecuadamente podremos ver cosas como ésta en nuestro log, que sinceramente me encantan:

Sep 14 01:02:41 casa sshd[2771]: User root from xxx.xxx.xxx.xxx 
not allowed because not listed in AllowUsers
Sep 14 01:02:42 casa sshd[2771]: User root from xxx.xxx.xxx.xxx 
not allowed because not listed in AllowUsers
Sep 14 01:02:42 - denyhosts : INFO new denied hosts: ['xxx.xxx.xxx.xxx']
Sep 14 01:02:44 casa sshd[2771]: refused connect from 
xxxx.com (::ffff:xxx.xxx.xxx.xxx)
Sep 14 02:00:01 - sync : INFO sent 1 new host
Sep 14 02:00:01 - sync : INFO received 50 new hosts

Evidentemente, el host con IP xxx.xxx.xxx.xxx no podrá volver a intentarlo en nuestro sistema mientras esté su IP en /etc/hosts.deny. También hay que decir que desde que este sistema está funcionando en mi servidor han descendido los ataques por fuerza bruta y diccionario de forma considerable.

Un resumen normal de conexiones denegadas para un periodo de 24 horas es el siguiente, y teniendo en cuenta que el sistema introduce la IP en la lista negra con solamente un único intento fallido, la cosa no está nada mal:

::ffff:xxx.xxx.xxx.xxx (::ffff:xxx.xxx.xxx.xxx): 2 Time(s)
::ffff:xxx.xxx.xxx.xxx (::ffff:xxx.xxx.xxx.xxx): 11 Time(s)
::ffff:xxx.xxx.xxx.xxx (::ffff:xxx.xxx.xxx.xxx): 1 Time(s)
::ffff:xxx.xxx.xxx.xxx (::ffff:xxx.xxx.xxx.xxx): 2 Time(s)
::ffff:xxx.xxx.xxx.xxx (::ffff:xxx.xxx.xxx.xxx): 2 Time(s)
::ffff:xxx.xxx.xxx.xxx (::ffff:xxx.xxx.xxx.xxx): 2 Time(s)
::ffff:xxx.xxx.xxx.xxx (::ffff:xxx.xxx.xxx.xxx): 2 Time(s)
xxx.com (::ffff:xxx.xxx.xxx.xxx): 1 Time(s)

Como se puede ver, el programa -cuando se ha configurado para que lo haga así- también resuelve el dominio del atacante cuando existe resolución inversa del mismo, lo que también es muy útil.

Alguno se sorprendería de ver de qué sitio vienen algunos intentos ;-) .

"Copyleft 2008 Fernando Acero Martín. Verbatim copying, translation and distribution of this entire article is permitted in any digital medium, provided this notice is preserved."

Etiquetas

Comentarios

Selecciona arriba tu forma preferida de visualizar los comentarios y pulsa el botón para guardar tu elección para próximas visitas (sólo si eres usuario registrado).

Con lo facil que es cambiar el puerto del SSH, y te quitas todas estas mierdas, la gente se complica 120 veces más de lo necesario.

Más fácil todavía es hacerte un escaneo de puertos y averiguar en cuál escucha ahora sshd.

Hay un módulo de IPTables que te permite bloquear una conexión TCP procedente de un determinado site si el número de conexiones por segundo supera un determinado límite.

Yo lo uso para bloquear el acceso ssh a la maquinita de mi casa.

Pero reconozco que tu solución parece mas elegante ...

Un saludo

Hola Jonsito:

Esta solución es más flexible y no se basa en la saturación del puerto, se basa en el número de intentos, que por cierto, pueden estar bastante espaciados en el tiempo. Por supuesto está unido, como comento en mi artículo, a otras medidas de seguridad, como no permitir el acceso nada más que a determinados usuarios, no permitir el acceso a root, no permitir el acceso mediante nombre de usuario y contraseña, exigiendo el uso de claves RSA, etc, etc, etc.

Está claro que también hay otras soluciones, como fail2ban, pyauthanalyzer, DaemonShield, creo que el módulo al que te refieres es "recent". De todos modos, con las redes distribuidas yo ya he detectado ataques al servidor en los que van cambiando las IP, por lo que sería bueno poder meter en /etc/hosts.deny todas las IP que han usado un mismo usuario en un determinado periodo de tiempo, aunque a mi no me afecta ya que tengo el umbral de disparo muy bajo.

Un saludo, Fernando Acero

El siguiente fragmento de script realentiza los ataques aceptando sólo una conexión SSH por minuto.

La pega es una denegación de servicio ante un ataque sostenido en el tiempo (siempre gana el malo y bloquea el puerto 22 por un minuto para todo el mundo, incluído tú).

Personalmente, nunca he tenido ese inconveniente.

# Reemplazar por la interfaz de cara a Internet
IF_PUBLIC="eth0"

# Válido para la versión de ifconfig en inglés.
# En caso de tener la versión en castellano, reemplazar "inet addr" por "inet dirección"
# Alternativamente, asignar la dirección IP a mano :)

IP_PUBLIC=`/sbin/ifconfig $IF_PUBLIC | /usr/bin/awk -F "[ \t\n:]+" '/inet addr:/ {  print $4  }'`

# La regla de ralentización
/sbin/iptables -A INPUT -i $IF_PUBLIC -d $IP_PUBLIC -p tcp --dport 22 -m limit --limit 1/minute --limit-burst 1 -j ACCEPT

Saludos,
Andy

Esta opción me parece peligrosa, al fin y al cabo, lo que provoca la denegación de servicio en muchos casos son los paquetes que llegan a la interfaz, cuando estos superan el ancho de banda del sistema, lo que es independiente de las conexiones que se realizan realmente, o si son rechazados o no los paquetes por el cortafuegos.

Dar la oportunidad mediante la reducción del ancho de banda de una forma artificial como comentas, permitiendo solamente un determinado número de conexiones por minuto a un determinado puerto, puede ser peligroso puesto que pone muy sencillo que acabes no pudiendo acceder al sistema a través de ese puerto. Esto es como adelantar varios órdenes de magnitud la agonía del DOS

Por el motivo que he indicado en el primer párrafo, una denegación de servicio es uno de los ataques más complicados de solucionar.

"Copyleft 2008 Fernando Acero Martí­n. Verbatim copying, translation and distribution of this entire article is permitted in any digital medium, provided this notice is preserved".
"Copyleft 2009 Fernando Acero Martí­n. Verbatim copying, translation and distribution of this entire article is permitted in any digital medium, provided this notice is preserved".

Con esto localizas los malos, los metes en un fichero y despues los bloqueas como quieras

cat /root/auth.sh

#!/bin/bash
###############################
# Autor: Paco Aldarias.
# Fichero: aut.sh
# Fecha: 3.12.07
################################

###############################
# Variables
##############################
f="intrusos.txt"

###############################
# Elimina repetidos
###############################
function repe
{
cat $f | sort |  uniq > temp.txt
mv temp.txt $f
}

################################
# MAIN
################################

cambio=0
ip=$(cat /var/log/auth.log  | awk ' /Invalid user/ {print $10}' | uniq)
for i in $ip;do
 cant=$(cat /var/log/auth.log  | awk ' /Invalid user/ {print $10}' | grep $i  |wc -w)
 #echo "ip: $i cant: $cant"
if [ $cant > 5 ];then
    echo "Añandiendo $i a $f"
    echo $i >> $f
    cambio=1
fi
done
if [ $cambio = 1 ];then
  repe
fi
#cat $f

Esta funcion la saco de mi cortafuegos, es para bloquear esas ips 1 vez

function intrusos {
fich="intrusos.txt"
cf=/sbin/iptables
iadsl=
echo [*] Bloqueando maquina no confiables fichero $fich

for d in $(cat $fich); do
  echo " [+] $d "
  for ch in FORWARD INPUT; do
    if [ -z "`iptables -L $ch -n|grep DROP.*$d.*`" ]; then
      iptables -A $ch -s $d -j DROP
    fi
  done
done
}

Es mas artesanal. Tb aplico proteccion de ssh

function protegessh {

echo [*] Protegiendo ssh

#Creamos las cadenas
iptables -N ssh
iptables -N sshscan

#Reglas para Input

iptables -A INPUT -p tcp --dport 22 -j ssh

#Reglas para ssh

iptables -A ssh -m state --state NEW -m recent --name ssh \
--rsource --update --seconds 60 --hitcount 6 -j sshscan

iptables -A ssh -m state --state NEW -m recent --name ssh \
--rsource --set -j ACCEPT

#Reglas en caso de escaneo

iptables -A sshscan  -m limit --limit 1/minute --limit-burst 2
#-j LOG --log-prefix "OJO, hay demasiados SSH:"

iptables -A sshscan -j DROP

}

Un saludo.

Una de las principales ventajas de HostDeny es que detecta el ataque en segundos y toma la acción necesaria para evitarlo en milisegundos, no tienes que estar pendiente del log y funciona muy bien.

Aademás las reglas que utiliza permiten mantener el archivo hosts.deny bastante delgado, por lo que a la larga es fácil de mantener. Hay que entender que si me dedico pasarle a iptables los 20 0 30 sistemas que intentan acceder ilegalmente al ssh diariamente, puede que solamente una vez en la vida, acabaré teniendo un filtro de lo más pesado.

Un saludo, Fernando Acero

Hola.

fail2ban es otra utilidad que realiza lo mismo pero para más servicios, no solo ssh. Animo al que quiera a probarla, está bastante bien. Además tiene paquetes para varias distribuciones por lo que su instalación os será fácil.

Un saludo.

En mi caso uso fail2ban, con una filosofía parecida y muy fácil de usar. En principio sólo "banea" temporalmente las IP's, tiempo más que suficiente para que los robots te dejen en paz.

Publicidad

Patrocinadores

Kriptópolis alojado en
Zilos-Veloxia Network

Tu mejor defensa:
Bufet Almeida

Publicidad