Пару дней назад, по чистой случайности заметил на одном из серверов подозрительную активность. А именно, кто-то брутил pop3 авторизацию, атака шла с одного ip поэтому я забанил его, написал владельцу подсети и благополучно забыл об этом..
Делается это такой командой:
# iptables -I INPUT -s 255.255.255.255 -j DROP
где, 255.255.255.255 - ip адрес атакующего.
Сегодня решил проверить как там обстоят дела.. К сожалению, атакующий не успокоился, сменил ip и продолжил свое черное дело..
В логах dbmail-а, были вот такие записи:
... Aug 04 14:24:02 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [christopher] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:02 mysite.com dbmail-pop3d[16280]: Error:[pop3] pop3.c,pop3(+407): user [client] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:03 mysite.com dbmail-pop3d[16280]: Error:[pop3] pop3.c,pop3(+407): user [clayton] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:03 mysite.com dbmail-pop3d[16246]: Error:[pop3] pop3.c,pop3(+407): user [chuck] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:04 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [client] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:04 mysite.com dbmail-pop3d[16246]: Error:[pop3] pop3.c,pop3(+407): user [chuck] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [clayton] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[10498]: Error:[pop3] pop3.c,pop3(+407): user [chuck] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[10492]: Error:[pop3] pop3.c,pop3(+407): user [christopher] coming from [255.255.255.255] tried to login with wrong password Aug 04 14:24:05 mysite.com dbmail-pop3d[16246]: Error:[pop3] pop3.c,pop3(+407): user [clayton] coming from [255.255.255.255] tried to login with wrong password123 ...
Т.к. я в первую очередь программист, то решил написать скрипт, который будет парсить лог и банить атакующих. Это сделать было просто, т.к. у всех кто пользовался почтой на этом сервере, были постоянные и известные мне ip адреса, поэтому я мог применить фильтрацию "забанить всех, кроме разрешенных".. Через пол часа был готов простенький скрипт:
<?php class cfg { public static $root_dir='./'; // список разрешенных ip public static $allowed_ips = array( 'ВАШ.IP.АДР.ЕС', ); // путь к логу dbmail public static $dbmail_log_path = '/var/log/dbmail/dbmail.err'; // кол-во строк с конца которое будет анализироваться public static $dbmail_read_last_lines_cnt = 500; // если встретится в выборке кол-во ip больше чем // установлено тут, ip будет считаться атакующим public static $ban_if_found_more_than = 5; } // ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cfg::$root_dir = dirname(__FILE__).'/'; $malefactor_ips = dbmail_get_malefactor_ips(); $banned_ips = get_banned_ips(); $banned_ips = combine_banned_and_malefactor($banned_ips, $malefactor_ips); file_put_contents(cfg::$root_dir.'banned_ip.txt', implode(PHP_EOL,$banned_ips)); if ($banned_ips) { $ipt_banned_ips = ipt_get_banned_ips(); foreach ($banned_ips as $ip) { if (!in_array($ip, $ipt_banned_ips)) { ipt_ban_ip($ip); echo 'IP: '.$ip.' banned'.PHP_EOL; } } } $blocked_ips = ipt_get_banned_ips(); echo '====================================='.PHP_EOL; echo 'IP\'s blocked in iptables:'.PHP_EOL; foreach ($blocked_ips as $ip) echo '-> '.$ip.PHP_EOL; echo '====================================='.PHP_EOL; // ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; function mexec($cmd) { $result = exec("$cmd 2>&1", $out, $err); return array( 'result' => $result, 'out' => $out, 'err' => $err, ); } function dbmail_get_malefactor_ips() { $malefactor = array(); $cmd = 'tail -n '.cfg::$dbmail_read_last_lines_cnt.' '.cfg::$dbmail_log_path; $res = mexec($cmd); if (is_array($res['out'])) { foreach ($res['out'] as $line) { if (preg_match('~user.+\[(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\].+tried\sto\slogin~', $line, $regs)) { $ip = $regs[1]; if (in_array($ip, cfg::$allowed_ips)) continue; if (!isset($malefactor[$ip])) $malefactor[$ip] = 0; $malefactor[$regs[1]]++; } } } foreach ($malefactor as $ip=>$cnt) if ($cnt<cfg::$ban_if_found_more_than) unset($malefactor[$ip]); return $malefactor; } function get_banned_ips() { $banned_ips = array(); if (file_exists(cfg::$root_dir.'banned_ip.txt')) { $banned_ips = file(cfg::$root_dir.'banned_ip.txt'); } return $banned_ips; } function combine_banned_and_malefactor(array $banned_ips, array $malefactor_ips) { foreach ($malefactor_ips as $ip=>$cnt) { if (!in_array($ip, $banned_ips)) $banned_ips[] = $ip; } foreach ($banned_ips as $k=>$ip) if (!trim($ip)) unset($banned_ips[$k]); return $banned_ips; } function ipt_get_banned_ips() { $banned_in_iptables = array(); $cmd = 'iptables -L INPUT -v -n'; $res = mexec($cmd); if (is_array($res['out'])) { foreach ($res['out'] as $line) { // pkts bytes target prot opt in out source destination // 0 0 DROP all -- * * 12.232.164.2 0.0.0.0/0 if (preg_match('~DROP.+\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+0\.0\.0\.0/0~', $line, $regs)) { $ip = trim($regs[1]); $banned_in_iptables[$ip] = $ip; } } } return $banned_in_iptables; } function ipt_ban_ip($ip) { $cmd = 'iptables -I INPUT -s '.$ip.' -j DROP'; $res = mexec($cmd); }
Вкратце, скрипт работает так: берет из лога последние 500 строк, ищет в них ip адреса по маске, далее если ip адрес встретился больше чем 5 раз за выборку, он банится с помощью iptables.
Такой скрипт надо поставить в крон от рута на запуск раз в 15 минут, и проблема с атакующими решена. Во всяком случае, от брутфорса защита будет работать отлично.
Разумеется, сам скрипт можно быстро переписать вообще под любой анализ логов и любое действие, принцип один и тот же. Именно поэтому я его и выложил здесь.
Поигравшись чуть чуть со своим скриптом, я вспомнил, что у меня на сервере установлен fail2ban, и он уже успешно защищает ssh и ftp от всяких слоупоков. Я никогда раньше не писал фильтры для fail2ban, однако покурив ман и посмотрев заготовленные конфиги, за 5 минут написал правило для всё тех же действий - выдачу банов атакующим. Итак, первое что нужно сделать, это создать файл фильтра:
# nano /etc/fail2ban/filter.d/dbmail.conf
В него записываем следующее:
# Fail2Ban configuration file for dbmail # [Definition] # Option: failregex # Notes.: regex to match the password failures messages in the logfile. # Values: TEXT # failregex = user \[.+\] coming from \[<HOST>\] tried to login with wrong password$ # Option: ignoreregex # Notes.: regex to ignore. If this regex matches, the line is ignored. # Values: TEXT # ignoreregex = (127\.0\.0\.1|localhost)
Тут есть всего два интересующих нас параметра:
- failregex - регулярка, для поиска IP адресов. IP адрес заменяется на директиву <HOST>
- ignoreregex - регулярка позволяющая исключить некоторые строки по подстроке
Если Вам понадобится что-то посложнее, посмотрите другие фильтры в папке:
/etc/fail2ban/filter.d/
Теперь можно протестировать наш фильтр на конкретном лог файле, делается это так:
# fail2ban-regex /var/log/dbmail/dbmail.err /etc/fail2ban/filter.d/dbmail.conf
Если все ок, нам покажут простыню текста, типа этого:
Running tests ============= Use regex file : dbmail.conf Use log file : /var/log/dbmail/dbmail.err Results ======= Failregex |- Regular expressions: | [1] user \[.+\] coming from \[<HOST>\] tried to login with wrong password$ | `- Number of matches: [1] 142 match(es) Ignoreregex |- Regular expressions: | `- Number of matches: Summary ======= Addresses found: [1] 255.255.255.255 (Sun Aug 04 14:24:02 2013) 255.255.255.255 (Sun Aug 04 14:24:02 2013) ... 255.255.255.255 (Sun Aug 04 14:29:27 2013) 255.255.255.255 (Sun Aug 04 14:29:27 2013) Date template hits: 343 hit(s): MONTH Day Hour:Minute:Second 0 hit(s): WEEKDAY MONTH Day Hour:Minute:Second Year 0 hit(s): WEEKDAY MONTH Day Hour:Minute:Second 0 hit(s): Year/Month/Day Hour:Minute:Second 0 hit(s): Day/Month/Year Hour:Minute:Second 0 hit(s): Day/Month/Year Hour:Minute:Second 0 hit(s): Day/MONTH/Year:Hour:Minute:Second 0 hit(s): Month/Day/Year:Hour:Minute:Second 0 hit(s): Year-Month-Day Hour:Minute:Second 0 hit(s): Day-MONTH-Year Hour:Minute:Second[.Millisecond] 0 hit(s): Day-Month-Year Hour:Minute:Second 0 hit(s): TAI64N 0 hit(s): Epoch 0 hit(s): ISO 8601 0 hit(s): Hour:Minute:Second 0 hit(s): <Month/Day/Year@Hour:Minute:Second> Success, the total number of match is 142 However, look at the above section 'Running tests' which could contain important information.
Когда мы убедились что регулярка работает, добавляем наш фильтр в конфиг fail2ban-а:
# nano /etc/fail2ban/jail.conf
Нужно добавить такой текст в конец:
[dbmail] enabled = true port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s filter = dbmail bantime = 86400 logpath = /var/log/dbmail/dbmail.err maxretry = 3
Параметры описывать не буду, по их названиям и так все ясно, да и гугл работает.
После этих изменений перезапускаем fail2ban:
# service fail2ban restart
в моем случае в логе (/var/log/fail2ban.log), сразу появилась запись:
2013-08-04 14:29:27,877 fail2ban.actions: WARNING [dbmail] Ban 255.255.255.255
Что говорит о том, что атакующий забанен.
Как показывает практика, любую задачу можно решать разными способами. Изобретать велосипеды, конечно, занимательно, но намного дольше чем использовать готовые инструменты.