Как стать автором
Обновить
2505.88
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

Мой MikroTik – моя цифровая крепость (часть 4)

Время на прочтение 11 мин
Количество просмотров 31K

Статья является продолжением первой, второй и третьей частей, посвящённых организации практической безопасности сетей, построенных на оборудовании MikroTik. Ранее были рассмотрены общие рекомендации, безопасность уровней L1, L2 и L3, реализация централизованного логирования. Настало время поговорить про развёртывание IDS и её интеграцию в инфраструктуру RouterOS.

11. Настройка IDS


В качестве системы обнаружения вторжений выберем решение с открытым программным кодом Suricata. Оно умеет работать со следующими прикладными протоколами: http, ftp, smtp, tls, ssh, imap, msn, smb, dce\rpc, dns, nfs, ntp, tftp, ikev, krb5 и dhcp. Организуем режим работы promiscuous, при котором маршрутизатор будет посылать копии пакетов на IDS и фундаментально не влиять на их прохождение. На RouterOS это можно сделать двумя способами.


Посредством зеркалирования информации, передающейся через конкретный порт роутера (для этого необходимо проверить на сайте производителя, умеет ли он так делать). Тип имеющейся микросхемы можно посмотреть так:

/interface ethernet switch print 
Columns: NAME, TYPE, L3-HW-OFFLOADING
  #  NAME     TYPE      L3
  0  switch1  QCA-8337  no

Этот вариант подойдёт, если вы развернёте Suricata внутри вашего LAN на железе, так как должно быть физическое соединение между зеркалируемым портом и его коллектором, тогда:

/interface ethernet switch set switch1 mirror-source=ether2 mirror-target=ether3

Сохранять передающийся трафик можно с помощью Tcpdump. Если такой вариант вас не устраивает, тогда рекомендую использовать арендованный VDS сервер более мощной конфигурации:



Для зеркалирования трафика можно использовать встроенное в RouterOS программное обеспечение Packet-sniffer:

/tool sniffer set filter-interface=WAN filter-stream=yes memory-scroll=no streaming-enabled=yes streaming-server=192.168.15.29

Довольно грубое решение. Пакеты пересылать, разумеется, нужно по шифрованному VPN туннелю, который может быть достаточно узким, и тогда часть пакетов будет теряться. Для зеркалирования MikroTik применяет протокол TZSP, трафик будет обрамляться служебными заголовками и передаваться UDP пакетами, и тут уже без потерь никак. Для более точной настройки можно вместо Packet-sniffer использовать Firewall mangle (в примере отбираются пакеты для Winbox службы и SSH):

/ip firewall mangle
add action=mark-connection chain=prerouting comment="Managment connections" \
dst-port=22,8291 new-connection-mark=\
"Managment connections" passthrough=yes protocol=tcp
add action=mark-packet chain=output comment="Managment packets" \
connection-mark="Managment connections" new-packet-mark=\
"Managment packets" passthrough=yes
add action=sniff-tzsp chain=postrouting comment="Sniffer Managments" \
packet-mark=" Managment packets" sniff-target=192.168.15.29 sniff-target-port=37008

Firewall mangle подробно изучается на курсе MTCTCE (MikroTik Certified Traffic Control Engineer). Скажем только, что гибкость при его настройке достаточно высокая, и умелыми руками можно здорово дифференцировать пакеты. Сохранять передающийся трафик, конечно, можно с помощью Tcpdump, однако наличие служебных заголовков протокола TZSP доставляет хлопот:



Для обратного преобразования зеркалируемого трафика к первоначальному виду можно использовать программы: Trafr (настройка рассмотрена здесь) или Tzsp2pcap:

apt install git libpcap-dev screen tcpreplay
git clone https://github.com/thefloweringash/tzsp2pcap.git
make
make install

После этого создаем виртуальный интерфейс, поднимаем для него MTU, чтобы пакеты не обрезались (IP адрес 192.168.3.254 выбран случайно):

ip link add dummy0 type dummy
ip link set dummy0 mtu 3000
ip link set name eth10 dev dummy0
ip addr add 192.168.3.254/24 dev eth10
ip link set eth10 up

В режиме реального времени сервер будет принимать зеркалируемый трафик, снимать с него служебные заголовки протокола TZSP и ретранслировать его на виртуальный интерфейс eth10:

tzsp2pcap -f | tcpreplay-edit --mtu=3000 --mtu-trunc -i eth10 -

Трансмиссию привели в порядок, приступим к настройке IDS. С параметрами по умолчанию все будет хорошо работать (правила хранятся в /etc/suricata/rules/), не забудем только уточнить домашнюю сеть (192.168.15.9 – IP адрес роутера в VPN):

apt install suricata
vi /etc/suricata/suricata.yaml

HOME_NET: "[192.168.1.0/24,192.168.15.9/32]"
af-packet:
  - interface: eth10

Лог всего трафика, проходящего через Suricata, можно посмотреть так:

tail -f /var/log/suricata/eve.json

{"timestamp":"2021-08-12T07:17:28.408914+0600","flow_id":1497450928422226,"in_iface":"eth10","event_type":"dns","src_ip":"10.0.5.174","src_port":50460,"dest_ip":"1.1.1.1","dest_port":53,"proto":"UDP","dns":{"type":"query","id":39632,"rrname":"dr.habracdn.net","rrtype":"A","tx_id":0}}
{"timestamp":"2021-08-12T07:17:28.548078+0600","flow_id":1410381203911063,"in_iface":"eth10","event_type":"tls","src_ip":"10.0.5.174","src_port":22443,"dest_ip":"178.248.237.68","dest_port":443,"proto":"TCP","tls":{"sni":"habr.com","version":"TLS 1.3","ja3":{}}}

Лог детектированных угроз лежит здесь /var/log/suricata/fast.log. Настало время испытаний.

12. Из IDS в IPS


IDS расшифровывается как Intrusion Detection System – система обнаружения вторжений. IPS (Intrusion Prevention System) – система предотвращения вторжений. На текущий момент наша Suricata анализирует зеркалируемый на нее трафик и, в случае обнаружения угроз, сохраняет их описание в лог. Посмотрим, справляется ли она со своими задачами. Попробуем развернуть bruteforce атаку на SSH службу нашего роутера:

hydra 192.168.15.9 ssh -l admin -P rockyou.txt -s 22 -vv

tail -f /var/log/suricata/fast.log -F
08/16/2021-06:57:38.341563  [**] [1:2001219:20] ET SCAN Potential SSH Scan [**] [Classification: Attempted Information Leak] [Priority: 2] {TCP} 192.168.15.1:37126 -> 192.168.15.9:22

Как мы видим, угроза была детектирована: «Potential SSH Scan». Попробуем выполнить nmap сканирование роутера:

nmap 192.168.15.9
PORT     STATE SERVICE
22/tcp   open  ssh
53/tcp   open  domain
80/tcp   open  http
2000/tcp open  cisco-sccp
8291/tcp open  unknown

Suricata и в этом видит угрозы:

08/17/2021-07:08:38.242647  [**] [1:2010937:3] ET SCAN Suspicious inbound to mySQL port 3306 [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 192.168.15.1:38863 -> 192.168.15.9:3306
08/17/2021-07:08:38.799069  [**] [1:2010936:3] ET SCAN Suspicious inbound to Oracle SQL port 1521 [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 192.168.15.1:38863 -> 192.168.15.9:1521
08/17/2021-07:08:39.617393  [**] [1:2010939:3] ET SCAN Suspicious inbound to PostgreSQL port 5432 [**] [Classification: Potentially Bad Traffic] [Priority: 2] {TCP} 192.168.15.1:38863 -> 192.168.15.9:5432
08/17/2021-07:08:39.761901  [**] [1:2002911:6] ET SCAN Potential VNC Scan 5900-5920 

Теперь наша задача обрабатывать получаемый лог, выделять угрозообразующие IP адреса и блокировать их на MikroTik-е. Для этого уже существует готовое решение: PHP скрипт fast2mikrotik по API добавляет их в /ip firewall address-list. Однако у нас он в итоге так и не заработал, поэтому далее мы представим вариант собственной реализации. Здесь немного отвлечемся от темы. Управлять роутером посредством API это очень заманчиво, так как уже имеется готовый PHP класс.

Активируем API в RouterOS:

/ip service set api disabled=no

И кидаем на маршрутизатор команды, примерно так:

<?php
require('routeros_api.class.php');
header('Content-Type: text/plain');
$API = new RouterosAPI();
 try {
 $API->connect('192.168.15.9', 'admin', 'verySTRONGpassword!!');
 } catch (Exception $e) {
 die('Unable to connect to RouterOS. Error:' . $e);
 }
 $ARRAY = $API->comm("/tool/sniffer/stop");
 $API->disconnect();
?>

Можно, конечно, работать через API по SSL, подготовив заранее сертификаты, чтобы все было безопасно, но мы решили выполнить интеграцию по-другому и пошли своим путем, может, не самым эффективным, но удобным, так как отсутствует необходимость запуска и настройки дополнительных сервисов, таких как база данных, например. Команды будем передавать примерно так, как показано ниже:

sshpass -p verySTRONGpassword!! ssh admin@192.168.15.9 /tool sniffer stop

Полный Bash скрипт под спойлером.

fast2mikrotik.sh
#/bin/sh
#Сюда вбиваем IP адрес нашего MikroTik
MIKROTIK_IP=192.168.15.9
#Логин от него
MIKROTIK_USER=admin
#Пароль:
MIKROTIK_PASS=verySTRONGpassword!!
#Где лежит лог от Suricata
LOG=/var/log/suricata/fast.log
#Файл, необходимый для работы скрипта (будет создан самим скриптом)
INLOG_FILE=/root/test_after_parser.log
declare -A IP_OUR_MASC
#IP address 192.168.15.9, получаемый по VPN (статический)
#IP address 10.0.14.144, получаемый по WAN (динамический)
#Ниже задаются шаблоны соответствия, по которым будет отрабатывать
#скрипт при анализе IP адресов из fast.log. IP_OUR_MASC[0,1]=10. означает,
#что WAN интерфейс получает адрес, формата 10.0.0.0/8
IP_OUR_MASC[0,0]=IPS; IP_OUR_MASC[0,1]=10.; IP_OUR_MASC[0,2]=12
IP_OUR_MASC[1,0]=OVPN; IP_OUR_MASC[1,1]=192.168.15.9; IP_OUR_MASC[1,2]=12
#Имя вашего WAN интерфейса в RouterOS, необходим для получения текущего IP адреса
NAME_WAN_INTERFACE=WAN
#IP адрес IDS сервера
IP_SURICATA_SERVER=192.168.15.29
#Блокировать угрозы будем на 24 часа
BLOCK_TIMEOUT=24h
#Периодичность просмотра лога от Suricata 10 секунд
SLEEP_TIMEOUT=10
#Имя, создаваемого самим скриптом, address-list-а в RouterOS
NAME_BLOCK_LIST=Suricata_rules
IP_SRC_DEFAULT='IP_OUR'
#Сообщения с таким содержанием будем игнорировать
EVENT_NOT_INTEREST='SURICATA TLS invalid handshake message'
EVENT_NOT_INTEREST2='SURICATA TLS invalid record/traffic'
declare -p ARRAY > /dev/null

while [ 1 = 1 ]; do
	#Проверяем, пустой ли лог
	STRINGS_IN_LOG_FILE=$(cat $LOG | wc | awk {'print $1'})
		if [ "$STRINGS_IN_LOG_FILE" -lt 1 ]; then
			echo "Fastlog is empty, sleep $SLEEP_TIMEOUT second..."
			sleep $SLEEP_TIMEOUT
		else
			#Имеем, что лог не пустой. Начинаем с ним работу
			cat $LOG > $INLOG_FILE
			#Очищаем лог Suricata. В этом месте, конечно, можно терять записи, вносимые IDS системой
			#в текущий момент времени, что является допущением текущей реализации
			echo -n '' > $LOG
			#Парсим лог по переменным
			readarray -t ARRAY < $INLOG_FILE
			for LINE in "${ARRAY[@]}"; do
				DATE=$(echo $LINE | awk -F " " '{print $1}' | cut -c1-10)
				TIME=$(echo $LINE | awk -F " " '{print $1}' | cut -c12-26)
				EVENT=$(echo $LINE | awk -F '[**]' '{print $3}' | awk '!($2="")' | cut -c3- | rev | cut -c 3- | rev)
				CLASS=$(echo $LINE | awk -F '[**]' '{print $5}' | cut -c4- | rev | awk '{$1=$2=$3=$4=$5=$6=""; print $0}' | cut -c 8- | rev)
				PRIOR=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $5'} | cut -c 2- | rev)
				PROTOCOL=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $4'} | cut -c 2- | rev | cut -c 2-)
				IP_SRC=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $3'} | rev | cut -d : -f 1)
				IP_SRC_PORT=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $3'} | rev | cut -d : -f 2-)
				IP_DST=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $1'} | rev | cut -d : -f 1)
				IP_DST_PORT=$(echo $LINE | awk -F '[**]' '{print $5}' | rev | awk {'print $1'} | rev | cut -d : -f 2-)
				echo "$DATE $TIME $EVENT $CLASS $PRIOR $PROTOCOL $IP_SRC $IP_SRC_PORT $IP_DST $IP_DST_PORT"

			#Проверяем текущую строку лога на наличие неинтересного для нас сообщения
			echo -n "$EVENT" | grep "$EVENT_NOT_INTEREST" > /dev/null
			if [ $? = 0 ]; then
				echo -e "Find $EVENT_NOT_INTEREST, do nothing..."
			else
				#Проверяем текущую строку лога на наличие неинтересного для нас сообщения
				echo -n "$EVENT" | grep "$EVENT_NOT_INTEREST2" > /dev/null
					if [ $? = 0 ]; then
						echo -e "Find $EVENT_NOT_INTEREST2, do nothing..."
					else
						#Проверяем не попала ли сама IDS в сработку, так как команды управления будут идти по SSH
						#и могут быть восприняты как брутфорс атака
						if [ x"$IP_SRC" = x"$IP_SURICATA_SERVER" ]; then
							echo -e "Find IP of the Suricata server, do nothing..."
						elif [ x"$IP_DST" = x"$IP_SURICATA_SERVER" ]; then
							echo -e "Find IP of the Suricata server, do nothing..."
						else
							#Уточняем текущий IP адрес на WAN интерфейсе
							IP_OUR_MASC[0,1]=$(sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
							/ip address print where interface=$NAME_WAN_INTERFACE | grep '/' | awk {'print $3'} | rev | cut -d / -f 2- | rev)

							#Проверки для работы скрипта
							IP_SRC_TEST=$(echo $IP_SRC | cut  -c1-${IP_OUR_MASC[0,2]})
								if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[0,1]}" ]; then
									IP_SRC=$IP_SRC_DEFAULT
								else
									IP_SRC_TEST=$(echo $IP_SRC | cut  -c1-${IP_OUR_MASC[1,2]})
									if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[1,1]}" ]; then
										IP_SRC=$IP_SRC_DEFAULT
									fi
								fi
							
							#Проверки для работы скрипта
							IP_SRC_TEST=$(echo $IP_DST | cut  -c1-${IP_OUR_MASC[0,2]})
								if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[0,1]}" ]; then
									IP_DST=$IP_SRC_DEFAULT
								else
									IP_SRC_TEST=$(echo $IP_DST | cut  -c1-${IP_OUR_MASC[1,2]})
										if [ x"$IP_SRC_TEST" = x"${IP_OUR_MASC[1,1]}" ]; then
											IP_DST=$IP_SRC_DEFAULT
										fi
								fi
							
							#Находится ли детектированный IP уже в address-list на маршрутизаторе
							if [ x"$IP_SRC" = x'IP_OUR' ]; then
								NUMBER_IN_ADDRESS_LIST=$(sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
								/ip firewall address-list print where list=$NAME_BLOCK_LIST | grep -w "Suricata $IP_DST" | awk {'print $1'})
									if [ -z "$NUMBER_IN_ADDRESS_LIST" ]; then
										#Добавляем новую запись в address-list маршрутизатора
										sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
										/ip firewall address-list add address=$IP_DST timeout=$BLOCK_TIMEOUT \
										comment=\"Suricata $IP_DST:$IP_DST_PORT to $IP_SRC_PORT $PROTOCOL priority $PRIOR \### $CLASS \### $EVENT\" list=$NAME_BLOCK_LIST
									else
										#Обновляем таймаут для уже существующей записи
										sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
										/ip firewall address-list set timeout=$BLOCK_TIMEOUT numbers=$NUMBER_IN_ADDRESS_LIST
									fi
							else
								#Тоже самое, относительно другой переменной
								NUMBER_IN_ADDRESS_LIST=$(sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
								/ip firewall address-list print where list=$NAME_BLOCK_LIST | grep -w "Suricata $IP_SRC" | awk {'print $1'})
									if [ -z "$NUMBER_IN_ADDRESS_LIST" ]; then
										sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
										/ip firewall address-list add address=$IP_SRC timeout=$BLOCK_TIMEOUT \
										comment=\"Suricata $IP_SRC:$IP_SRC_PORT to $IP_DST_PORT $PROTOCOL priority $PRIOR \### $CLASS \### $EVENT\" list=$NAME_BLOCK_LIST
									else
										sshpass -p $MIKROTIK_PASS ssh $MIKROTIK_USER@$MIKROTIK_IP \
										/ip firewall address-list set timeout=$BLOCK_TIMEOUT numbers=$NUMBER_IN_ADDRESS_LIST
									fi
							fi
							echo 'End ...'
						fi
					fi
			fi
			done
		fi
done


Код прокомментирован и ясен. Результат его работы на RouterOS выглядит примерно так:



Мы пропускали неинтересные для нас сработки «TLS invalid handshake message» и «TLS invalid record/traffic». Можно переделать скрипт и пропускать сообщения с приоритетом 3, так как это не угрозы безопасности оберегаемой сети, и они носят скорее уведомительный характер. Блокировать IP адреса, обнаруживаемые IDS, будем посредством Firewall:

/ip firewall raw add action=drop comment="Block from Suricata" src-address-list=Suricata_rules
/ip firewall raw add action=drop comment="Block from Suricata" dst-address-list=Suricata_rules

Лучше это делать в RAW filter, так как пакеты будут отброшены в самом начале обработки их маршрутизатором и не потратят зря имеющиеся ресурсы. Это хорошо видно в схеме прохождения трафика внутри RouterOS, взятой у этих ребят:



Попробуем повторить брутфорс службы SSH роутера:



Попробуем что-нибудь серьезнее, загрузим эксплоит для уязвимости службы Winbox (CVE-2018-14847):

git clone https://github.com/BasuCert/WinboxPoC
python3 WinboxExploit.py 192.168.15.9




Как видно, все было детектировано и соответственно заблокировано на маршрутизаторе. Поэтому можно говорить, что из IDS наша реализация превратилась в IPS. Здесь стоит еще упомянуть коммерческий проект, в котором уже все это решено за нас, можно брать и пользоваться. Поставляется в виде готовой сборки ISO.

13. Заключение


Вот и подошёл к концу цикл наших статей, посвящённый широким возможностям операционной системы RouterOS, а также сопрягаемых с ней opensource решений. В комплексе организованная безопасность уровней L1, L2 и L3, реализация централизованного логирования, интегрированное IPS решение позволяют говорить, что наш MikroTik – это полноценный барьер для разноуровневых угроз, нацеленных на защищаемую сеть. То, что не умеет делать RouterOS, можно допилить самому готовыми бесплатными решениями.

И вот еще о чем важном мы не упомянули. Всегда помните, что в любой, даже в самой технически защищённой системе, остаётся слабое место – человек её обслуживающий. Здесь использован перифраз известной цитаты Кевина Митника из книги «Искусство обмана»: «Человеческий фактор по-настоящему самое слабое звено в безопасности». Поэтому гигиена, в том числе цифровая, никогда не теряет актуальности, особенно в период пандемии.


Теги:
Хабы:
+44
Комментарии 16
Комментарии Комментарии 16

Публикации

Информация

Сайт
ruvds.com
Дата регистрации
Дата основания
Численность
11–30 человек
Местоположение
Россия
Представитель
ruvds