sábado, 29 de diciembre de 2007

Primeros pasos con jQuery

jQuery es una librería de JavaScript, su principal utilidad es dar un acceso más claro y sencillo a los elementos de la web mediante la filosofía DOM.

La librería es muy sencilla de instalar, tan solo hay que ir a la web de jQuery, descargar la última versión, subirla al servidor web e incluirla en donde queráis usarla de la siguiente forma (como cualquier fichero javascript):

<script type="text/javascript" src="jquery.js"></script>

Casi todos los códigos que usen jQuery comenzarán así:

$(document).ready(inicializar);

¿Qué significa esa línea de código? "Cuando se descargue la página web por completo llama a la función 'inicializar'".

Esa función 'inicializar' será la que asignará cada evento a cada elemento del DOM de la página.
Para los ejemplos voy a utilizar una web con 6 botones y una caja de texto:

<html>
<title>Titulillo</title>
<h1>Prueba</h1>

<body>

<input type="button" id="boton1" value="click">
<input type="button" id="boton2" value="click">
<input type="button" id="boton3" value="click">
<input type="button" id="boton4" value="click">
<input type="button" id="boton5" value="click">
<input type="button" id="boton6" value="click">
<input type="text" id="text1" value="click">

</body></html>

La función 'inicializar' sería algo como lo que sigue:

function inicializar(){
$("input").click(BotonClick);
$("input").hover(
function() { $(this).addClass("blanco"); $(this).removeClass("rojo");},
function() { $(this).addClass("rojo"); $(this).removeClass("blanco"); }
);
}

Esta función asocia a todos los elementos "input" de la página el evento "click" con la función 'BotonClick', así que cada vez que hagamos click en un botón de la página o en la caja de texto se ejecutará esa función.

La segunda hace lo mismo con el evento "hover", pero en esta ocasión se le asocian dos funciones, la primera se ejecutará cuando el ratón pase por encima de un elemento "input" y la segunda cuando salga de su espacio.

Otra diferencia es que las funciones asociadas son anónimas (no se pueden llamar desde otro sitio).

¿Qué hacen esas funciones? El código de las dos es muy parecido, añaden al objeto la clase rojo y eliminan la clase blanca y viceversa.

¿Para qué? Asociando los objetos de la página mediante clases se pueden manipular de forma más sencilla, por ejemplo, en el caso anterior si la página tuviera el siguiente código css:

<style>

.blanco{
background-color:#ffffff;
}

.rojo{
background-color:#ff0000;
}

</style>

Los elementos "input" de la página cambiarían de rojo a blanco y de blanco a rojo al pasar por encima de ellos con el ratón.

Hay muchas posibilidades, otro ejemplo de lo que se puede hacer:

function BotonClick(){
$(this).hide();
}

El elemento en el que se haga click desaparecerá, gracias a cosas como esta se puede reconstruir la interfaz de una página dinámicamente.

viernes, 21 de diciembre de 2007

Enviar formularios con javascript

Normalmente, el código html para enviar un formulario es algo parecido al siguiente:
Introduzca su nombre:
<form action="index.php" method="get">
<input name="nombre" type="text">
<input value="Enviar" type="submit">
</form>

Pero puede darse el caso, de que en nuestra página nos interese enviar el formulario al hacer click en un enlace, eso se puede solucionar mediante javascript de la siguiente forma:

Introduzca su nombre:
<form action="index.php" name="formulario" method="GET">
<input type="text" name="nombre">
<a href=javascript:document["formulario"].submit();>Enviar</a>
</form>

Aunque funciona correctamente no queda demasiado bien que el usuario vea el código javascript al pasar por encima del enlace, se puede ocultar cambiandolo por lo siguiente:

Introduzca su nombre:
<form action="index.php" name="formulario" method="GET">
<input type="text" name="nombre">
<a href=# onclick=javascript:document["formulario"].submit();>Enviar</a>
</form>

Bueno, aunque todos los enlaces los he colocado entre las etiquetas form del formulario, pueden estar en cualquier otro sitio, por ejemplo:

Introduzca su nombre:
<form action="index.php" name="formulario" method="GET">
<input type="text" name="nombre">
</form>

<a href=# onclick=javascript:document["formulario"].submit();>Enviar</a>

Igualmente, he usado enlaces para colocar el código javascript, pero es indiferente, se puede colocar el código en cualquier elemento que soporte javascript.

jueves, 6 de diciembre de 2007

¿Como hashear contraseñas en perl?

Casi todas las aplicaciones web que hay en internet requieren registro, este registro consiste normalmente un nombre de usuario y una contraseña.

Para que la aplicación web nos pueda identificar la próxima vez que introduzcamos el usuario y contraseña es necesario que almacene estos datos de alguna forma (normalmente una base de datos).

El problema de este enfoque es que si se descubre una vulnerabilidad en la aplicación web que permita listar campos de la base de datos (por ejemplo, inyección SQL) el atacante podrá leer todos los nombres de usuario y contraseñas del sitio web.

Para solucionarlo en parte, una medida común es no guardar las contraseñas como tales en la base de datos, sino un dato derivado de la contraseña, por ejemplo, se puede pasar la contraseña a una función hash y guardar el resultado obtenido.

Para el proceso de autentificación, ahora no habrá que comparar la entrada del usuario con el valor que hay en la base de datos, sino el hash de la entrada del usuario con el valor de la base de datos, si estos resultados coinciden, la autentificación será correcta.

Eligiento como función hash MD5 y como lenguaje perl, el proceso para crear el hash de una cadena es muy sencillo.

#!/usr/bin/perl

use Digest::MD5 qw(md5 md5_hex md5_base64);

$hash1=md5("pass");
$hash2=md5_hex("pass");
$hash3=md5_base64("pass");

print $hash1 . "\n";
print $hash2 . "\n";
print $hash3 . "\n";

Como se puede ver, he usado tres formas distintas para la misma función hash, la primera (md5) da el resultado en binario, la segunda (md5_hex) en hexadecimal y la tercera (md5_base64) en base 64. Se puede elegir cualquiera de ellas.

martes, 11 de septiembre de 2007

Introducción a las herramientas de depuración de linux

En todos los sistemas operativos que merezcan ese nombre hay una serie de utilidades para depurar programas. Para comentar las de linux voy a basarme en el siguiente mini-programa.

#include <stdio.h>

int main(){
char cadena[1024];
printf("Introduce el pass: ");
scanf("%s", cadena);

if(!strcmp(cadena, "esteeselpass")) printf("Correcto\n");
else printf("Incorrecto\n");
}

Si compilamos ese fichero con gcc, obtendremos un ejecutable, yo le he llamado "prueba".

La primera utilidad es objdump, con ella se puede desensamblar ejecutables, bueno, eso y algunas cosas más.

objdump -d prueba > prueba.asm

Ahora en prueba.asm tenemos el fichero con el código desensamblado.

Intentemos averiguar el password del programa anterior, para ello hay que saber algo de ensamblador. Sabemos que en algún momento hay un salto condicional (je, jne, jz, jnz...) que es el que nos lleva a la situación de error, buscando estos saltos podemos ir rápido sobre el código y averiguar la zona en la que se produce la comprobación.

8048472:    e8 d9 fe ff ff           call   8048350 
8048477: 8d 85 f0 fb ff ff lea 0xfffffbf0(%ebp),%eax
804847d: 89 85 e0 fb ff ff mov %eax,0xfffffbe0(%ebp)
8048483: c7 85 dc fb ff ff e3 movl $0x80485e3,0xfffffbdc(%ebp)
804848a: 85 04 08
804848d: c7 85 d8 fb ff ff 0d movl $0xd,0xfffffbd8(%ebp)
8048494: 00 00 00
8048497: fc cld
8048498: 8b b5 e0 fb ff ff mov 0xfffffbe0(%ebp),%esi
804849e: 8b bd dc fb ff ff mov 0xfffffbdc(%ebp),%edi
80484a4: 8b 8d d8 fb ff ff mov 0xfffffbd8(%ebp),%ecx
80484aa: f3 a6 repz cmpsb %es:(%edi),%ds:(%esi)
80484ac: 0f 97 c2 seta %dl
80484af: 0f 92 c0 setb %al
80484b2: 89 d1 mov %edx,%ecx
80484b4: 28 c1 sub %al,%cl
80484b6: 89 c8 mov %ecx,%eax
80484b8: 0f be c0 movsbl %al,%eax
80484bb: 85 c0 test %eax,%eax
80484bd: 75 0e jne 80484cd
80484bf: c7 04 24 f0 85 04 08 movl $0x80485f0,(%esp)
80484c6: e8 b5 fe ff ff call 8048380
80484cb: eb 0c jmp 80484d9
80484cd: c7 04 24 f9 85 04 08 movl $0x80485f9,(%esp)
80484d4: e8 a7 fe ff ff call 8048380

En este código vemos que se hace un scanf, unas operaciones intermedias, luego hay un jne que nos lleva a un puts o a otro (salidas por pantalla diferentes según una condición).

Ahora que ya sabemos por que zona movernos, podemos usar gdb.

gdb es un debugger gnu para linux, ejecutamos:

gdb prueba

En el listado de arriba desensamblado se puede ver que la instrucción que hace la comparación de las cadenas introducida y correcta es la siguiente:

80484aa:    f3 a6                    repz cmpsb %es:(%edi),%ds:(%esi)

asi que dentro de gdb, ejecutamos:

break *0x80484aa

Con lo que la ejecución se parará justo en esa instrucción, ejecutamos:

run

info registers

Obtenemos el valor de edi y esi, en mi caso 0x80485e3 y 0xbf86a3d8, inspeccionamos lo que hay en esas direcciones de memoria:

x/s 0x80485e3
x/s 0xbf86a3d8

Nos aparecen el pass que hemos introducido, y el ¡correcto!

Hay otra forma de conseguir que el programa nos de "su aprobación":

80484bb:    85 c0                    test   %eax,%eax
80484bd: 75 0e jne 80484cd

Vamos a la instrucción justo anterior a la del salto, el test (0x80484bb) ponemos un breakpoint ahí, si ejecutamos:

info registers

Vemos que eax vale 1, asi que la condición nos llevará al error, pero vamos ha hacer lo siguiente:

set $eax=0

Después pulsamos la 'c' para continuar...y obtenemos el mensaje que queríamos obtener aún habiendo introducido mal la contraseña.

martes, 21 de agosto de 2007

Programación orientada a objetos sencilla y elegante

La mayoría de los programadores cuando piensan en programación orientada a objetos piensa en java o c++, pero hay muchos lenguajes que implementan esta técnica y algunos mucho mejor que en c++ y java.

Me ha llamado la atención python, para mí uno de los mejores lenguajes para aprender orientación a objetos, más que nada, por su sencillez.

¿Qué hay que hacer para definir una clase en python?
Imaginemos que queremos definir una clase llamada pareja que tiene dos atributos, con una única operación, sumarlos.

class pareja:
def __init__(self, a=0, b=0):
self.a=a
self.b=b
def sumar(self):
return self.a+self.b

Simplemente eso, __init__ siempre es el constructor, y tal y como lo he definido se pueden crear objetos así:

p=pareja(2,3)

O así:

p=pareja()

O incluso así:

p=pareja(b=2)

Añadiendo un parámetro más, ¡se puede tener el constructor por defecto, por parámetros y por copia con un mismo método y así de simple!

Otro aspecto son los iteradores, tanto en java como c++, cuando quieres recorrer un array, con un iterador, hay que declarar el iterador indicando su tipo, crear un bloque for que vaya desde it.begin hasta it.end, etc...

En python esto es mucho más fácil, ¿como se recorre un array de objetos?

for i in array:
print i

Da igual si el array es de números, de listas, de objetos complejos, o incluso si el array es una cadena de carácteres.

Una última cosa, normalmente hay que pelearse con las bases de datos para ir guardando los objetos, vamos, para que sean persistentes. En python esto no es problema, simplemente haciendo:

pickle.dump(objeto, fichero)

Se guarda el objeto en el fichero, y haciendo:

objeto=pickle.load(fichero)

Se carga el objeto desde el fichero, sin más complicaciones.

jueves, 9 de agosto de 2007

Aprendiendo AJAX desde cero

AJAX es una tecnología usada para la web, permite la carga asíncrona de contenidos. Los que no sepais que es os pongo un ejemplo muy sencillo.

Si entráis a hotmail (no windows live), y queréis leer un mensaje, hacéis click en el enlace y tenéis que esperar a que cargue la nueva página. En gmail, por el contrario, pincháis en el mensaje que quereis leer y se despliega, no hace falta esperar a que cargue de nuevo toda la página.
El segundo método de carga usa AJAX, mientras que el primero es una petición al servidor de las de toda la vida.

AJAX no es nada por si mismo, es la unión de javascript, el objeto XMLHttpRequest y XML.

La arquitectura de una aplicación AJAX es la siguiente, se tiene un programa en javascript en el cliente, y este, mediante el objeto XMLHttpRequest se comunica con el servidor según convenga mostrando directamente el resultado en el cliente. No es necesario recargar la página, lo que se hace es ir cambiando el contenido dinámicamente sobre la misma página.

Cuando digo que se comunica con el servidor puede ser tanto desplegar una opción, como cambiar el texto de toda la página o mandar una petición al servidor para darnos de alta, cualquier cosa.

¿Cómo se crea el programa en javascript para poder comunicarse con el servidor? Pues lo primero es un método casi estándard, la creación del objeto XMLHttpRequest de la siguiente forma:

if(window.XMLHttpRequest) { obj=new XMLHttpRequest(); }
else {
try { obj=new ActiveXObject("Microsoft.XMLHTTP"); }
catch (e) { alert('El navegador no tiene el objeto XMLHttpRequest'); }
}

Después, para hacer las peticiones hay que usar un código como el siguiente:

obj.open('GET', 'registro.txt');
obj.onreadystatechange = tratarTexto ;
obj.send('');

Creado el objeto, mandamos (por el método GET) una petición al servidor del fichero registro.txt, cuando cambie el estado del objeto XMLHttpRequest se llamará a la función 'tratarTexto'. Esta función puede ser algo como lo siguiente:

if(obj.readyState==4){
respuesta=obj.responseText;
zonaRegistro=document.getElementById(zonaRegistro);
zonaRegistro.innerHTML=respuesta;
}

El estado 4 es el estado en el que la petición ha finalizado correctamente, en la documentación se pueden consultar los demás estados por si son necesarios.
Obtenemos el objeto que apunta al elemento de la página 'zonaRegistro' y lo rellenamos con la respuesta que hemos obtenido en el servidor.

Para terminar, supongamos que el registro que estábamos manejando arriba viene de un formulario, en lugar de mandarlo a un script en perl (por ejemplo) lo que hacemos es mandar una petición a dicho script mediante el objeto XMLHttpRequest. Por ejemplo:

<form name="formulario" action="javascript:peticionAJAX('script.pl', parametros);">

De esa forma podemos hacer una petición de registro al servidor mediante el script 'script.pl' y que cuando este termine y nos deje la información en 'registro.txt' devolvérsela al cliente, todo eso sin tener que recargar la página.

En este caso nos hemos comunicado con el servidor recibiendo tan solo texto, pero también se puede entregar el texto formateado en XML, es un poco más complejo, pero tampoco demasiado.

jueves, 26 de julio de 2007

Transparencias con javascript

He estado investigando un poco el tema, al final he llegado a la conclusión que aún no hay una manera estándar de manejar las transparencias con javascript.

Yo lo he conseguido manejar de la siguiente forma:

var object = document.getElementById(id).style;
object.opacity = (opacity / 100);
object.MozOpacity = (opacity / 100);
object.KhtmlOpacity = (opacity / 100);
object.filter = "alpha(opacity=" + opacity + ")";


Supuestamente deberia funcionar en todos los navegadores (aunque en konqueror no se por que no funciona), cada línea es para un navegador, para que os hagais una idea de como está la cosa.

miércoles, 4 de julio de 2007

Buscando los enlaces de una web

Pongo otro ejemplo de lo bueno que es perl tratando textos, es un script simple que extrae los enlaces de una web, hay algunos tipos de enlaces que no filtra del todo bien, pero como era solo un experimento...tampoco me he preocupado.


#!/usr/bin/perl
use IO::Socket;

#analyzer, extrae los enlaces de una pagina web determinada

unless (@ARGV > 1) { die "Uso: $0 host documento" }
$host = shift(@ARGV);
foreach $document ( @ARGV ) {
$remote = IO::Socket::INET->new(Proto => "tcp", PeerAddr => $host, PeerPort => "http(80)");

unless ($remote) { die "No se puede conectar a $host" }

$remote->autoflush(1);
print $remote "GET $document HTTP/1.0\n\n";

push(@respuesta, <$remote>);
while (<$remote>) {push(@respuesta,<$remote>);}
$texto = join(" ",@respuesta);


#Extrae los enlaces
while ($texto=~/(href=)([^>]+)>/g){
$dir=$2;

#Elimina las comillas y limpia algunas direcciones
if($dir=~/\"([\s\S]+)\"[\s\S]*/){
$dir_limpia=$1;

#Elimina los enlaces que no empiecen por http o https
if($dir_limpia=~/^http/ or $dir_limpia=~/^https/){
print "$dir_limpia\n";
}
}
}

close $remote;
}



No tiene mucha utilidad por si solo, pero puede integrarse en programas más grandes, un ejemplo de funcionamiento, si ejecutamos: ./analyzer.pl google.es "/search?hl=es&q=javiyu&btnG=Buscar+con+Google&meta="

Obtendremos como salida los enlaces de la búsqueda en google.

...
http://javiyu.blogspot.com/
http://216.239.59.104/search?q=cache:Ai8OiBXDTQEJ:javiyu.blogspot.com/+javiyu&hl=es&ct=clnk&cd=4&ie=UTF-8
http://javiyu.blogspot.com/search/label/redes
...

lunes, 25 de junio de 2007

QoS con pf

Intentando establecer un límite de bajada para un servidor web he estado haciendo pruebas con mod_bandwidth (en el post anterior) pero no funciona todo lo bien que debiera.

Como alternativa he probado a hacer QoS con pf (el cortafuegos de openBSD), funciona realmente bien, se pueden establecer colas con prioridad de la siguiente forma en el archivo pf.conf:

altq on ne3 cbq bandwidth 10Mb queue {http, std}

queue http bandwidth 1Mb priority 1
queue std bandwidth 9Mb priority 4 cbq (default)

pass in quick log on ne3 proto tcp from any to any port 80 queue http

ne3 es la interfaz de red, he definido una cola para http y el resto para los demás servicios, los límites también se pueden establecer mediante porcentajes.

Para recargar las reglas en el cortafuegos simplemente pfctl -e -f /etc/pf.conf como root.

Limitar el ancho de banda de apache en openBSD

Como no solemos tener ancho de banda de subida de sobra (siempre es más caro que el de bajada), puede ser interesante limitar el ancho de banda de apache.

En openBSD solo hay que hacer lo siguiente:

pkg_add ftp://ftp.openbsd.org/pub/OpenBSD/4.1/packages/i386/mod_bandwidth-2.0.6.tgz

/usr/local/sbin/mod_bandwidth-enable

Y editar el httpd.conf y poner lo siguiente:

<Directory "/var/www/htdocs/">
BandWidth all 10240
</Directory>

Siendo /var/www/htdocs/ el directorio a limitar, y el 10240 el número de bytes/segundo máximo, en el caso de arriba estamos limitando a 10KB/s.

Para que apache lea esta configuración:

apachectl restart

Para otros sistemas que no sean openBSD lo único que variará será la forma de instalar el mod_bandwidth.

sábado, 23 de junio de 2007

Descargar la wikipedia

Siempre viene bien tener la información en soporte físico para aquellas ocasiones en las que no tenemos conexión a internet, la gente que contribuye a la wikipedia ya ha pensado en esto.

Podéis bajaros la wikipedia completa en español, en inglés o en cualquier cualquier otro idioma.

El último backup que he encontrado para descargar es a fecha de abril del 2007, aunque quizá haya por ahí versiones más actualizadas, los archivos están comprimidos con 7-zip.

El programa para descomprimir los archivos lo podéis encontrar aquí.

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!.

martes, 12 de junio de 2007

De estreno

¡Hola! esta es la primera entrada de mi blog, no aspiro a actualizarlo diariamente, pero de vez en cuando iré poniendo cosillas.

¿La temática? Programación, redes, sistemas operativos...esas cosillas.

Espero que conseguir muchos lectores, eso significará que a alguien le interesa lo que escribo :P, bueno, ¡hasta otra!.