Combatiendo el SPAM - ataques de diccionario

El spam es uno de los males que está plagando la Internet. Si tienes tu propio servidor de e-mail para uso personal o administras uno en el trabajo, lidiar con ellos es un verdadero dolor de cabezas. Existen varios tipos de ataques, en este artículo muestro como combatir y bloquear a los spammers que realizan el ataque de diccionario.

El ataque de diccionario, es el ataque más tonto que un pseudo-spammer puede hacer, y ya que es un ataque tonto, tiene una solución bastante sencilla. El ataque consiste en lo siguiente.

  • Toman un e-mail real (Ejemplo: crodas@foo-bar.com)
  • Luego se le agregan letras randomicas al inicio al final

La solución se simple, hay que seleccionar todos lo mails que fueron enviados a destinatarios no existentes, luego agrupar por IP, y comparar a ver si los destinatarios son similares, si son es un ataque de diccionario. Este script funciona con el output del Log del sendmail.

#!/usr/bin/php -q
<?php
# Dedicado para los que piensan que PHP es solo para páginas webs
# y para las personas tontas que comparan un framework cualquiera
# con un lenguaje de programación.
#
if ( isset($argv[1]) && $argv[1] == "--debug")
    define("DEBUG",1);

function x_readline()
{
    return trim(fgets(STDIN));
}

function asum($x,$y)
{
    $x += $y;
    return $x;
}

$ip=array();
$dst=array();
while ($line=x_readline())
{
    $line = strtolower($line);

    preg_match("/\]\: ([a-zA-Z0-9]+):.*from\=.*relay=.*\[([0-9\.]+)\]/i",$line,$result);
    if ( count($result) > 0) $ip[$result[1]] = $result[2];
    if ( strpos($line,"user unknown") !== false )
    {
        preg_match("/]\: ([a-zA-Z0-9]+):.*to=<?([^\@]+)>?/i",$line,$result);
        if ( count($result) > 0 && isset($ip[$result[1]]) )
            $dst[ $ip[$result[1]] ][] = $result[2];
    }
}
$result=array();
#ahora a buscar los posibles candidatos
foreach($dst as $ip => $mail)
{
    $tmp=array();
    $t = count($mail);
    if ( $t < 5) continue; /* una regla mia, un mínimo de 5 mails fallidos para el control */
    for($i=0; $i < $t-1; $i++)
    {
        $threshold = (strlen($mail[$i]) + strlen($mail[$i]))/2 * 0.3;
        $weight = levenshtein($mail[$i],$mail[$i+1]);
        $tmp[] = $weight < $threshold && $weight!=0  ? 1 : -1;
    }
    $result[$ip] = array_reduce($tmp,"asum");
}

foreach($result as $ip => $weight)
{
    if ( $weight < 0) continue;
    print "$ip\n";
    if (defined("DEBUG")) print_r($dst[$ip]);
}
?>

El script devuelve una lista de IPs que realizan ataques de SPAM. Esta lista es suficiente para poder combatirlo, ejemplo ejecutar periodicamente (crontab) a cada 3 (es un ejemplo) horas y agregar en la lista de bloqueos del sendmail. Aquí un script bash que hace el trabajo “duro” de darle forma a el “output” del php.

#!/bin/bash
#configuracion
LOG=/var/log/maillog
SENDMAIL=/etc/mail/
DB=/etc/mail/access
DATE=`date`
TEXT="550 We don't accept mail from spammers # Auto added $DATE"
#el programa en sí
if [ `whoami` != "root" ]
then
    echo "Only root can run this script"
    exit
fi

find_in_access() {
    VALUE=$1
    local ACCESSIP
    for ACCESSIP in $( cat $DB | awk '{print $1}' );
    do
        if [  "$ACCESSIP" = "$VALUE" ];
        then
            return 0
        fi
    done
    return 1
}

for SPAMMER in `cat $LOG | php php_dict_attack.php`
do
    find_in_access $IP
    if [ $? -eq 1 ];
    then
        echo "$SPAMMER			$TEXT" >> $DB
    fi
done
cd $SENDMAIL
make

4 Comments

  1. ferbog says:

    mmmm.. medio que al Qmail no le gusta el script :( con tu permiso voy a tocar tu script man :D por cierto muy bueno

  2. César Rodas says:

    Claro que sí, todos mis códigos en mi blog son libres… “Public domain”.

    Y la verdad que no creo que funcione con el Qmail, porque yo me base en los logs del sendmail, pero podes enviarme el log del Qmail si queres ( tiene que ser relativamente grande ~20M) a mi correo electrónico, así puedo divertirme este fin de semana.

  3. Jose says:

    Hola:

    Llegaste a modificar el script para su uso con Qmail?.

    Gracias.

Leave a Reply