viernes, 15 de junio de 2007

Filtrando ataques de fuerza bruta a ssh

Todos los que hayáis instalado un servidor de ssh en alguna máquina conectada a internet os habréis dado cuenta de la cantidad de ataques de fuerza bruta que recibe.

Si algún despistado no sabe de lo que hablo solo tiene que leer el fichero /var/log/auth.log (la ruta puede variar), si se encuentra cosas como esta:


Jun 7 15:16:50 ubuntu sshd[10729]: Invalid user root from 192.168.0.20
Jun 7 15:16:50 ubuntu sshd[10729]: Failed none for invalid user root from 192.168.0.20 port 47762 ssh2
Jun 7 15:16:50 ubuntu sshd[10729]: Failed password for invalid user root from 192.168.0.20 port 47762 ssh2
Jun 7 15:16:50 ubuntu last message repeated 2 times
Jun 7 15:16:51 ubuntu sshd[10731]: Connection from 192.168.0.20 port 44584
Jun 7 15:16:52 ubuntu sshd[10731]: Invalid user root from 192.168.0.20
Jun 7 15:16:52 ubuntu sshd[10731]: Failed none for invalid user root from 192.168.0.20 port 44584 ssh2
Jun 7 15:16:52 ubuntu sshd[10731]: Failed password for invalid user root from 192.168.0.20 port 44584 ssh2
Jun 7 15:16:52 ubuntu last message repeated 2 times
Jun 7 15:16:52 ubuntu sshd[10733]: Connection from 192.168.0.20 port 35640
Jun 7 15:16:52 ubuntu sshd[10733]: Invalid user root from 192.168.0.20
Jun 7 15:16:52 ubuntu sshd[10733]: Failed none for invalid user root from 192.168.0.20 port 35640 ssh2
Jun 7 15:16:52 ubuntu sshd[10733]: Failed password for invalid user root from 192.168.0.20 port 35640 ssh2
Jun 7 15:16:52 ubuntu last message repeated 2 times
Jun 7 15:16:53 ubuntu sshd[10735]: Connection from 192.168.0.20 port 12587
Jun 7 15:16:53 ubuntu sshd[10735]: Invalid user root from 192.168.0.20
Jun 7 15:16:53 ubuntu sshd[10735]: Failed none for invalid user root from 192.168.0.20 port 12587 ssh2
Jun 7 15:16:53 ubuntu sshd[10735]: Failed password for invalid user root from 192.168.0.20 port 12587 ssh2
Jun 7 15:16:53 ubuntu last message repeated 2 times
Jun 7 15:16:53 ubuntu sshd[10737]: Connection from 192.168.0.20 port 20968
Jun 7 15:16:54 ubuntu sshd[10737]: Invalid user root from 192.168.0.20
Jun 7 15:16:54 ubuntu sshd[10737]: Failed none for invalid user root from 192.168.0.20 port 20968 ssh2
Jun 7 15:16:54 ubuntu sshd[10737]: Failed password for invalid user root from 192.168.0.20 port 20968 ssh2

etc...


Como se puede ver, hay muchos intentos de acceso fallidos desde la misma ip, y ocurren tan rápido que sabemos que al otro lado no hay nadie tecleando, seguramente será algún programa probando una lista de contraseñas por fuerza bruta para conseguir acceso a nuestra máquina.

¿Qué podemos hacer para evitarlo? podríamos poner una regla en el cortafuegos para denegar el acceso a es ip en concreto, pero eso es pan para hoy y hambre para mañana, ya que el siguiente que lo intente se va a encontrar que no tiene problemas para probar.

Para no estar poniendo y quitando reglas del firewall todo el tiempo, y aprovechando que estaba aprendiendo perl, pues me he programado el siguiente script para evitar estos ataques:

#!/usr/bin/perl
use POSIX qw(strftime);
use Time::Local;


#ssh_abf.pl
#Deniega ips en el cortafuegos (iptables)
#que esten atacando por fuerza bruta al demonio ssh.
#Probado con perl 5.8.8 y openSSH4.3


#Ruta al fichero auth.log a analizar
$entrada="/var/log/auth.log";


#Intervalo durante el que se deniegan las ips
$intervalo_denegacion=600;


#Intervalo tras el cual se analiza el fichero
$intervalo_analisis=5;


#Maximo de ips denegadas
$max_denegadas=10;


#Numero de entradas en el fichero para ser denegada la ip
$intentos=2;


#Puerto en el que escucha el demonio ssh
$puerto=22;


@meses = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );


while(1){
if(scalar(@IPs)<$max_denegadas){
my @IPs=();
my @momentos=();
my @veces=();


open (ENTRADA,"<$entrada")
|| die "ERROR: No puedo abrir el fichero $entrada\n";


$anio = strftime "%Y", localtime;


# Obtiene los intentos fallidos durante el ultimo
# $intervalo_denegacion.
# Las ips se guardan en @IPs y el momento en el que atacaron en
# @momentos.
# Si una ip aparece mas de una vez solo se tiene en cuenta la
# ultima ocurrencia.
while ($linea=<ENTRADA>){
if($linea=~/Failed password*/){
$linea=~/(\S+)\s+(\d+)\s+(\d+):(\d+):(\d+)/;
$i=0;
while($meses[$i] ne $1){$i++;}
$t=timelocal($5, $4, $3, $2, $i,($anio-1900));


if((time-$t)<$intervalo_denegacion){
$linea=~/(\d+\.\d+\.\d+\.\d+)/;


$esta=0;
$pos=0;
foreach $dir (@IPs){
if($dir eq $1){$esta=1;}
if($esta==0){$pos++;}
}


if($esta==0 and scalar(@IPs)<$max_denegadas){
push(@IPs, $1);
push(@momentos, (time-$t));
push(@veces,1);
}
elsif($esta==1){
$momentos[$pos]=time-$t;
$veces[$pos]++;
}
}
}
}


#Insertar reglas en el cortafuegos
$pos=0;
foreach $v (@veces){
if($v>=$intentos and $activadas{$IPs[$pos]}==0){
system("iptables -I INPUT -p tcp -s $IPs[$pos] --dport $puerto -j DROP");
$activadas{$IPs[$pos]}=1;
}
$pos++;
}


#Borra las reglas antiguas del cortafuegos
foreach $dir (keys %activadas){
$desactivar=1;
foreach $j (@IPs){
if($dir eq $j){
$desactivar=0;
}
}
if($desactivar==1){
system("iptables -D INPUT -p tcp -s $dir --dport $puerto -j DROP");
delete $activadas{$dir};
}
}
close (ENTRADA);
}
sleep($intervalo_analisis);
}



Para probarlo solo hay que guardarlo como un fichero de texto (por ejemplo ssh_abf.pl), darle permisos de ejecución chmod u+x ssh_abf.pl y ejecutarlo como root.

Es posible que tengáis/queráis cambiar alguno de los parámetros que aparecen al principio, sobre todo la ruta del fichero auth.log.

Lo que hace el script es simplemente cada cierto tiempo lee el fichero auth.log, comprueba si alguien está haciendo un ataque de fuerza bruta, si lo está haciendo crea automáticamente una regla en el cortafuegos para que se ignore lo que llega de esa ip.

Cuando termina el "intervalo de denegación", elimina esa regla (seguramente tras ese tiempo el atacante esté con otro y ya no se acuerde de nosotros), de esta forma nos aseguramos que no hay reglas innecesarias en el cortafuegos.

Agradecería que si alguien lo prueba me comente si encuentra algún error, ¡hasta otra!.

2 comentarios:

Unknown dijo...

uuuu, un codigo sin tabular??? mal mal mal, q diria cubero si lo viera?!?!? Suspenso d x vida q tndrias mp1!!! jajajaja

javiyu dijo...

Post editado, he cambiado el diseño y ya cabe el codigo tabulado ;).