The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]



Индекс форумов
Составление сообщения

Исходное сообщение
"Раздел полезных советов: Автоматизация борьбы со спамботами ..."
Отправлено auto_tips, 24-Авг-09 11:29 
Администраторы больших сетей регулярно получают жалобы на исходящий из них спам. Получив жалобу,
можно найти виновного и устранить проблему. Но нехорошо узнавать о таких делах только от чужих людей.
Спамеров вполне можно вычислить самому, и принять меры раньше, чем кто-то пострадает.

Чисто интуитивно достаточно запустить tcpdump на исходящем канале, и посмотреть, какие IP-адреса больше в
сего посылают SYN-пакетов на 25 порт.

  tcpdump -ni bridge0 'tcp[tcpflags] & tcp-syn != 0 and dst port 25'

В идеале это был бы ваш SMTP-сервер. В действительности вы увидите в основном IP-адреса простых пользователей,
"одержимых бесами". Дело в том, что обычный спамбот создает SMTP-соединений во много раз больше
вашего почтового сервера. Причем с разными IP-адресами.

В наши дни все нормальные почтовые сервера имеют PTR-запись DNS. Простые клиенты, напротив,
такой записи обычно не имеют. Если почтовый сервер может устанавливать SMTP-соединения
со множеством IP-адресов, то для простого клиента эта величина обычно не более 1-2 (например,
пользование удаленными SMTP-серверами с лаптопа).

Исходя из этого, можно автоматически определять спамботов в нашей сети, и блокировать их (например,
средствами IPFW). В качестве критерия оценки мы установили максимальное количество IP-адресов,
с которыми каждый хост может общаться по SMTP в течение 5 минут. Для хостов с реверсной записью DNS это 200,
для хостов без реверсной записи - 20. Если хост превышает этот лимит, он попадает в черный список на сутки.

Нами написан Perl-скрипт, запускающий tcpdump на 5 минут, и анализирующий результаты.
Хосты-нарушители заносятся в таблицу PostgreSQL для удобства персонала, и затем помещаются
в таблицу IPFW. Правила файрвола блокируют все соединения на 25 порт с хостов, находящихся в данной таблице.

Текст скрипта:

       #!/usr/local/bin/perl
       # dimss@stalin.lv 2009-08-20

       my $colltime = 300;    # How long to collect data
       my $rev_limit = 200;   # Remote IP limit for hosts with PTR record
       my $norev_limit = 20;  # Remote IP limit for hosts without PTR record
       my $table_no = 1; # Number of IPFW table

       use strict;
       use warnings;

       use POSIX ':signal_h';
       use Socket;
       use DBI;

       #
       # Collect data
       # Run tcpdump for some time
       #

       my $conns = {};

       my $mask = POSIX::SigSet->new( SIGALRM );
       my $action = POSIX::SigAction->new(
               sub {
                       system("killall tcpdump");
               },
               $mask
       );
       my $oldaction = POSIX::SigAction->new();
       sigaction(14, $action, $oldaction);

       alarm($colltime); # Run tcpdump for this amount of time
       open(T, "/usr/sbin/tcpdump -ni bridge0 'tcp[tcpflags] & tcp-syn != 0 " .
                       "and dst port 25' " .
                       "2>/dev/null |") or die;

       while(<T>){
               /\ ((\d+\.){3}\d+).+?((\d+\.){3}\d+)/;
               my $locip = $1;
               my $remip = $3;
               #print "$locip $remip\n";
               $conns->{$locip} ||= {};
               $conns->{$locip}->{$remip} ||= 1;
       }
       alarm(0);
       sleep(1);

       #
       # Analyze connections
       #


       $mask = POSIX::SigSet->new( SIGALRM );
       $action = POSIX::SigAction->new(
               sub {
                       die("Reverse lookup took too long");
               },
               $mask
       );
       $oldaction = POSIX::SigAction->new();
       sigaction(14, $action, $oldaction);

       alarm(60); # Reverse DNS lookups must complete within this period

       my %concount;
       my %bots;

       for my $locip (keys(%$conns)){
               #print("Loc IP: $locip\n");
               my $cnt = 0;
               for my $remip (keys(%{$conns->{$locip}})){
                       #print("  Rem IP: $remip\n");
                       $cnt++;
               }
               $concount{$locip} = $cnt;
       }

       for my $locip (sort {$concount{$b} <=> $concount{$a}} keys(%concount)){
               if($concount{$locip} > $norev_limit){
                       my $ip = inet_aton($locip);
                       my $name = gethostbyaddr($ip, AF_INET);
                       if($concount{$locip} > ($name ? $rev_limit : $norev_limit)){
                               #print("$locip: $concount{$locip} (" . ($name || '') . ")\n");
                               $bots{$locip} = $name;
                       }
               }
       }

       alarm(0);

       #
       # Update database
       #

       $mask = POSIX::SigSet->new( SIGALRM );
       $action = POSIX::SigAction->new(
               sub {
                       die("Database timeout");
               },
               $mask
       );
       $oldaction = POSIX::SigAction->new();
       sigaction(14, $action, $oldaction);

       alarm(15);

       my $dbh = DBI->connect("dbi:Pg:host=db.host.tld;dbname=spambot",
               "spambot", "passwd");
       if(!$dbh) {
               die("Cannot connect to DB");
       }

       my $query;
       my $sth;
       my $rv;
       for my $botip (keys(%bots)){
               #print "$botip\n";
               my $qname = $dbh->quote($bots{$botip});
               $query = "
                       update spambot_hosts set
                               active_till = now() + '1 days',
                               hostname = $qname
                       where ip = '$botip'
               ";
               $sth = $dbh->prepare($query);
               $rv = $sth->execute();
               if($rv != 1){
                       $query = "
                               insert into spambot_hosts
                               (ip, hostname, active_till)
                               values
                               ('$botip', $qname, now() + '1 days')
                       ";
                       $sth = $dbh->prepare($query);
                       $sth->execute();
               }
       }
       $query = "
               delete from spambot_hosts
               where active_till < now()
       ";
       $sth = $dbh->prepare($query);
       $sth->execute();

       #
       # Get full list of banned hosts from DB
       #

       $query = "
               select ip from spambot_hosts
       ";
       $sth = $dbh->prepare($query);
       $sth->execute();

       my %dlist;
       while(my $row = $sth->fetchrow_hashref){
               $dlist{$row->{ip}} = 1;
       }

       undef $dbh;
       alarm(0);

       #
       # Read list of banned hosts from kernel (ipfw) table
       #

       my %klist;
       if(open(KTABLE, "/sbin/ipfw table $table_no list|")){
               while(<KTABLE>){
                       chomp();
                       s/\/32//;
                       my ($prefix, $queue) = split();
                       $klist{$prefix} = 1;
               }
               close(KTABLE);
       }

       #
       # Update kernel table
       #

       for my $host (keys(%dlist)){
               unless($klist{$host}){
                       #print "Add $host\n";
                       system("/sbin/ipfw table $table_no add $host");
               }
       }

       for my $host (keys(%klist)){
               unless($dlist{$host}){
                       #print "Remove $host\n";
                       system("/sbin/ipfw table $table_no delete $host");
               }
       }

       exit;

Из /etc/ipfw.rules:

   #....
   ipfw -q add deny tcp from "table(1)" to any 25
   #....

У нас скрипт работает на шейпере, обслуживающем только заграничный канал. Местный трафик им не анализируется
и не блокируется. При этом большая часть "хорошей" почты ходит именно по местным каналам, но спамеры
спамят в основном по заграничному каналу. Так что лимиты вам наверняка придется подстроить под себя.
Возможно, вам придется также реализовать "белый список", если у вас есть очень активные SMTP-сервера.

На момент написания статьи в "расстрельном списке" несколько десятков хостов. Жалоб за истекшие сутки
не поступало, хотя раньше их было множество.

URL:
Обсуждается: https://www.opennet.ru/tips/info/2146.shtml

 

Ваше сообщение
Имя*:
EMail:
Для отправки ответов на email укажите знак ! перед адресом, например, !user@host.ru (!! - не показывать email).
Более тонкая настройка отправки ответов производится в профиле зарегистрированного участника форума.
Заголовок*:
Сообщение*:
 
При общении не допускается: неуважительное отношение к собеседнику, хамство, унизительное обращение, ненормативная лексика, переход на личности, агрессивное поведение, обесценивание собеседника, провоцирование флейма голословными и заведомо ложными заявлениями. Не отвечайте на сообщения, явно нарушающие правила - удаляются не только сами нарушения, но и все ответы на них. Лог модерирования.



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру