Reglamento general de los foros   •   FAQ   •   Buscar en el foro •  Registrarse | Iniciar sesión 



Portada » Foros Linux » Desarrollo » Programación


Nuevo tema Responder al tema
 [ 7 mensajes ] 
Patrocinadores

Autor
Buscar:
Mensaje

Desconectado
Forista Medio
Forista Medio

Registrado: Mié Abr 27, 2011 11:32 am
Mensajes: 302

Nota Publicado: Sab Mar 30, 2013 5:03 pm 
Arriba  
No sabía dónde dejarlo, si en proyectos o aquí, pero como en realidad tampoco es un proyecto en sí sino un script único pues me he decidido por la sección «Programación»

Dejo un script en perl que sirve para banear mediante iptables a quienes incordien. Su funcionamiento es sencillo, revisa los logs del sistema que le digas y banea ips en función de coincidencias de cadenas en las líneas de dichos logs, destinado a servidores caseros.

En principio está destinado a quien tenga un poco experiencia, hay que saber un poco de perl y expresiones regulares, más que nada para saber qué se quiere machear y banear. Cada sistema es un mundo y es difícil adaptar algo para cualquiera. Tampoco está destinado para servidores de gran tráfico sino para los caseros, el típico que tiene el ssh y un par de servicios para uso propio o de poco tráfico. Para servidores serios hay otras herramientas tipo fail2ban (creo que se llama así) mucho más apropiadas.

El script (uno parecido) lo tengo corriendo desde hace más de 1 año en mi servidor (lo lanzo con cron cada minuto) y me ha quitado de encima a docenas de bots, principalmente para ssh y postfix, nada simpáticos.

Si alguien quiere más aclaraciones estoy dispuesto a darlas,

Un saludo!

Nota. Se requiere la utilidad «geoiplookup» si se quiere usar para saber de dónde viene la ip, si no se instala (es un pequeño paquete) hay que modificar el script

Código:
#!/usr/bin/perl
# -------------------------------------------------------
# Script para vigilar logs del sistema relacionados con
# entradas desde internet hacia ssh, postfix, webserver..
#
# Funciona de forma que sólo lee las líneas nuevas que haya
# desde la última lectura, las pasa por expresiones regulares
# y en caso de encontrar alguna cadena sospechosa predefinida
# recupera si puede la ip de origen y la guarda en un hash
# junto con el número de intentos sospechosos ya encontrados
# para la misma ip.
# Si el número de intentos sobrepasa el límite de aguante
# banea con iptables al que incordia (si no lo está ya).
use strict;
use warnings;
# -------------------------------------------------------
# ---- CONFIGURACION ------------------------------------
# -------------------------------------------------------
# archivos para registrar los baneados y el log general
my $file_baneados = 'banwtf.log';
my $file_loggral  = 'vigwtf.log';
# array de logs a vigilar, añadir los que se deseen
my @logs = ();
$logs[0] = '/var/log/auth.log';
$logs[1] = '/var/log/maillog';
# número máximo de puntos de aguante
# 5 sería poco aguante, 20 aguante medio, 50 aguante top
# por defecto se irrita enseguida
my $aguante = 5;
# ips que no hay que banear, ejemplos
my @allows =  qw('192.168.1.10' '192.168.1.18');
# para modo test poner a 1, para activar wtf poner a 0
my $test = 1;
# -------------------------------------------------------
# ---- REGLAS -------------------------------------------
# -------------------------------------------------------
# hash de frases WTF y expresiones regulares.
# el principio es simple, busca en la línea de log una
# cadena sospechosa, wtf, le pasa el regexp para ver si
# puede sacar la ip de origen y si es así la penaliza con
# el valor que se le dé
my %hash_wtf = (
    '0001' => {  # ssh
        'wtf' => 'Invalid user',          # cadena WTF
        'reg' => '(\d+\.\d+\.\d+\.\d+)$', # regexp
        'pen' => 5,                       # penalización
        'act' => 1,                       # activo o no
    },
    '0002' => {  # ssh
        'wtf' => 'User root from \d+\.\d+\.\d+\.\d+ not',
        'reg' => 'User root from (\d+\.\d+\.\d+\.\d+) not',
        'pen' => 10,
        'act' => 1,
    },
    '0003' => {  # ssh
        'wtf' => 'Did not receive identification string from \d+\.\d+\.\d+\.\d+',
        'reg' => 'Did not receive identification string from (\d+\.\d+\.\d+\.\d+)',
        'pen' => 10,
        'act' => 1,
    },
    '0004' => {  # postfix
        'wtf' => 'tried to use disabled plaintext auth',
        'reg' => 'rip\=(\d+\.\d+\.\d+\.\d+),',
        'pen' => 10,
        'act' => 1,
    },
    '0005' => {  # postfix
        'wtf' => 'Relay access denied',
        'reg' => '\[(\d+\.\d+\.\d+\.\d+)\]:',
        'pen' => 2,
        'act' => 1,
    },
    '0006' => {  # postfix
        'wtf' => 'non-SMTP command from',
        'reg' => '\[(\d+\.\d+\.\d+\.\d+)\]:',
        'pen' => 10,
        'act' => 1,
    },
    '0007' => {  # postfix
        'wtf' => 'Connection rate limit exceeded',
        'reg' => '\[(\d+\.\d+\.\d+\.\d+)\]:',
        'pen' => 10,
        'act' => 1,
    },
    '0008' => {  # postfix
        'wtf' => 'Connection concurrency limit exceeded',
        'reg' => '\[(\d+\.\d+\.\d+\.\d+)\]:',
        'pen' => 10,
        'act' => 1,
    },
    '0009' => {  # postfix
        'wtf' => 'therichsheickc',
        'reg' => '\[(\d+\.\d+\.\d+\.\d+)\]:',
        'pen' => 10,
        'act' => 1,
    },
);
# -------------------------------------------------------
# ---- CUERPO DEL SCRIPT --------------------------------
# -------------------------------------------------------
# coge la opción de nivel de log --log
my $opt = $ARGV[0];
# si no hay level_log se establece a 2
$opt = "--log=2" unless $opt;
my $level_log = ($opt =~m/=(\d)$/,$1);
# por si acaso,
$level_log = 1 unless $level_log;
# nivel 2 para el test
$level_log = 2 if $test;

# hora de arranque del script, se loguea si level_log > 1
loguear ("#### Inicio script ####") if $level_log > 1;
# recupero el número de líneas a leer de cada log a vigilar
my @num_lin  = (); # líneas de cada log
my $cont     = 0;  # contador
my $lin_tot  = 0;  # líneas totales
foreach (@logs) {
    # comprueba que existe el archivo log antes de contar líneas
    print "No existe el archivo log $_\n" and next if (!-f $_);
    $num_lin[$cont] = devuelve_num_lineas_a_leer($logs[$cont]);
    $lin_tot += $num_lin[$cont];
    $cont++;
}
# sale si no hay nada nuevo que revisar
if ($lin_tot == 0) {
    loguear ("#### Fin script ####") if $level_log > 1;
    exit;
}
# sigo si hay algo, ....
loguear ("## leyendo $lin_tot líneas ##") if $level_log > 0;

# construyo la cadena de comando
my $cadena_cmd = "";
my @res = ();
$cont = 0;
my $and = "";
foreach (@logs) {
    $and = ' && ' if $cont > 0;
    $cadena_cmd .= $and .'tail -n ' . $num_lin[$cont] . ' ' . $logs[$cont];
    $cont++;
}
# lanza la cadena comando y recupero la salida en un array
@res = `$cadena_cmd`;
# recupera el hash de baneados guardado en la anterior lectura
# de esta forma no pierde los intentos anteriores
my %hash_cazados = devuelve_hash();
my $cont_lineas  = 0;
# revisa las líneas devueltas
foreach (@res) {
    my $cazado;
    my $linea = $_;
    foreach my $regla (sort keys %hash_wtf){
        if ($linea =~ m/$hash_wtf{$regla}{wtf}/ and $hash_wtf{$regla}{act}==1){
            $cazado = ($linea =~m/$hash_wtf{$regla}{reg}/,$1);
            $hash_cazados{$cazado}{intentos} += $hash_wtf{$regla}{pen} if $cazado;
            loguear ("wtf: $hash_wtf{$regla}{wtf}") if $level_log > 1;
            last;
        }
    }
    # si hay algún intruso...
    if ($cazado) {
        $hash_cazados{$cazado}{ip} = $cazado;
        loguear ("Cazado: $cazado Intentos: $hash_cazados{$cazado}{intentos} Linea: $_") if $level_log > 0;
    }
    # cuenta las líneas que va leyendo
    $cont_lineas++;
}

# loguea el número de líneas leídas totales si level_log > 1
loguear ("Líneas leídas: $cont_lineas") if $level_log > 1;

# ahora pasamos a banearlos si es necesario ...
foreach my $ip (sort keys %hash_cazados){
    # nos saltamos algunas ips que no queramos banear nunca...
    next if ("@allows" =~m /$ip/);
    # repasa el hash, si alguna ip tiene más de $aguante intentos sigue
    if ($hash_cazados{$ip}{intentos} > $aguante ) {
        # si ya está anotada como baneada salta al siguiente
        next if $hash_cazados{$ip}{baneado};
        # se asegura así y todo que no esté en las reglas de iptables
        my $esta_o_no = "-";
      $esta_o_no = `iptables -nL | grep $ip` if (!$test);
        # si no está ...
      if ($esta_o_no !~m/$ip/) {
         # no está en las reglas, se mira origen y
            # se escribe en el log de baneados
            my $origen = `geoiplookup $ip | head -n 1 | sed -e 's/^.*://'`;
            chomp ($origen);
            open "FILE",">>","$file_baneados";
         my $hora = `date +"%d-%m-%Y %H:%M:%S"`;chomp($hora);
         print FILE "#$hora Baneado: $origen\n$ip\n";
            close (FILE);
            if (!$test) {
                my $comando_baneo_input = "iptables -I INPUT -p all -s $ip -j DROP";
                my $salida_baneo_input = `$comando_baneo_input`;
                my $comando_baneo_output = "iptables -I OUPUT -p all -d $ip -j DROP";
                my $salida_baneo_output = `$comando_baneo_output`;
            }

            # se escribe en el log de baneador
            loguear ("Baneado: $ip , intento: $hash_cazados{$ip}{intentos}") if $level_log > 0;
            # coloco el flag baneado a 1 para que no intente banearlo de nuevo
            $hash_cazados{$ip}{baneado} = 1;
      }
    }
    else {
        # si ya está baneada ni caso
        next if $hash_cazados{$ip}{baneado};
        # avisando
        if ($hash_cazados{$ip}{intentos} > 1){
            loguear ("Hacia el baneo: $ip , intentos: $hash_cazados{$ip}{intentos}") if $level_log > 1;
        };
    }
}
# guarda el hash de cazados para leerlo la próxima vez
guarda_hash(%hash_cazados);

sub devuelve_num_lineas_a_leer {
    my $archivo_log = shift;
    # recupera el número de líneas actual
    my $lineas_actuales = `wc -l $archivo_log | sed -e 's/ .*\$//'`;
    chomp($lineas_actuales);
    # coge el nombre del archivo para apuntar después última línea
    my $nom_file = ($archivo_log =~ m/.*\/(.*)$/,$1);
    # coge la última línea leída
    my $ultima_linea_leida = 1;
    $ultima_linea_leida = `cat .ult_$nom_file` if (-f ".ult_$nom_file");
    chomp ($ultima_linea_leida) if $ultima_linea_leida;
    # hace el cálculo de líneas a leer, primero mira si no se ha
    # rotado y es más corto que en la anterior lectura
    $ultima_linea_leida = 1 if ($lineas_actuales < $ultima_linea_leida);
    my $num_lin = $lineas_actuales - $ultima_linea_leida;

    # escribe el número total de líneas actuales para la próxima lectura
    open "FILE",">",".ult_$nom_file";print FILE $lineas_actuales;close (FILE);

    loguear ("Lineas en $nom_file $lineas_actuales, última leída anterior: $ultima_linea_leida, líneas a leer: $num_lin") if $level_log > 1;
    return $num_lin;
}
sub devuelve_hash {
    my %hash_guardado ;
    # si no hay file no hay hash todavía
    return %hash_guardado if !(-f (".hash.dat"));
    my @contenido = `cat .hash.dat`;
    foreach (@contenido) {
        next if ($_ =~ m/^#/);
        next if ($_ eq "");
        my ($ip,$intentos) = split(" ",$_);
        $hash_guardado{$ip}{ip} = $ip;
        $hash_guardado{$ip}{intentos} = $intentos;
        $hash_guardado{$ip}{intentos} = 0 unless $intentos;
        $hash_guardado{$ip}{baneado} = 0;
    }
    return %hash_guardado;
}
sub guarda_hash {
    my %hash_ips = @_;
    open "FILE",">",".hash.dat";
    foreach my $ip (sort keys %hash_ips){
        # no guarda los ya baneados en el hash
        next if ($hash_ips{$ip}{baneado});
        print FILE "$hash_ips{$ip}{ip} $hash_ips{$ip}{intentos}\n";
    }
    close (FILE);
}
sub loguear {
    my $rec_log = shift;
    chomp ($rec_log) if $rec_log =~/\n/;
    my $hora = `date +"%d-%m-%Y %H:%M:%S:%N"`;chomp($hora);
    open "FILE",">>","$file_loggral";
    print FILE "## $hora # $rec_log\n";
    close (FILE);
}
loguear ("## saliendo ##") if $level_log > 1;
loguear ("#### Fin script ####") if $level_log > 1;



editado: corregido algún fallo.

_________________
OpenBSD en un ordenador simplón


Última edición por MetTxin el Dom Abr 14, 2013 8:50 pm, editado 1 vez en total
 Perfil WWW  

Desconectado
Administrador
Administrador
Avatar de Usuario

Registrado: Jue Ene 01, 1970 2:00 am
Mensajes: 3362
Ubicación: León, Guanajuato; México.

Nota Publicado: Sab Mar 30, 2013 7:18 pm 
Arriba  
Hola, MetTxin:

Muy bueno para hacer pruebas y quizá para adaptarlo en ambientes más productivos.

Te lo enlazo a portada para que no se pierda ;-)

un saludo.

_________________
No hay nada que agradecer. Hago, lo tengo que hacer.
Reglamento del foro | Temas más preguntados | Blog personal | Twitter: @pacorevilla

 Perfil WWW ICQ  

Desconectado
Forista Medio
Forista Medio

Registrado: Mié Abr 27, 2011 11:32 am
Mensajes: 302

Nota Publicado: Sab Mar 30, 2013 8:28 pm 
Arriba  
Gracias Ayax,

Me han quedado cosas por decir y explicar pero bueno, como va dirigido a usuarios de nivel medio pues ¡que pregunten! :)

Un saludo!

_________________
OpenBSD en un ordenador simplón

 Perfil WWW  

Desconectado
Forista Distinguido
Forista Distinguido
Avatar de Usuario

Registrado: Jue Ene 08, 2009 8:00 am
Mensajes: 1339
Ubicación: Magdalena [Argentina]

Nota Publicado: Dom Mar 31, 2013 12:25 am 
Arriba  
Hola MetTxin, la verdad que es un excelente aporte, felicitaciones

_________________
Diplomacia, es el arte de saber lo que no se debe decir...
_________________
Hardware: Intel i5-3570k | ASUS P8H77-M |HD [500GB] [1 TB] | Ram 8 GB | GPU: Nvidia GeForce 210/1 GB
S.O. Debian@testing x86_64 Openbox
Linux user #506272

 Perfil YIM  

Desconectado
Forista Nuevo
Forista Nuevo
Avatar de Usuario

Registrado: Dom Abr 14, 2013 2:47 am
Mensajes: 16

Nota Publicado: Dom Abr 14, 2013 6:29 pm 
Arriba  
lo que tengo yo es este pequeño script que su cometido es matar cualquier proceso de ssh en terminal como en nautilus (supuestamente,mas adelante lo explico)

Código:
#!/bin/bash
#script realizaco por Dani Bellver Martinez
#correo : dani_chella@hotmail.com
#script en bucle que mata cualquier proceso relacionado con ssh tanto en terminal como por nautilus



read -p "para activar el antissh introduzca su
contraseña: " pass



lugar="$HOME/logs/log_antissh.txt"

if [ ! -f $lugar ];then
   clear
   echo "se ha generado un log en la siguiente ubicacion :"
   echo "$HOME/logs/log_antissh.txt"
   mkdir $HOME/logs/
   touch $HOME/logs/log_antissh.txt
   sleep 2

   
else
   clear
   echo "el log_antissh.txt se encuentra en:"
   echo "$lugar"
   sleep 2
fi

sleep 2
clear
while true;do
pid=`ps aux|grep "\[priv\]"|tr -s " "|cut -f2 -d" "`



   for iii in $pid;do
      
      
      
           sudo kill $iii <<< `echo $pass` 2>/dev/null
      if [ $? -eq 0 ];then
         echo "Intento de ssh abortado."
         echo "Proceso eliminado : ssh"
         echo "Fecha             : `date +%d/%m/%y`"
         echo "Hora        : `date +%H:%M`"
         echo "PID        : $iii"
         echo "------------------------------------"
         echo "Intento de ssh abortado." >> $lugar
         echo "Proceso eliminado : ssh" >> $lugar
         echo "Fecha             : `date +%d/%m/%y`" >> $lugar
         echo "Hora        : `date +%H:%M`" >>  $lugar
         echo "PID        : $iii" >> $lugar
         echo "------------------------------------" >>  $lugar
      else
         echo "se ha producido un error, posiblemente sea que"
         echo " la contraseña introducida sea erronea"
         echo "------------------------------------"
      fi
   
   done
pid=""
done

------------
digo supuestamente porque es la unica cadena de texto "\[priv\]" que he encontrado para hacerle un grep que me busque los pid de ssh nautilus, aunque tambien mata los de la terminal... aunque parece peligroso porque si no escapo los corchetes mata todos los procesos y la pantalla se queda vacia, y no puedes hacer nada.
-----------
tengo otro que solo es para ssh terminal que te dice la ip de quien te intento hacer ssh. lo tengo en la clase, el lunes me lo traigo a casa y el martes por la mañana lo publico

 Perfil  

Desconectado
Forista Medio
Forista Medio

Registrado: Mié Abr 27, 2011 11:32 am
Mensajes: 302

Nota Publicado: Dom Abr 14, 2013 8:48 pm 
Arriba  
Gracias por compartir el código ercros.

Me entra alguna duda, ¿cómo distingues un usuario autorizado de uno que no lo esté? No veo claro ese punto, podría matar un proceso abierto por ti mismo, y si no se distinguen ¿no bastaría con desactivar el demonio ssh y ya está?

Otra cosa, una idea, para evitar mezclar procesos (macheando aquellos que no tienen nada que ver) podrías usar «who -up», incluso ganarías algo de tiempo (aunque me queda la duda si serviría para los procesos abiertos por nautilus, no uso nautilus)

un saludo y gracias de nuevo por compartir!

_________________
OpenBSD en un ordenador simplón

 Perfil WWW  

Desconectado
Forista Nuevo
Forista Nuevo
Avatar de Usuario

Registrado: Dom Abr 14, 2013 2:47 am
Mensajes: 16

Nota Publicado: Dom Abr 14, 2013 9:22 pm 
Arriba  
1ª -pregunta- realmente no distingue nada... no se porque con el \[priv\] mata los procesos ssh, no he hecho muchas pruebas al respecto.
para distinguir supongo que comparando los usuarios de /etc/passwd y de ps aux (tipo filtro) y lo que no coincida con /etc/passwd que mate su proceso bastaria.
2ª -pregunta- si jajajaja, pero estas tonterias vienen de perlas para practicar
con who tengo otro...(solo por terminal) que el martes lo publicare que te muestra la ip del intruso y mata su proceso..

soy aun un novato en este mundo, entonces estoy limitado jeje
gracias a ti por contestar...:)

 Perfil  
Mostrar mensajes previos:  Ordenar por  
 [ 7 mensajes ] 
Nuevo tema Responder al tema

Saltar a:  



¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 2 invitados

No puede abrir nuevos temas en este Foro
No puede responder a temas en este Foro
No puede editar sus mensajes en este Foro
No puede borrar sus mensajes en este Foro
No puede enviar adjuntos en este Foro

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group :: Style based on FI Subice by phpBBservice.nl :: Todos los horarios son UTC + 1 hora [ DST ]
Traducción al español por Huan Manwë
phpBB SEO