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

    Добрый день.

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

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

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

    Средства: Свежий дистрибутив 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.
    Поделиться публикацией

    Комментарии 35

      +1
      Это работа ради работы?
      В Wiki Mikrotik есть статья «Bruteforce login prevention» как сделать ip firewall filter для защиты от брутфорса.
      Или я чего-то не понял?
        0
        В какой-то степени да, это скорее работа ради опыта, но это не умоляет ценности.
        Я не прав в том, что опустил эту деталь в самый низ и не сказал об этом сразу.
        Я использую механику защиты от брутфорса предлагаемую Микротиком, но мне не нравится как она работает.
        Во первых: брутфорсер бывает хитрый, он высчитывает таймаут stage1, и делает так что его ip не попадают в блэклист.
        Во вторых: что бы эта механика работала лучше я бы сделал более трех этапов проверки перед баном(штук 6 или даже 9, чем больше тем лучше), но это нагромождение.
        Поясню, интересный факт, при отправке почты через мобильные сети, по какой-то причине последовательно прилетает 3 SYNа, то есть фактически устанавливается 3 соединения и ip src сразу попадает аж на третий этап(заключительный этап), который держит его там, допустим, 2 минуты. При попытке отправить еще что-нибудь, он попадает в банлист. Это неприятно, особенно для директора с его iphone.
          0
          И да, забыл сказать.
          Цифры полученные в первом SQL запросе (с которого начиналась статья) были при уже включенном «Bruteforce login prevention».
          0
          По-хорошему, количество записей в банлисте нужно ограничить. Возможна ситуация когда добавится много правил и будет тормозить маршрутизация, либо тормозить процесс изменения этого списка.
            0
            Возможно. Есть идеи как отследить торможение маршрутизации и процесс добавления в список?
            Пока банлист держит запись сутки, буду наблюдать за динамикой появления новых записей.
              0
              Через систему мониторинга отслеживать среднюю загрузку CPU (это можно отследить по SNMP), но это только примерная будет оценка, если выше 60-70% тогда начинайте искать проблему.
              С изменением банлиста — логировать на график время работы вашего обновляющего скрипта, а также сделать триггер на время отработки скрипта, если оно раза в три больше обычного то паниковать.
            0

            В крайней версии openssh ключи DSA объявлены deprecated.

              0
              Что посоветуете? RSA?
                0

                Я бы посоветовал не использовать протоколы, которые брутфорсить, для удаленного доступа к внутренним ресурсам :). Радикально, да. VPN с авторизацией по ключам, и пусть подбирают пароль root.

              0
              Вам лучше сделать обратную фишку: отследить длительность сессии и легитимные адреса подключающиеся к вашему RDP и уже получив список легитимных адресов и длительности автоматически корректировать время интервала блокировки нарушителей.
                0
                Это будет уже искусственный интеллект )
                Как предлагаете отслеживать длительность сессии? Есть вариант вычислять время между приходом SYN и FIN, с соответствующими src:sport dst:dport, но FIN может и не прилететь.
                Автоматически корректировать таймаут, да, идея хорошая, пожалуй нарушителей можно вычислить и по другим признакам, обычно атаки проходят в промежутках времени и повторяются в скором времени. Надо понаблюдать.
                  0
                  Ну в том же микротике отследить длительность сессии можно и так (буду писать для sstp, его просто и быстро накидать на лабораторном столе, а то рабочие столы у меня не подключаются без обёртки в виде IKEv2 или ipsec/vpn):
                  1) сначала отлавливаем соединение с состоянием новое и помечаем соединение
                  2) далее отлавливаем соединение в состоянии установлено и пометкой созданной шагом выше, сохраняем адрес источника.
                  /ip firewall mangle
                  add action=mark-connection chain=prerouting connection-state=new dst-port=443 new-connection-mark=«SSTP connect» passthrough=yes protocol=tcp
                  add action=add-src-to-address-list address-list=sstp-login address-list-timeout=10m chain=prerouting connection-mark=«SSTP connect» connection-state=established dst-port=\
                  443 protocol=tcp

                  Время можно поставить и меньше, но тогда будет увеличена нагрузка на процессор микротика (у меня это подопытное крутится в виде виртуалки на отдельном лабораторном сервере, поэтому я могу его очень сильно нагружать).
                  Если соединение установлено, то время существования адреса постоянно будет близко к заданному вами значению (в моём примере это получается около 9 минут, связано с временем удержания соединения в клиенте sstp соединения равному 60 секунд).

                  Таким образом вы получите адреса подключившиеся к вашему с достаточно длительным сроком работы (отдельным сборщиком логов собираем раз в 10 минут все адреса из таблицы микротика в БД и там уже считаем длительность их подключения к вашему сервису, хотя это можно прокрутить и на микротике, но после вчерашнего корпоратива голова как-то не варит написать полное решение ;) ).
                  0
                  Для RDP у меня есть другая идея.
                  Все тот же Rsyslog должен собирать логи windows, отправляемые, например, с помощью evtsys(связка мне уже знакома, я использовал её ранее). Но для хранения тут уже понадобится mongodb, логи windows по своему формату не очень читаемы. До прикручивания монги я еще не добрался, но руки чешутся. При таком решении, анализ можно будет проводить уже на уровне аудита системы и открываются широкие перспективы.
                0
                Простите великодушно, но задача из разряда «как приличной девушке ходить ночью топлесс, отгоняя приставал газовым баллончиком. выбираем баллончик». 21 век. В интернете повсеместно ssl, любая китайская мыльница умеет в l2tp/ipsec, у микротиков vpn на любой вкус, а вы о брутфорсе-пробросе rdp. Не надо так
                  0
                  Хе-хе, прикольная аналогия.
                  Я рассматривал этот вариант первым делом, как только приступил к своим обязанностям.
                  Но понял, что мои секьюрные рвения никто не оценил. Так как прикручивание клиентов через ipsec/l2tp несет за собой много неудобств для самих пользователей и как следствие головную боль админу. Это в первую очередь, но положим мы справились. А теперь представьте что вы приехали в гости, и вам нужно срочно подключиться по RDP к вашему рабочему серверу и выполнит некоторые действия в 1С, делать на смартфоне это мука, а что бы подключится с компа, вам нужно сначала настроить ipsec/l2tp ) но вы не админ, или админ но у вас нет ключа-сертификата или PSK. И снова головная боль админу, а он катается на лыжах где-нибудь в лесу :)
                  Про маршрутизацию в туннель и совместимость адресного пространства дома и работы я даже и говорить не буду, это жопа жопная для простого человека(не админа).
                    0
                    вы приехали в гости, и вам нужно срочно подключиться по RDP к вашему рабочему серверу

                    Не нужно такого хотеть.

                      0
                      Сейчас есть автоматические вирусы, которые заходят в 1С по RDP и шифруют базу. Ни с телефона, ни с гостевого компьютера никогда не делайте этого.
                        0
                        Полностью поддержу, что нужно ходить только через vpn.
                        Заранее можно озаботиться максимально широким спектром настроек для различных устройств (заскриншотить ключевые моменты и добавить в доку). И сделать для всех потенциальных пользователей аккаунты (вероятно, в пределе — для всех) и сервис самовосстановления данных по смс.
                        В остальном — rdp наружу — это практически 100% дыра в безопасности. Т.к. может быть и уязвимость в tcp стеке рдп-сервера, так и в протоколе rdp

                        А плодить jump-терминалы — так себе идея

                      0
                      Все не так сложно с впн. Если время будет — напишу статью как это нормально сделать, с фэйловером туннелей, разделением доступа, win/mac-удаленщиками и прочими глупостями. Пока надо добить черновик про бэкап postgresql-баз 1с средствами bacula. Не переключайтесь.

                      А по теме простое правило. Выставляйте что-то через dnat только в крайнем случае.
                        0
                        1. Algo VPN разворачивается за минуты.
                        2. Блок «белого» адресного пространства для п. 1 решает все вопросы несовместимости адресов.
                        3. Доступ к RDP только с заданного в п. 2 блока адресов решает все вопросы с безопасностью протокола RDP.
                        ps: это не отменяет целенаправленной атаки с подменой адреса, но это уже другой разговор.
                        0
                        И кстати хоть в публикации идет речь про RDP, но это все лишь частный случай, просто пример предоставленный для демонстрации плюшек получаемых при сборе и анализе логов маршрутизатора.
                        Широкий простор для мечты и для жизни,
                        Логи в БД открывают для нас…
                          0

                          Это вопрос философский теперь. Работает ли вообще бан по ip?
                          Учитываем, что


                          • умные брутеры уже давным-давно пользуются широкими пудами адресов (бот-неты, провайдеры с динамическим ip, в конце-концов, облачные провайдеры с виртуалками с эфемерными ip)
                          • потихонечку — распространение ipv6 (не напасешься ОЗУ и проца, чтобы лочить эти адреса)
                          • провайдеров, предоставляющих "серые" ip клиентам и использующие nat для выхода в интернет, а таковых на российском рынке большинство (что мобильные операторы, что стационарные — вроде Ростелеком).
                            Представьте себе радость генерального, когда он с мобильного телефона не сможет зайти на сервер, потому что случайно пошарил адрес с брутфорсером ?!?!?!
                            0
                            | 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 |

                            Наглядный пример широкого пула адресов. Возможно и шире, надо подумать как за этим следить.

                            Серые адреса да, проблема, но я пока не видел, что бы с них прилетали подобные атаки.
                              0
                              Работает. На почтовике в логе:
                              86.47.96.237 [01A8] 12:17:57 >>> 535 5.7.8 Authentication credentials invalid
                              выясняем кто:
                              PEER_AS | IP | BGP Prefix | AS Name
                              174 | 86.47.96.237 | 86.40.0.0/13 | COGENT-174 — Cogent Communications, US

                              В ФВ блочим 86.40.0.0/13.
                              Работает автоматически, ибо достали.
                              Очен редко бывают жалобы о том, что почта перестала ходить.., тогда выясняем откуда её посылают и делаем исключение на конкретный адрес оставив остальную часть адресов в запрете.
                                0
                                Вот, вот. Была у меня мысль написать в публикации именно про бан при работе с smtp, но побоялся быть банальным. Хотя изначально реализовал именно защиту smtp, аналогичным в способом, только с другими интервалами и счетчиками.
                                  0
                                  Эм… Очень интересно почитать про грамотную реализацию SMTP бана. Опять же — у меня есть подозрения, что ее сделать корректно не получится, без нежелательного отключения крупных почтовиков типа gmail/yandex/mail etc.
                                    +1
                                    Считаю если можно перенаправить логи smtp на связку rsyslog->БД, не сложно будет изловить негодяя подбирающего пароль и отправить в бан.
                                    В моем случае я пока не научился перенаправлять логи Лотус Домино куда либо, да и логи там надо сказать скудные, по сравнению с IceWarp. В Лотусе все логи складываются в свою БД, надо научится к ней подключаться внешними тогда можно будет и анализировать.
                                    Логи почтовика лучше чтения содержимого пакетов, так как шифрованый smtp уже не особо почитаешь. А так на микротике есть возможность читать на лету не шифрованный smtp, я например использую такую связку: если в пакете smtp присутствует фраза AUTH LOGIN то запускается цепочка аналогичная «Bruteforce login prevention».
                                    Мы ведь говорим именно про брут smtp?
                          0
                          Сам бился над подобным вопросом, как лучше банить брутфорсеров, в итоге сильно модифицировал решение с вики микротика.

                          Но любой бан по IP приводит к большому количеству, порой, очень неприятных эффектов: от ложных срабатываний до не срабатывания в тех случаях, когда это необходимо.
                          Причём, это будет в любом случае, а модификация способов определения бан/не бан позволяет только сократить эти эффекты, но исключить их не удастся.

                          Данный же способ — зло, т.к. достаточно представить терминальник, на котором работают 5 человек с одного офиса, а потом, у их провайдера происходит авария и 3-4 обрыва соединения за день… итого, 20 новых подключений за день, IP в бан, а 5 юзеров не смогут работать))
                            0
                            Почему же сразу «зло»? если включить фантазию, то возможности реализации аналитики с помощью SQL и bash сильно выше чем возможности которые дает сам Микротик. Или вы считаете что вам удалось модифицировать «зло» предложенное вендором в менее злое «зло» чем возможно сделать с помощью накопленных данных SQL и всего что можно только запустить на linux?
                            Вопрос блокировки офиса с 5 сотрудниками решается быстро. Три варианта:
                            1 Если они имеют статический ip то просто вносятся в белый лист
                            2 Если нет статики им ставится железка поднимающая туннель
                            3 Если нет денег на новую железку, они звонят мне, говорят что у них не работает и я за пару кликов удаляю их из банлиста.
                            К тому же, редко случается когда у провайдера падает канал пять раз в день, на моей памяти ни разу. Плюс ко всему, представление которым мы выбираем данные из БД можно переписать под свои реалии.

                            По поводу «сократить эти эффекты» согласен. Нет антибиотиков которые бы не убивали микрофлору кишечника. Но слышал, врачи говорят, при внутримышечном введении кишечник страдает меньше.
                              0
                              Давайте честно — port knocking гораздо более надёжное решение, чем бан по ip.
                              Проблема только в том, что придется оборачивать rdp подключение в скрипты.
                              И это нормально можно сделать только на полноценных клиентах (win/lin/mac).
                              Для мобильных клиентов — не знаю.
                              Ещё можно было бы трафик гнать через ssh туннель, но это оставляется возможность брута ssh реквизитов
                            0
                            Cat, буфер обмена, блокнот, ftp с помощью drag'n'drop…
                            Зачем же так сложно, с вашего же линукса scp файла на Микротик. А не заработало у вас скорее всего из-за (виндового) перевода строки.
                              0
                              Навевает анекдот про группу школьников в зоопарке «А слона то я и не заметил.»
                              Нет, это был не перевод строки, ftp на микроте отключен и заблокирован правилами, катнуть, копипастнуть, перетащить быстрее чем включать ftp.
                              Затык, мне кажется, был в процедуре создании пользователя и привязки pub_rsa, когда я его делал через winbox — не получилось, когда создал его через консоль — получилось.
                              0
                              Извиняюсь, не туда ткнул для ответа.
                              Как удалить комментарий?

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое