Использование базы данных лога Mikrotik для пресечения брутфорса

habr.png

Добрый день.

В предыдущей публикации я рассказывал как, легко и непринужденно, можно настроить сбор метаданных сетевого трафика на маршрутизаторах Микротик в базу данных.

Теперь настало время научить наш сервер делать элементарный анализ получаемых данных и отправлять команды обратно.

Цель: Динамическое управление правилами фаервола Микротик для пресечения сетевых атак с перебором пароля.

Средства: Свежий дистрибутив Linux с rsyslogd v8, crond, СУБД mariadb и собственно сам маршрутизатор Микротик.

Механика: С помощью назначенного задания, выполняется SQL запрос в БД с накопленными и пополняемыми данными трафика и возвращает список исходящих ip-адресов, запускаемый кроном bash скрипт формирует команды Микротика и с помощью ssh соединения, пополняет список адресов для имеющихся правил блокировки.
Речь пойдет о защите открытых TCP портов. Это могут быть входящие на Микротик и пробрасываемые в локальную сеть порты.

Для начала обозначим где могут быть слабые места:

  • Управляющие протоколы маршрутизатора ssh, telnet, web, winbox
  • Почтовые службы smtp, pop, imap
  • Любые веб сервисы предоставляемые наружу
  • Удаленный рабочий стол MS RDP, VNC и т.д.
  • Что-либо другое, на ваше усмотрение


Пишем запрос SQL для поиска брутфорсера

В нашей организации есть терминальные серверы открытые наружу по не приоритетным портам.
В DNAT Микротика я включил логгирование необходимых правил добавив префикс RDP_DNAT. По этому префиксу мы и будем производить поиск:

MariaDB [traflog]> select src,dport,count(dport) as 'попытки подключения' from traffic where datetime>now() - interval 1 day and logpref='RDP_DNAT' group by src having count(dport)>50;
+-----------------+-------+---------------------------------------+
| src             | dport | попытки подключения                   |
+-----------------+-------+---------------------------------------+
| 185.156.177.58  | 12345 |                                   118 |
| 185.156.177.59  | 12345 |                                   267 |
| 193.238.46.12   | 12345 |                                   318 |
| 193.238.46.13   | 12345 |                                   319 |
| 193.238.46.99   | 12345 |                                   342 |
| 194.113.106.150 | 12345 |                                    67 |
| 194.113.106.152 | 12345 |                                   167 |
| 194.113.106.153 | 12345 |                                   190 |
| 194.113.106.154 | 12345 |                                   192 |
| 194.113.106.155 | 12345 |                                   190 |
| 194.113.106.156 | 12345 |                                   216 |
| 194.113.106.158 | 12345 |                                   124 |
+-----------------+-------+---------------------------------------+
12 rows in set (0.06 sec)


Этот запрос показывает ip адрес (с которого идет атака), порт на который происходит подключение (номер порта изменен) и количество попыток подключения, с предварительной группировкой по src и выборкой строк, с количеством попыток более 50 за прошедшие, от текущего момента, сутки.

В моем случае, эти адреса можно смело банить, так как количество подключений у «хороших» клиентов меньше, не более 5–10 в сутки с одного ip.

Запрос работает нормально, быстро, но он длинноват. Для дальнейшего использования я предлагаю сделать представление (view), что бы в будущем меньше копипастить:

MariaDB [traflog]> create or replace view rdp_brute_day as select src, dport, count(dport) from traffic where datetime>now() - interval 1 day and logpref='RDP_DNAT' group by src having count(dport)>50;
Query OK, 0 rows affected (0.23 sec)


Проверим как работает вьюшка:

MariaDB [traflog]> select src,count(dport) from rdp_brute_day;
+----------------+--------------+
| src            | count(dport) |
+----------------+--------------+
| 185.156.177.58 |           11 |
+----------------+--------------+
1 row in set (0.09 sec)


Отлично.

Добавляем пользователя Микротик с авторизацией по ключу dsa

В консоли linux генерируем ключ dsa, под пользователем, от имени которого будет запускаться назначенное задание, я делал из под root:

root@monix:~# ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/root/.ssh/id_dsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_dsa.
Your public key has been saved in /root/.ssh/id_dsa.pub.
...


Passphrase назначать не надо. Публичный ключ /root/.ssh/id_dsa.pub копируем на Микротик любым доступным способом. Я вывел его командой cat, скопировал текст из окна putty в текстовый файл, сохранился и перетащил в окно winbox files.

Не знаю почему, но при выполнении следующих операций через интерфейс winbox что-то пошло не так. При подключении с сервера через ssh, Микротик запрашивал у меня еще и пароль. После того, как удалил созданного пользователя и выполнил все операции через консоль, подключение по dsa заработало. Делал примерно, как описано тут.

В общем я получил желанный вход без пароля по ключу dsa и выполнил проверочную команду:

root@monix:/# ssh rsyslogger@192.168.0.230 /system resource print
             uptime: 2w1d5h22m43s
            version: 6.43.2 (stable)
...


Хорошо.

Пишем bash скрипт

Скрипт получился не сложный:

mikrotik_cmd_list(){
        brute_src_list=$(mysql --skip-column-names traflog  -e 'select src from rdp_brute_day')
        for src in $brute_src_list
                do
                echo "ip firewall address-list add address=$src list=rdp_banlist timeout=1d"
                done
        }

mikrotik_cmd_list | ssh rsyslogger@192.168.0.230


Для того чтобы передать все команды в рамках одного ssh соединения, мне понадобилось описать функцию mikrotik_cmd_list (), в которой сначала выполняется запрос с сохранением ip адресов в переменную brute_src_list, далее в цикле эта переменная последовательно формирует команды для Микротика. После вызова функции, вывод направляется через трубу в ssh.

Не забываем закрыть права доступа к скрипту всем кроме root и делаем файл исполняемым.
Команда которую генерирует скрипт, добавит ip адрес в rdp_banlist на 1 день, по истечении этого времени он сам удалится из списка. Если хотите оставить его навсегда, уберите опцию timeout.

Добавляем правило в фаервол

Я придумал два варианта, как использовать список rdp_banlist:

Вариант первый: добавить список rdp_banlist с восклицательным знаком в правила NAT имеющие префикс RDP_DNAT.

add action=dst-nat chain=dstnat comment="..." dst-address=1.2.3.4 dst-port=12345 log=yes log-prefix=RDP_DNAT protocol=tcp src-address-list=\
    !rdp_banlist to-addresses=192.168.200.181 to-ports=3389


Примерно так. То есть днатим все, кроме того, что есть в rdp_banlist.

В этом варианте есть плюс и минус.

Плюс в том, что подключения тут же прекратятся.

Минус в том, что больше этот ip не будет попадать в БД traflog и по истечении суток, когда пройдет таймаут хранения в блэклисте, он снова начнет гадить.

Вариант второй: добавить список rdp_banlist с восклицательным знаком в правило firewall цепочки forward, где мы разрешаем прохождение трафика на TCP 3389, аналогично тому, как это сделали в первом способе.

add action=accept chain=forward comment="..."  dst-port=3389 log=yes log-prefix=ACCEPT_RDP protocol=tcp src-address-list=\
    !rdp_banlist to-ports=3389


Примерно так. Разрешаем все, кроме того, что в банлисте.

Тут тоже плюс и минус.

Плюс. В БД traflog будут продолжать сыпаться логи с префиксом RDP_DNAT, по которым мы определяем признак атаки. В результате, когда закончится таймаут бана определенного хоста, продолжающего попытки брутфорса, он вновь будет добавлен в банлист после очередного запуска назначенного задания.

Минус в том, что он продолжает гадить в таблицу DSTNAT, каждым своим подключением создавая новую запись, пусть и временную.

В общем решение за вами, я выбрал оба :) (на самом деле, в таком случае работает только первый), так как второй у меня был включен раньше и механика там была другая, основанная на последовательной записи в списки stage1, stage2, stage3, banlist… ну вы поняли. Старый и не очень надежный фокус, может легко забанить «хороших» клиентов и при этом пропустить «плохих», вежливо расчитавших таймаут stage1.

Назначенное задание crontab

Осталось добавить назначенное задание в кронтаб:

root@monix:/root# echo '12 *    * * *   root    /usr/share/traflog/scripts/rdp_brute.sh >/dev/null 2>&1' >> /etc/crontab


Такая запись будет запускать скрипт каждый час в 12 минут.

Надо признаться, я только сегодня закончил работу над этой механикой и с большой долей вероятности, что-то может пойти не так. По обстоятельствам буду дополнять и исправлять ошибки. Хочется пить спать спокойно в новогодние праздники, потому и тороплюсь закончить.
Вот пожалуй и все.

Всем спасибо за внимание и с наступающим Новым Годом!

Список литературы:

Документация по mysql
Документация по Mikrotik firewall
Спасибо Андрею Смирнову за статью про подключение по ключу dsa.

© Habrahabr.ru